]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: fix callee tracking in traceback printing
authorAustin Clements <austin@google.com>
Wed, 15 Mar 2023 18:27:10 +0000 (14:27 -0400)
committerAustin Clements <austin@google.com>
Wed, 15 Mar 2023 23:53:51 +0000 (23:53 +0000)
In CL 466099, we accidentally stopped tracking callees while unwinding
inlined frames during traceback printing. The effect is that if you
have a call stack like:

  f -> wrapper -> inlined into wrapper -> panic

when considering whether to print the frame for "wrapper", we'll think
that wrapper called panic, rather than the inlined function.

Fix this in the traceback code and add a test.

Change-Id: I30ec836cc316846ce93de94e28a650e23dca184e
Reviewed-on: https://go-review.googlesource.com/c/go/+/476579
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Michael Pratt <mpratt@google.com>
src/runtime/stack_test.go
src/runtime/traceback.go

index 24f8290f676e9af5f9582df4f3f05566b720caf0..9a096f553896b8079969076eb71ebc50243caa1f 100644 (file)
@@ -6,6 +6,7 @@ package runtime_test
 
 import (
        "fmt"
+       "internal/testenv"
        "reflect"
        "regexp"
        . "runtime"
@@ -652,6 +653,8 @@ func (s structWithMethod) stack() string {
 
 func (s structWithMethod) nop() {}
 
+func (s structWithMethod) inlinablePanic() { panic("panic") }
+
 func TestStackWrapperCaller(t *testing.T) {
        var d structWithMethod
        // Force the compiler to construct a wrapper method.
@@ -689,6 +692,33 @@ func TestStackWrapperStack(t *testing.T) {
        }
 }
 
+func TestStackWrapperStackInlinePanic(t *testing.T) {
+       // Test that inline unwinding correctly tracks the callee by creating a
+       // stack of the form wrapper -> inlined function -> panic. If we mess up
+       // callee tracking, it will look like the wrapper called panic and we'll see
+       // the wrapper in the stack trace.
+       var d structWithMethod
+       wrapper := (*structWithMethod).inlinablePanic
+       defer func() {
+               err := recover()
+               if err == nil {
+                       t.Fatalf("expected panic")
+               }
+               buf := make([]byte, 4<<10)
+               stk := string(buf[:Stack(buf, false)])
+               if strings.Contains(stk, "<autogenerated>") {
+                       t.Fatalf("<autogenerated> appears in stack trace:\n%s", stk)
+               }
+               // Self-check: make sure inlinablePanic got inlined.
+               if !testenv.OptimizationOff() {
+                       if !strings.Contains(stk, "inlinablePanic(...)") {
+                               t.Fatalf("inlinablePanic not inlined")
+                       }
+               }
+       }()
+       wrapper(&d)
+}
+
 type I interface {
        M()
 }
index f0d61cd994d7e3a51e8ba1f4a8abc27ac74bbe51..9dfa97f6581d84fd31402cbe18c03eac6f2911db 100644 (file)
@@ -845,7 +845,9 @@ func traceback2(u *unwinder, showRuntime bool) int {
                f := u.frame.fn
                for iu, uf := newInlineUnwinder(f, u.symPC(), noEscapePtr(&u.cache)); n < max && uf.valid(); uf = iu.next(uf) {
                        sf := iu.srcFunc(uf)
-                       if !(showRuntime || showframe(sf, gp, n == 0, u.calleeFuncID)) {
+                       callee := u.calleeFuncID
+                       u.calleeFuncID = sf.funcID
+                       if !(showRuntime || showframe(sf, gp, n == 0, callee)) {
                                continue
                        }