]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: use inlining tables to generate accurate tracebacks
authorDavid Lazar <lazard@golang.org>
Fri, 17 Feb 2017 21:08:36 +0000 (16:08 -0500)
committerDavid Lazar <lazard@golang.org>
Fri, 3 Mar 2017 21:29:34 +0000 (21:29 +0000)
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 <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
src/runtime/crash_test.go
src/runtime/symtab.go
src/runtime/traceback.go

index 8813c3655a3fb5c1c9d8bb2fde2955336e3dbf80..3f84935f4eaa19d7b564dc6e25d54152e8193694 100644 (file)
@@ -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()
+}
index 94bdc92853bf94a2f233232bfc38d7efbd085541..b16e5445bd1872a81ba9e48d3e2a5a07ac916dd9 100644 (file)
@@ -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
+}
index 274d5c4d498830a7aa546a4ad9d87e4ebcd404b4..39ef8a2a6408d682a917684501d2c9a89085ffb2 100644 (file)
@@ -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))