diff options
| author | Eric Daniels <eric@erdaniels.com> | 2018-04-03 21:35:46 -0400 |
|---|---|---|
| committer | Austin Clements <austin@google.com> | 2018-04-13 20:42:38 +0000 |
| commit | d9b006a7057d4666cb4fa9c421f2360ef3994b0f (patch) | |
| tree | c164e0e3b0f5c10fa391ba1e25456e52c1dfa365 /src/runtime/traceback.go | |
| parent | 115b1cd192609624a898954b9759fcd90247badc (diff) | |
| download | go-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/traceback.go')
| -rw-r--r-- | src/runtime/traceback.go | 95 |
1 files changed, 84 insertions, 11 deletions
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index 06df9385fd..0f392a50fd 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -627,18 +627,22 @@ func printcreatedby(gp *g) { pc := gp.gopc f := findfunc(pc) if f.valid() && showframe(f, gp, false, false) && gp.goid != 1 { - print("created by ", funcname(f), "\n") - tracepc := pc // back up to CALL instruction for funcline. - if pc > f.entry { - tracepc -= sys.PCQuantum - } - file, line := funcline(f, tracepc) - print("\t", file, ":", line) - if pc > f.entry { - print(" +", hex(pc-f.entry)) - } - print("\n") + printcreatedby1(f, pc) + } +} + +func printcreatedby1(f funcInfo, pc uintptr) { + print("created by ", funcname(f), "\n") + tracepc := pc // back up to CALL instruction for funcline. + if pc > f.entry { + tracepc -= sys.PCQuantum } + file, line := funcline(f, tracepc) + print("\t", file, ":", line) + if pc > f.entry { + print(" +", hex(pc-f.entry)) + } + print("\n") } func traceback(pc, sp, lr uintptr, gp *g) { @@ -689,6 +693,71 @@ func traceback1(pc, sp, lr uintptr, gp *g, flags uint) { print("...additional frames elided...\n") } printcreatedby(gp) + + if gp.ancestors == nil { + return + } + for _, ancestor := range *gp.ancestors { + printAncestorTraceback(ancestor) + } +} + +// printAncestorTraceback prints the traceback of the given ancestor. +// TODO: Unify this with gentraceback and CallersFrames. +func printAncestorTraceback(ancestor ancestorInfo) { + print("[originating from goroutine ", ancestor.goid, "]:\n") + elideWrapper := false + for fidx, pc := range ancestor.pcs { + f := findfunc(pc) // f previously validated + if showfuncinfo(f, fidx == 0, elideWrapper && fidx != 0) { + elideWrapper = printAncestorTracebackFuncInfo(f, pc) + } + } + if len(ancestor.pcs) == _TracebackMaxFrames { + print("...additional frames elided...\n") + } + // Show what created goroutine, except main goroutine (goid 1). + f := findfunc(ancestor.gopc) + if f.valid() && showfuncinfo(f, false, false) && ancestor.goid != 1 { + printcreatedby1(f, ancestor.gopc) + } +} + +// printAncestorTraceback prints the given function info at a given pc +// within an ancestor traceback. The precision of this info is reduced +// due to only have access to the pcs at the time of the caller +// goroutine being created. +func printAncestorTracebackFuncInfo(f funcInfo, pc uintptr) bool { + tracepc := pc // back up to CALL instruction for funcline. + if pc > f.entry { + tracepc -= sys.PCQuantum + } + file, line := funcline(f, tracepc) + inldata := funcdata(f, _FUNCDATA_InlTree) + if inldata != nil { + inltree := (*[1 << 20]inlinedCall)(inldata) + ix := pcdatavalue(f, _PCDATA_InlTreeIndex, tracepc, nil) + for ix != -1 { + name := funcnameFromNameoff(f, inltree[ix].func_) + print(name, "(...)\n") + print("\t", file, ":", line, "\n") + + file = funcfile(f, inltree[ix].file) + line = inltree[ix].line + ix = inltree[ix].parent + } + } + name := funcname(f) + if name == "runtime.gopanic" { + name = "panic" + } + print(name, "(...)\n") + print("\t", file, ":", line) + if pc > f.entry { + print(" +", hex(pc-f.entry)) + } + print("\n") + return elideWrapperCalling(name) } func callers(skip int, pcbuf []uintptr) int { @@ -711,6 +780,10 @@ func showframe(f funcInfo, gp *g, firstFrame, elideWrapper bool) bool { if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig.ptr()) { return true } + return showfuncinfo(f, firstFrame, elideWrapper) +} + +func showfuncinfo(f funcInfo, firstFrame, elideWrapper bool) bool { level, _, _ := gotraceback() if level > 1 { // Show all frames. |
