diff options
author | Keith Randall <keithr@alum.mit.edu> | 2018-12-04 07:58:18 -0800 |
---|---|---|
committer | Keith Randall <khr@golang.org> | 2018-12-28 20:55:36 +0000 |
commit | 69c2c56453cdea1ad32d50fc82193f06b1b33f93 (patch) | |
tree | 68923304deca5d767ceff2dca690faffb7b58616 /src/runtime/traceback.go | |
parent | c043fc4f655ce34f67a0e7fe2833139f6313a3f0 (diff) | |
download | go-69c2c56453cdea1ad32d50fc82193f06b1b33f93.tar.gz go-69c2c56453cdea1ad32d50fc82193f06b1b33f93.zip |
cmd/compile,runtime: redo mid-stack inlining tracebacks
Work involved in getting a stack trace is divided between
runtime.Callers and runtime.CallersFrames.
Before this CL, runtime.Callers returns a pc per runtime frame.
runtime.CallersFrames is responsible for expanding a runtime frame
into potentially multiple user frames.
After this CL, runtime.Callers returns a pc per user frame.
runtime.CallersFrames just maps those to user frame info.
Entries in the result of runtime.Callers are now pcs
of the calls (or of the inline marks), not of the instruction
just after the call.
Fixes #29007
Fixes #28640
Update #26320
Change-Id: I1c9567596ff73dc73271311005097a9188c3406f
Reviewed-on: https://go-review.googlesource.com/c/152537
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
Diffstat (limited to 'src/runtime/traceback.go')
-rw-r--r-- | src/runtime/traceback.go | 88 |
1 files changed, 42 insertions, 46 deletions
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index 0328fee4e6..da15ed0680 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -179,6 +179,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in var cache pcvalueCache + lastFuncID := funcID_normal n := 0 for n < max { // Typically: @@ -344,48 +345,44 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in } if pcbuf != nil { - if skip == 0 { - (*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = frame.pc - } else { - // backup to CALL instruction to read inlining info (same logic as below) - tracepc := frame.pc - if (n > 0 || flags&_TraceTrap == 0) && frame.pc > f.entry && !waspanic { - tracepc-- - } - inldata := funcdata(f, _FUNCDATA_InlTree) - - // no inlining info, skip the physical frame - if inldata == nil { - skip-- - goto skipped - } + // backup to CALL instruction to read inlining info (same logic as below) + tracepc := frame.pc + if (n > 0 || flags&_TraceTrap == 0) && frame.pc > f.entry && !waspanic { + tracepc-- + } - ix := pcdatavalue(f, _PCDATA_InlTreeIndex, tracepc, &cache) + // If there is inlining info, record the inner frames. + if inldata := funcdata(f, _FUNCDATA_InlTree); inldata != nil { inltree := (*[1 << 20]inlinedCall)(inldata) - // skip the logical (inlined) frames - logicalSkipped := 0 - for ix >= 0 && skip > 0 { - skip-- - logicalSkipped++ - ix = inltree[ix].parent - } - - // skip the physical frame if there's more to skip - if skip > 0 { - skip-- - goto skipped - } - - // now we have a partially skipped frame - (*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = frame.pc - - // if there's room, pcbuf[1] is a skip PC that encodes the number of skipped frames in pcbuf[0] - if n+1 < max { - n++ - pc := skipPC + uintptr(logicalSkipped) - (*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = pc + for { + ix := pcdatavalue(f, _PCDATA_InlTreeIndex, tracepc, &cache) + if ix < 0 { + break + } + if inltree[ix].funcID == funcID_wrapper && elideWrapperCalling(lastFuncID) { + // ignore wrappers + } else if skip > 0 { + skip-- + } else if n < max { + (*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = tracepc + n++ + } + lastFuncID = inltree[ix].funcID + // Back up to an instruction in the "caller". + tracepc = frame.fn.entry + uintptr(inltree[ix].parentPc) } } + // Record the main frame. + if f.funcID == funcID_wrapper && elideWrapperCalling(lastFuncID) { + // Ignore wrapper functions (except when they trigger panics). + } else if skip > 0 { + skip-- + } else if n < max { + (*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = tracepc + n++ + } + lastFuncID = f.funcID + n-- // offset n++ below } if printing { @@ -396,7 +393,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in // called panic rather than the wrapped // function. Otherwise, leave them out. name := funcname(f) - nextElideWrapper := elideWrapperCalling(name) + nextElideWrapper := elideWrapperCalling(f.funcID) if (flags&_TraceRuntimeFrames) != 0 || showframe(f, gp, nprint == 0, elideWrapper && nprint != 0) { // Print during crash. // main(0x1, 0x2, 0x3) @@ -418,7 +415,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in file = funcfile(f, inltree[ix].file) line = inltree[ix].line - ix = inltree[ix].parent + ix = int32(inltree[ix].parent) } } if name == "runtime.gopanic" { @@ -451,7 +448,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in } n++ - skipped: if f.funcID == funcID_cgocallback_gofunc && len(cgoCtxt) > 0 { ctxt := cgoCtxt[len(cgoCtxt)-1] cgoCtxt = cgoCtxt[:len(cgoCtxt)-1] @@ -798,7 +794,7 @@ func printAncestorTracebackFuncInfo(f funcInfo, pc uintptr) bool { file = funcfile(f, inltree[ix].file) line = inltree[ix].line - ix = inltree[ix].parent + ix = int32(inltree[ix].parent) } } name := funcname(f) @@ -811,7 +807,7 @@ func printAncestorTracebackFuncInfo(f funcInfo, pc uintptr) bool { print(" +", hex(pc-f.entry)) } print("\n") - return elideWrapperCalling(name) + return elideWrapperCalling(f.funcID) } func callers(skip int, pcbuf []uintptr) int { @@ -877,11 +873,11 @@ func isExportedRuntime(name string) bool { } // elideWrapperCalling reports whether a wrapper function that called -// function "name" should be elided from stack traces. -func elideWrapperCalling(name string) bool { +// function id should be elided from stack traces. +func elideWrapperCalling(id funcID) bool { // If the wrapper called a panic function instead of the // wrapped function, we want to include it in stacks. - return !(name == "runtime.gopanic" || name == "runtime.sigpanic" || name == "runtime.panicwrap") + return !(id == funcID_gopanic || id == funcID_sigpanic || id == funcID_panicwrap) } var gStatusStrings = [...]string{ |