From 781fd3998e3c1031379cf5709043a9c0e2987287 Mon Sep 17 00:00:00 2001 From: David Lazar Date: Fri, 17 Feb 2017 16:08:36 -0500 Subject: [PATCH] runtime: use inlining tables to generate accurate tracebacks The code in https://play.golang.org/p/aYQPrTtzoK now produces the following stack trace: goroutine 1 [running]: main.(*point).negate(...) /tmp/go/main.go:8 main.main() /tmp/go/main.go:14 +0x23 Previously the stack trace missed the inlined call: goroutine 1 [running]: main.main() /tmp/go/main.go:14 +0x23 Fixes #10152. Updates #19348. Change-Id: Ib43c67012f53da0ef1a1e69bcafb65b57d9cecb2 Reviewed-on: https://go-review.googlesource.com/37233 Run-TryBot: David Lazar TryBot-Result: Gobot Gobot Reviewed-by: Austin Clements --- src/runtime/crash_test.go | 28 ++++++++++++++++++++++++++++ src/runtime/symtab.go | 25 +++++++++++++++++++++++++ src/runtime/traceback.go | 17 ++++++++++++++++- 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index 8813c3655a3fb5..3f84935f4eaa19 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -538,3 +538,31 @@ func TestConcurrentMapIterateWrite(t *testing.T) { t.Fatalf("output does not start with %q:\n%s", want, output) } } + +type point struct { + x, y *int +} + +func (p *point) negate() { + *p.x = *p.x * -1 + *p.y = *p.y * -1 +} + +// Test for issue #10152. +func TestPanicInlined(t *testing.T) { + defer func() { + r := recover() + if r == nil { + t.Fatalf("recover failed") + } + buf := make([]byte, 2048) + n := runtime.Stack(buf, false) + buf = buf[:n] + if !bytes.Contains(buf, []byte("(*point).negate(")) { + t.Fatalf("expecting stack trace to contain call to (*point).negate()") + } + }() + + pt := new(point) + pt.negate() +} diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go index 94bdc92853bf94..b16e5445bd1872 100644 --- a/src/runtime/symtab.go +++ b/src/runtime/symtab.go @@ -604,6 +604,23 @@ func funcname(f *_func) string { return gostringnocopy(cfuncname(f)) } +func funcnameFromNameoff(f *_func, nameoff int32) string { + datap := findmoduledatap(f.entry) // inefficient + if datap == nil { + return "" + } + cstr := &datap.pclntable[nameoff] + return gostringnocopy(cstr) +} + +func funcfile(f *_func, fileno int32) string { + datap := findmoduledatap(f.entry) // inefficient + if datap == nil { + return "?" + } + return gostringnocopy(&datap.pclntable[datap.filetab[fileno]]) +} + func funcline1(f *_func, targetpc uintptr, strict bool) (file string, line int32) { datap := findmoduledatap(f.entry) // inefficient if datap == nil { @@ -699,3 +716,11 @@ func stackmapdata(stkmap *stackmap, n int32) bitvector { } return bitvector{stkmap.nbit, (*byte)(add(unsafe.Pointer(&stkmap.bytedata), uintptr(n*((stkmap.nbit+7)/8))))} } + +// inlinedCall is the encoding of entries in the FUNCDATA_InlTree table. +type inlinedCall struct { + parent int32 // index of parent in the inltree, or < 0 + file int32 // fileno index into filetab + line int32 // line number of the call site + func_ int32 // offset into pclntab for name of called function +} diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index 274d5c4d498830..39ef8a2a6408d6 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -332,6 +332,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in } } if printing { + // assume skip=0 for printing if (flags&_TraceRuntimeFrames) != 0 || showframe(f, gp, nprint == 0) { // Print during crash. // main(0x1, 0x2, 0x3) @@ -341,6 +342,21 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in if (n > 0 || flags&_TraceTrap == 0) && frame.pc > f.entry && !waspanic { tracepc-- } + 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" @@ -358,7 +374,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in print(hex(argp[i])) } print(")\n") - file, line := funcline(f, tracepc) print("\t", file, ":", line) if frame.pc > f.entry { print(" +", hex(frame.pc-f.entry))