AST-based function tracer for Go. Captures every function call with exact timings by rewriting source code at the AST level.
Unlike sampling profilers like pprof, GoTrace is deterministic — it traces every call, not a statistical sample.
go install github.com/codeflash-ai/gotrace/cmd/gotrace@latestOr build from source:
git clone https://github.com/codeflash-ai/gotrace.git
cd gotrace
go build -o gotrace ./cmd/gotracegotrace run ./cmd/server
gotrace run ./cmd/api listen --port 8080The first argument is the package to build. Everything after is passed as arguments to the instrumented binary.
gotrace test ./pkg/auth
gotrace test ./pkg/auth -run TestLogin -vGenerates collapsed stacks compatible with Speedscope and Brendan Gregg's FlameGraph:
gotrace flame ./cmd/server > trace.folded
# Open in speedscope
speedscope trace.folded
# Or generate SVG
flamegraph.pl trace.folded > trace.svggotrace json ./cmd/serverProduces a nested JSON tree:
[
{
"name": "main.main",
"duration_ms": 231.45,
"children": [
{
"name": "server.Start",
"duration_ms": 192.3,
"children": [...]
}
]
}
]Limit which packages get instrumented:
# Only instrument packages matching a pattern
gotrace run ./cmd --include="myapp/internal/*"
# Exclude specific packages
gotrace run ./cmd --exclude="myapp/generated/..."By default, GoTrace instruments all packages within your module. Standard library and third-party dependencies are never instrumented.
TOTAL: 2.31s
main.main 2.31s 100.0%
├── server.Start 1.92s 83.1%
│ ├── db.Connect 412ms 17.8%
│ ├── cache.Load 182ms 7.9%
│ └── router.Init 1.12s 48.5%
└── metrics.Start 301ms 13.0%
GoTrace operates in 5 stages:
Uses golang.org/x/tools/go/packages to parse your project. Resolves all packages, ASTs, type information, and import graphs.
Walks the AST of every in-module package and injects tracing instrumentation:
Original:
func QueryDB(ctx context.Context) (*Result, error) {
rows, err := db.Query(ctx, "SELECT ...")
return parse(rows), err
}Instrumented:
func QueryDB(ctx context.Context) (*Result, error) {
__gotrace_token := __gotrace_tracer.Enter(__gotrace_fid_QueryDB)
defer __gotrace_tracer.Exit(__gotrace_token)
rows, err := db.Query(ctx, "SELECT ...")
return parse(rows), err
}The rewriter also transforms go statements for goroutine tracking:
// Before
go worker(item)
// After
go __gotrace_tracer.Go(func() { worker(item) })This propagates parent-child relationships between goroutines.
Copies your source to a temporary workspace, writes the rewritten files, injects the tracer runtime as a local module, and runs go build (or go test -c).
Your original source is never modified.
Runs the instrumented binary. The tracer records events into a lock-free pre-allocated buffer using atomic operations. Each event is ~29 bytes:
- Event type (enter/exit/spawn)
- Function ID
- Goroutine ID
- Nanosecond timestamp
- Parent goroutine ID
After execution completes, reads the binary trace file, reconstructs the call tree from the event stream, and renders the output in the requested format.
- Regular functions
- Methods (with receiver type in the name)
- Generic functions
- Anonymous functions (named as
pkg.Outer.func1) - Goroutine spawn relationships
- Standard library
- Third-party dependencies (anything outside your module)
- Generated files (
*.pb.go,*_generated.go, files with// Code generatedheader) - The tracer runtime itself
| Command | Description |
|---|---|
gotrace run <pkg> [args...] |
Trace and print call tree |
gotrace test <pkg> [test flags...] |
Trace test execution |
gotrace flame <pkg> [args...] |
Output collapsed stacks |
gotrace json <pkg> [args...] |
Output JSON call tree |
gotrace exec <pkg> [args...] |
Build and run with tree output |
| Flag | Description |
|---|---|
--include |
Package patterns to instrument |
--exclude |
Package patterns to skip |
-v, --verbose |
Show workspace path and debug info |
- Go 1.21+
- Project must use Go modules
MIT