runtime: don't elide wrapper functions that call panic or at TOS

CL 45412 started hiding autogenerated wrapper functions from call
stacks so that call stack semantics better matched language semantics.
This is based on the theory that the wrapper function will call the
"real" function and all the programmer knows about is the real
function.

However, this theory breaks down in two cases:

1. If the wrapper is at the top of the stack, then it didn't call
   anything. This can happen, for example, if the "stack" was actually
   synthesized by the user.

2. If the wrapper panics, for example by calling panicwrap or by
   dereferencing a nil pointer, then it didn't call the wrapped
   function and the user needs to see what panicked, even if we can't
   attribute it nicely.

This commit modifies the traceback logic to include the wrapper
function in both of these cases.

Fixes #22231.

Change-Id: I6e4339a652f73038bd8331884320f0b8edd86eb1
Reviewed-on: https://go-review.googlesource.com/76770
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 2282b2d..501ecb0 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -184,6 +184,7 @@
 	cgoCtxt := gp.cgoCtxt
 	printing := pcbuf == nil && callback == nil
 	_defer := gp._defer
+	elideWrapper := false
 
 	for _defer != nil && _defer.sp == _NoArgs {
 		_defer = _defer.link
@@ -386,8 +387,15 @@
 		}
 
 		if printing {
-			// assume skip=0 for printing
-			if (flags&_TraceRuntimeFrames) != 0 || showframe(f, gp, nprint == 0) {
+			// assume skip=0 for printing.
+			//
+			// Never elide wrappers if we haven't printed
+			// any frames. And don't elide wrappers that
+			// called panic rather than the wrapped
+			// function. Otherwise, leave them out.
+			name := funcname(f)
+			nextElideWrapper := elideWrapperCalling(name)
+			if (flags&_TraceRuntimeFrames) != 0 || showframe(f, gp, nprint == 0, elideWrapper && nprint != 0) {
 				// Print during crash.
 				//	main(0x1, 0x2, 0x3)
 				//		/home/rsc/go/src/runtime/x.go:23 +0xf
@@ -411,7 +419,6 @@
 						ix = inltree[ix].parent
 					}
 				}
-				name := funcname(f)
 				if name == "runtime.gopanic" {
 					name = "panic"
 				}
@@ -438,6 +445,7 @@
 				print("\n")
 				nprint++
 			}
+			elideWrapper = nextElideWrapper
 		}
 		n++
 
@@ -647,7 +655,7 @@
 	// Show what created goroutine, except main goroutine (goid 1).
 	pc := gp.gopc
 	f := findfunc(pc)
-	if f.valid() && showframe(f, gp, false) && gp.goid != 1 {
+	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 {
@@ -727,7 +735,7 @@
 	return gentraceback(^uintptr(0), ^uintptr(0), 0, gp, skip, &pcbuf[0], len(pcbuf), nil, nil, 0)
 }
 
-func showframe(f funcInfo, gp *g, firstFrame bool) bool {
+func showframe(f funcInfo, gp *g, firstFrame, elideWrapper bool) bool {
 	g := getg()
 	if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig.ptr()) {
 		return true
@@ -738,8 +746,18 @@
 		return true
 	}
 
+	if !f.valid() {
+		return false
+	}
+
+	if elideWrapper {
+		file, _ := funcline(f, f.entry)
+		if file == "<autogenerated>" {
+			return false
+		}
+	}
+
 	name := funcname(f)
-	file, _ := funcline(f, f.entry)
 
 	// Special case: always show runtime.gopanic frame
 	// in the middle of a stack trace, so that we can
@@ -750,7 +768,7 @@
 		return true
 	}
 
-	return f.valid() && contains(name, ".") && (!hasprefix(name, "runtime.") || isExportedRuntime(name)) && file != "<autogenerated>"
+	return contains(name, ".") && (!hasprefix(name, "runtime.") || isExportedRuntime(name))
 }
 
 // isExportedRuntime reports whether name is an exported runtime function.
@@ -760,6 +778,14 @@
 	return len(name) > n && name[:n] == "runtime." && 'A' <= name[n] && name[n] <= 'Z'
 }
 
+// elideWrapperCalling returns whether a wrapper function that called
+// function "name" should be elided from stack traces.
+func elideWrapperCalling(name string) 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")
+}
+
 var gStatusStrings = [...]string{
 	_Gidle:      "idle",
 	_Grunnable:  "runnable",