aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/traceback.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/traceback.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/traceback.go')
-rw-r--r--src/runtime/traceback.go95
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.