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 <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go
index 8813c36..3f84935 100644
--- a/src/runtime/crash_test.go
+++ b/src/runtime/crash_test.go
@@ -538,3 +538,31 @@
 		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 94bdc92..b16e544 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -604,6 +604,23 @@
 	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 @@
 	}
 	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 274d5c4..39ef8a2 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -332,6 +332,7 @@
 			}
 		}
 		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 @@
 				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 @@
 					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))