aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/stack_test.go
diff options
context:
space:
mode:
authorEric Daniels <eric@erdaniels.com>2018-04-03 21:35:46 -0400
committerAustin Clements <austin@google.com>2018-04-13 20:42:38 +0000
commitd9b006a7057d4666cb4fa9c421f2360ef3994b0f (patch)
treec164e0e3b0f5c10fa391ba1e25456e52c1dfa365 /src/runtime/stack_test.go
parent115b1cd192609624a898954b9759fcd90247badc (diff)
downloadgo-d9b006a7057d4666cb4fa9c421f2360ef3994b0f.tar.xz
runtime/traceback: support tracking goroutine ancestor tracebacks with GODEBUG="tracebackancestors=N"
Currently, collecting a stack trace via runtime.Stack captures the stack for the immediately running goroutines. This change extends those tracebacks to include the tracebacks of their ancestors. This is done with a low memory cost and only utilized when debug option tracebackancestors is set to a value greater than 0. Resolves #22289 Change-Id: I7edacc62b2ee3bd278600c4a21052c351f313f3a Reviewed-on: https://go-review.googlesource.com/70993 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Austin Clements <austin@google.com>
Diffstat (limited to 'src/runtime/stack_test.go')
-rw-r--r--src/runtime/stack_test.go40
1 files changed, 40 insertions, 0 deletions
diff --git a/src/runtime/stack_test.go b/src/runtime/stack_test.go
index 8342724d0b..91d10bad5c 100644
--- a/src/runtime/stack_test.go
+++ b/src/runtime/stack_test.go
@@ -8,6 +8,7 @@ import (
"bytes"
"fmt"
"reflect"
+ "regexp"
. "runtime"
"strings"
"sync"
@@ -696,3 +697,42 @@ func TestTracebackSystemstack(t *testing.T) {
t.Fatalf("expected 5 calls to TracebackSystemstack and 1 call to TestTracebackSystemstack, got:%s", tb.String())
}
}
+
+func TestTracebackAncestors(t *testing.T) {
+ goroutineRegex := regexp.MustCompile(`goroutine [0-9]+ \[`)
+ for _, tracebackDepth := range []int{0, 1, 5, 50} {
+ output := runTestProg(t, "testprog", "TracebackAncestors", fmt.Sprintf("GODEBUG=tracebackancestors=%d", tracebackDepth))
+
+ numGoroutines := 3
+ numFrames := 2
+ ancestorsExpected := numGoroutines
+ if numGoroutines > tracebackDepth {
+ ancestorsExpected = tracebackDepth
+ }
+
+ matches := goroutineRegex.FindAllStringSubmatch(output, -1)
+ if len(matches) != 2 {
+ t.Fatalf("want 2 goroutines, got:\n%s", output)
+ }
+
+ // Check functions in the traceback.
+ fns := []string{"main.recurseThenCallGo", "main.main", "main.printStack", "main.TracebackAncestors"}
+ for _, fn := range fns {
+ if !strings.Contains(output, "\n"+fn+"(") {
+ t.Fatalf("expected %q function in traceback:\n%s", fn, output)
+ }
+ }
+
+ if want, count := "originating from goroutine", ancestorsExpected; strings.Count(output, want) != count {
+ t.Errorf("output does not contain %d instances of %q:\n%s", count, want, output)
+ }
+
+ if want, count := "main.recurseThenCallGo(...)", ancestorsExpected*(numFrames+1); strings.Count(output, want) != count {
+ t.Errorf("output does not contain %d instances of %q:\n%s", count, want, output)
+ }
+
+ if want, count := "main.recurseThenCallGo(0x", 1; strings.Count(output, want) != count {
+ t.Errorf("output does not contain %d instances of %q:\n%s", count, want, output)
+ }
+ }
+}