Brief
This tool provides a simple mechanism for auto code instrumentation.
User can provide at least one source code file, each file contains at least one global function(patch function
),
each function represents one instrumentation logic, and every function satifies patch function signature,
then this tool will copy every function body to every source code function.
Well, instrumentation is not simply copying code snippets, variables in function bodys will be rewritten, and source
file imports will be resolved.
It is very useful for tracing libaries.
Usage
For example, we write one file which contains only one patch function, package name or function name can be any name,
but function signature must satisfiy patch function sigature.
package sample
// Patch Function Signature
import (
gonativectx "context"
)
// general instrumentation function signature
ProcessFunc(spanName string, hasCtx bool, ctx gonativectx.Context, args ...interface{})
spanName
was generated by parser, if hasCtx
was true, user can use provided function param ctx,
gonativectx
is alias for go native context package, mainly for avoiding pkg name confliction.
args
represents args of the instrumented function, using interface{} other than any only for compatiblity.
NOTE: There should be only instrumentation functions in patch file, any global variables are not allowed.
User can ignore any fields other than spanName, for example, define one instrumentation function which is not nterested in ctx and function args.
package sample
import (
gonativectx "context"
)
ProcessFunc(spanName string, _ bool, _ gonativectx.Context, _ ...interface{})
Build and instrument patch codes to source files.
go build -o go-instrument-tool ./cmd/
Example
We can instrument functions with go execution tracing logic easily using this tool, see demo/instrument_go_trace.go
for more details.
First, we define one patch function:
func InstrumentGoTrace(spanName string, hasCtx bool, ctx gonativectx.Context, args ...interface{}) {
fctx := gonativectx.TODO()
var t *trace.Task
if hasCtx {
fctx = ctx
ctx, t = trace.NewTask(fctx, spanName)
} else {
_, t = trace.NewTask(fctx, spanName)
}
logbin, _ := json.Marshal(args)
trace.Logf(ctx, spanName, "function args: %s", string(logbin))
defer t.End()
}
For every source code function, we use trace task to correlate each function, and logging function params, if source function contains context param, task context will be propagated for each callee function.
Then in main function, enable and start go tracing logic.
f, err := os.Create("servertrace.out")
if err != nil {
log.Fatalf("failed to create trace output file: %v", err)
}
defer func() {
if err := f.Close(); err != nil {
log.Fatalf("failed to close trace file: %v", err)
}
}()
Now we can select source files to instrument.
// usage: find . -name "*.go"|grep -vE "test|example"|xargs -I {} go-instrument-tool -source={} -replace -patches=xxx/demo/instrument_go_trace.go
after executing, source files will be rewritten, and every function would be instrumented with go trace logic.
package main
import (
gonativectx "context"
"log"
"os"
"encoding/json"
"runtime/trace"
)
func main() {
spanNameinstrumentgotrace17251870431 := "test.go-main.main"
hasCtxinstrumentgotrace17251870431 := false
ctxinstrumentgotrace17251870431 := gonativectx.Background()
argsinstrumentgotrace17251870431 := []interface {
}{}
fctxinstrumentgotrace17251870431 := gonativectx.TODO()
var tinstrumentgotrace17251870431 *trace.Task
if hasCtxinstrumentgotrace17251870431 {
fctxinstrumentgotrace17251870431 = ctxinstrumentgotrace17251870431
ctxinstrumentgotrace17251870431, tinstrumentgotrace17251870431 = trace.NewTask(fctxinstrumentgotrace17251870431, spanNameinstrumentgotrace17251870431)
} else {
_, tinstrumentgotrace17251870431 = trace.NewTask(fctxinstrumentgotrace17251870431, spanNameinstrumentgotrace17251870431)
}
logbininstrumentgotrace17251870431, _ := json.Marshal(argsinstrumentgotrace17251870431)
trace.Logf(ctxinstrumentgotrace17251870431, spanNameinstrumentgotrace17251870431, "function args: %s", string(logbininstrumentgotrace17251870431))
defer tinstrumentgotrace17251870431.End()
f, err := os.Create("servertrace.out")
if err != nil {
log.Fatalf("failed to create trace output file: %v", err)
}
defer func() {
if err := f.Close(); err != nil {
log.Fatalf("failed to close trace file: %v", err)
}
}()
}