tracer

package module
v0.0.0-...-616d989 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 2, 2022 License: BSD-3-Clause Imports: 5 Imported by: 0

README

GoChan-Tracer

- This code is still work in progress and may not work or result in incorrect behavior!

What?

GoChan-Tracer implements drop in replacements for channel operations in Go. Those replacements can be used to create a trace of the executed program similar to the trace described in [1].

Installation

go get github.com/ErikKassubek/GoChan/tracer

How?

For the tracer, channel functionality is replaced by costum functions to create a trace of the program. The instrumenter can be used to automatically translate Go-Code into code with the replacements.

Example

Let's look at the following code:

package main

import (
	"math/rand"
	"sync"
	"time"
)

func func1(x chan int) {
	x <- rand.Intn(100)
}

func test() {

	x := make(chan int)
	y := make(chan int)

	a := make(chan int, 1)
	b := make(chan int, 0)
	c := make(chan string, 0)

	var l sync.Mutex
	var m sync.RWMutex

	i := 3

	go func1(x)
	go func() {
		m.Lock()
		<-x
		x <- rand.Intn(100)
		m.Unlock()
	}()
	go func(i int) {
		l.Lock()
		y <- i
		<-x
		l.Unlock()
	}(i)
	go func() {
		m.RLock()
		<-y
		m.RUnlock()
	}()

	time.Sleep(1 * time.Second)

	select {
	case x := <-a:
		println(x)
	case <-b:
		println("b")
	case c <- "3":
		println("c")
	default:
		println("default")
	}
}

func main() {
	test()
	time.Sleep(4 * time.Second)
}

By the instrumenter it gets translated into

package main

import (
	"math/rand"
	"time"

	"github.com/ErikKassubek/GoChan/tracer"
)

func func1(x tracer.Chan[int]) {
	x.Send(rand.Intn(100))
}

func test() {

	x := tracer.NewChan[int](0)
	y := tracer.NewChan[int](0)

	a := tracer.NewChan[int](1)
	b := tracer.NewChan[int](0)
	c := tracer.NewChan[string](0)

	l := tracer.NewLock()
	m := tracer.NewRWLock()

	i := 3

	func() {
		GoChanRoutineIndex := tracer.SpawnPre()
		go func() {
			tracer.SpawnPost(GoChanRoutineIndex)
			{

				func1(x)
			}
		}()
	}()

	func() {
		GoChanRoutineIndex := tracer.SpawnPre()
		go func() {
			tracer.SpawnPost(GoChanRoutineIndex)
			{
				m.Lock()
				x.Receive()
				x.Send(rand.Intn(100))
				m.Unlock()
			}
		}()
	}()

	func() {
		GoChanRoutineIndex := tracer.SpawnPre()
		go func(i int) {
			tracer.SpawnPost(GoChanRoutineIndex)
			{
				l.Lock()
				y.Send(i)
				x.Receive()

				l.Unlock()
			}
		}(i)
	}()

	func() {
		GoChanRoutineIndex := tracer.SpawnPre()
		go func() {
			tracer.SpawnPost(GoChanRoutineIndex)
			{
				m.RLock()
				y.Receive()
				m.RUnlock()
			}
		}()
	}()

	time.Sleep(1 * time.Second)

	{
		tracer.PreSelect(true, a.GetIdPre(true), b.GetIdPre(true), c.GetIdPre(false))
		sel_HctcuAxh := tracer.BuildMessage("3")

		select {
		case sel_XVlBzgbaiC := <-a.GetChan():
			a.PostSelect(true, sel_XVlBzgbaiC)
			x := sel_XVlBzgbaiC.GetInfo()
			println(x)
		case sel_MRAjWwhT := <-b.GetChan():
			b.PostSelect(true, sel_MRAjWwhT)
			println("b")
		case c.GetChan() <- sel_HctcuAxh:
			c.PostSelect(false, sel_HctcuAxh)
			println("c")
		default:
			tracer.PostDefault()
			println("default")
		}
	}
}

func main() {
	tracer.Init()
	defer tracer.PrintTrace()

	test()
	time.Sleep(4 * time.Second)
}

By running this program we get the resulting trace. One possible trace is

[signal(1, 2), signal(2, 3), signal(3, 4), signal(4, 5), pre(23, 3?, 4?, 5!, default), post(24, default)]
[wait(8, 2), pre(9, 2!), post(19, 2, 2!)]
[wait(10, 3), lock(11, 2, -, 1), pre(22, 2?)]
[wait(12, 4), lock(13, 1, -, 1), pre(14, 3!), post(15, 4, 3!), pre(16, 2?), post(17, 2, 2?, 9), unlock(18, 1)]
[wait(5, 5), lock(6, 2, r, 1), pre(7, 3?), post(20, 4, 3?, 14), unlock(21, 2)]

Every line represents a routine (the first line is the main routine). The elements have the following meaning:

Element Meaning
signal(t, i) a new routine with id = i has been created from the current routine
wait(t, i) the current, non main routine was started with id = i
pre(t, i!) the routine has reached a state, where channel i is supposed to send, but has not send yet
post(t, i, k!) the channel k has successfully send its data in routine i with time step j
pre(t, i?) the routine has reached a state, where channel i is supposed to receive, but has not received yet
post(t, i, j, k?) the channel k has successfully received its data from routine i with time step j of routine i
pre(t, i?, j?, k?) the routine has reached a select statements with cases for channels i, j and k. The select statement does not contain a default case. The statement has not yet executed.
pre(t, i?, j?, k?, default) the routine has reached a select statements with cases for channels i, j and k. The select statement does contain a default case. The statement has not yet executed.
post(t, default) The switch statement has executed and chosen the default case.
lock(t, i, j, l) The lock with id i has tried to lock. l is 1 if the lock was successful, 0 if it was not (e.g. with TryLock). j can be "t", "r", "tr" or "-". "t" shows, that the lock operation was a try-lock operation. "r" shows, that it was an r-lock operation. "tr" shows, that it was a try-r-operation".
unlock(t, i) The lock with id i was unlocked.
t always states the timestamp of the operation.

References

[1] M. Sulzmann and K. Stadtmüller, “Two-phase dynamic analysis of message-passing go programs based on vector clocks,” CoRR, vol. abs/1807.03585, 2018.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Init

func Init()

Function to initialize the tracer. @return: nil

func PostDefault

func PostDefault()

Function to add at the beginning of a select default body. @return nil

func PreSelect

func PreSelect(def bool, channels ...PreObj)

Function to add before a select statement. @param def bool: true if the select has a default statement, false otherwise @param channels ...PreObj: list of PreObj to store the cases @return nil

func PrintTrace

func PrintTrace()

Function to print the collected trace. @return nil

func SpawnPost

func SpawnPost(numRut uint32)

Function to call after the creation of a new routine in the new routine @param numRut uint32: id of the new routine

func SpawnPre

func SpawnPre() uint32

Function to call before the creation of a new routine in the old routine @return id of the new routine

Types

type Chan

type Chan[T any] struct {
	// contains filtered or unexported fields
}

Struct to implement a drop in replacement for a channel @field c chan Message[T]: channel to send a message @field id uint32: id for the channel

func NewChan

func NewChan[T any](size int) Chan[T]

Function to create a new channel object. This object can be used as a drop in replacement for a chan T. @param size int: size of the channel, 0 for un-buffered channel @return Chan[T]: channel object

func (*Chan[T]) Close

func (ch *Chan[T]) Close()

Function as drop-in replacement for closing a channel. @return nil

func (*Chan[T]) GetChan

func (ch *Chan[T]) GetChan() chan Message[T]

Getter for the chan field of a Chan object. @receiver *Chan[T] @return chan Message[T]: chan field of channel

func (*Chan[T]) GetId

func (ch *Chan[T]) GetId() uint32

Getter fir the id field of a Chan object @receiver *Chan[T] @return uint32: id of the Chan

func (*Chan[T]) GetIdPre

func (ch *Chan[T]) GetIdPre(receive bool) PreObj

Function to create a PreObj object from a Chan used with select. @param receive bool: true, if the select case is a channel receive, false if it is a send @return PreObj: the created preObj object

func (*Chan[T]) Post

func (ch *Chan[T]) Post(receive bool, message Message[T])

Function to add at the beginning of a select case body. &param receive bool: true, if the case was started with a receive false if with a send @param message Message[T]: message wich was send over the channel @return nil

func (*Chan[T]) Receive

func (ch *Chan[T]) Receive() T

Function as drop-in replacement for <-ch.c. @receiver: *Chan[T] @return T: received value

func (*Chan[T]) Send

func (ch *Chan[T]) Send(val T)

Function as drop-in replacements for ch.c <- T. @receiver: *Chan[T] @param: val T: message to send over the channel

type Message

type Message[T any] struct {
	// contains filtered or unexported fields
}

Struct for message to send by Chan object @field info T: actual message to send @field sender uint32: id of the sender @field senderTimestamp: timestamp when the message was send

func BuildMessage

func BuildMessage[T any](info T) Message[T]

Funktion to create a message. This function is mainly used, when sending a message in a select statement @param info T: info to send @return Message[T]: message object wich can be send

func (*Message[T]) GetInfo

func (m *Message[T]) GetInfo() T

Function to get the info field from a message. @receiver *Message[T] @return T: info field of the message

type Mutex

type Mutex struct {
	// contains filtered or unexported fields
}

Struct to implement a mutex. Is used as drop-in replacement for sync.Mutex @field mu *sync.Mutex: actual mutex to perform lock and unlock operations @field id uint32: id of the mutex

func NewMutex

func NewMutex() Mutex

Function to create and initialize a new Mutex @return nil

func (*Mutex) Lock

func (m *Mutex) Lock()

Function to lock a Mutex. @receiver *Mutex @return nil

func (*Mutex) TryLock

func (m *Mutex) TryLock() bool

Function to try-lock a Mutex. @receiver *Mutex @return bool: true if lock was successful, false otherwise

func (*Mutex) Unlock

func (m *Mutex) Unlock()

Function to unlock a Mutex. @receiver *Mutex @return nil

type PreObj

type PreObj struct {
	// contains filtered or unexported fields
}

Struct to save a Chan with id. Used to store channel cases in select. @field id uint32: id of the Chan @field receive bool: true, if the select case is a channel receive, false if it is a send

type RWMutex

type RWMutex struct {
	// contains filtered or unexported fields
}

Struct to implement a rw-mutex. Is used as drop-in replacement for sync.RWMutex @field mu *sync.RWMutex: actual rw-mutex to perform lock and unlock operations @field id uint32: id of the mutex

func NewRWMutex

func NewRWMutex() RWMutex

Function to create and initialize a new RWMutex @return RWMutex: new RWMutex object

func (*RWMutex) Lock

func (m *RWMutex) Lock()

Function to lock a RWMutex. @receiver *RWMutex @return nil

func (*RWMutex) RLock

func (m *RWMutex) RLock()

Function to r-lock a RWMutex. @receiver *RWMutex @return nil

func (*RWMutex) RUnlock

func (m *RWMutex) RUnlock()

Function to r-unlock a RWMutex. @receiver *RWMutex @return nil

func (*RWMutex) TryLock

func (m *RWMutex) TryLock() bool

Function to try-lock a Mutex. @receiver *RWMutex @return bool: true if lock was successful, false otherwise

func (*RWMutex) TryRLock

func (m *RWMutex) TryRLock() bool

Function to try-r-lock a Mutex. @receiver *RWMutex @return bool: true if lock was successful, false otherwise

func (*RWMutex) Unlock

func (m *RWMutex) Unlock()

Function to unlock a RWMutex. @receiver *RWMutex @return nil

type TraceClose

type TraceClose struct {
	// contains filtered or unexported fields
}

Struct for a close in the trace. @field timestamp uint32: timestamp of the creation of the trace object @field chanId uint32: id of the Chan

func (*TraceClose) PrintElement

func (tc *TraceClose) PrintElement()

Function to print the close trace element @receiver *TraceClose @return nil

type TraceDefault

type TraceDefault struct {
	// contains filtered or unexported fields
}

Struct for a default in the trace. @field timestamp uint32: timestamp of the creation of the trace object

func (*TraceDefault) PrintElement

func (td *TraceDefault) PrintElement()

Function to print the default trace element @receiver *TraceDefault @return nil

type TraceElement

type TraceElement interface {
	PrintElement()
}

Interface for a trace element. @signature PrintElement(): function to print the element

type TraceLock

type TraceLock struct {
	// contains filtered or unexported fields
}

Struct for a lock in the trace. @field timestamp uint32: timestamp of the creation of the trace object @field lockId uint32: id of the Mutex @field try bool: true if it is a try-lock, false otherwise @field read bool: true if it is a r-lock, false otherwise @field suc bool: true if the operation was successful, false otherwise (only try)

func (*TraceLock) PrintElement

func (tl *TraceLock) PrintElement()

Function to print the lock trace element @receiver *TraceLock @return nil

type TracePost

type TracePost struct {
	// contains filtered or unexported fields
}

Struct for a post in the trace. @field timestamp uint32: timestamp of the creation of the trace object @field chanId uint32: id of the Chan @field send bool: true if it is a preSend, false otherwise @field senderId: id of the sender of the message @field senderTimestamp: timestamp of the sender at send

func (*TracePost) PrintElement

func (tp *TracePost) PrintElement()

Function to print the post trace element @receiver *TracePost @return nil

type TracePre

type TracePre struct {
	// contains filtered or unexported fields
}

Struct for a pre in the trace. @field timestamp uint32: timestamp of the creation of the trace object @field chanId uint32: id of the Chan @field send bool: true if it is a preSend, false otherwise

func (*TracePre) PrintElement

func (tp *TracePre) PrintElement()

Function to print the pre trace element @receiver *TracePre @return nil

type TracePreSelect

type TracePreSelect struct {
	// contains filtered or unexported fields
}

Struct for a preSelect in the trace. @field timestamp uint32: timestamp of the creation of the trace object @field chanIds []PreObj: list of channels in cases @field def bool: true if the select has a default case, false otherwise

func (*TracePreSelect) PrintElement

func (tps *TracePreSelect) PrintElement()

Function to print the preSelect trace element @receiver *TracePreSelect @return nil

type TraceSignal

type TraceSignal struct {
	// contains filtered or unexported fields
}

Struct for a signal in the trace. @field timestamp uint32: timestamp of the creation of the trace object @field routine uint32: id of the new routine

func (*TraceSignal) PrintElement

func (ts *TraceSignal) PrintElement()

Function to print the signal trace element @receiver *TraceSignal @return nil

type TraceUnlock

type TraceUnlock struct {
	// contains filtered or unexported fields
}

Struct for a unlock in the trace. @field timestamp uint32: timestamp of the creation of the trace object @field lockId uint32: id of the Mutex

func (*TraceUnlock) PrintElement

func (tu *TraceUnlock) PrintElement()

Function to print the unlock trace element @receiver *TraceUnlock @return nil

type TraceWait

type TraceWait struct {
	// contains filtered or unexported fields
}

Struct for a wait in the trace. @field timestamp uint32: timestamp of the creation of the trace object @field routine uint32: id of the routine

func (*TraceWait) PrintElement

func (tw *TraceWait) PrintElement()

Function to print the wait trace element @receiver *TraceWait @return nil

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL