diff --git a/src/runtime/debug.go b/src/runtime/debug.go index 2703a0ce019..5a8103ee241 100644 --- a/src/runtime/debug.go +++ b/src/runtime/debug.go @@ -9,6 +9,15 @@ import ( "unsafe" ) +// TracebackAncestors sets the limits the number of ancestor goroutines to report. +// It extends tracebacks with the stacks at which goroutines were created. +// This also extends the information returned by runtime.Stack. Ancestor's goroutine +// IDs will refer to the ID of the goroutine at the time of creation; it's possible for this +// ID to be reused for another goroutine. Setting N to 0 will report no ancestry information. +func TracebackAncestors(n int32) { + debug.tracebackancestors = n +} + // GOMAXPROCS sets the maximum number of CPUs that can be executing // simultaneously and returns the previous setting. It defaults to // the value of runtime.NumCPU. If n < 1, it does not change the current setting. diff --git a/src/runtime/stack_test.go b/src/runtime/stack_test.go index 1a590869016..f621d4bbbf9 100644 --- a/src/runtime/stack_test.go +++ b/src/runtime/stack_test.go @@ -802,6 +802,37 @@ func TestTracebackAncestors(t *testing.T) { } } +func TestSetTracebackAncestors(t *testing.T) { + wait := make(chan struct{}) + + go func() { + <-wait + }() + + TracebackAncestors(3) + t.Cleanup(func() { + TracebackAncestors(0) + }) + + // GetStack of current runtime + getStack := func() string { + for i := 1024; ; i *= 2 { + buf := make([]byte, i) + if n := Stack(buf, true); n < i { + return string(buf[:n-1]) + } + } + } + + output := getStack() + + if !strings.Contains(output, "originating from goroutine") { + t.Errorf("output does not contain ancestor trace:\n%s", output) + } + + close(wait) +} + // Test that defer closure is correctly scanned when the stack is scanned. func TestDeferLiveness(t *testing.T) { output := runTestProg(t, "testprog", "DeferLiveness", "GODEBUG=clobberfree=1")