From 5f62ba621e9f1c925912f655901d0550e4a99f39 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 15 Mar 2023 14:27:10 -0400 Subject: [PATCH] runtime: fix callee tracking in traceback printing 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 Reviewed-by: Cherry Mui TryBot-Result: Gopher Robot Reviewed-by: Michael Pratt --- src/runtime/stack_test.go | 30 ++++++++++++++++++++++++++++++ src/runtime/traceback.go | 4 +++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/runtime/stack_test.go b/src/runtime/stack_test.go index 24f8290f676..9a096f55389 100644 --- a/src/runtime/stack_test.go +++ b/src/runtime/stack_test.go @@ -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, "") { + t.Fatalf(" 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() } diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index f0d61cd994d..9dfa97f6581 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -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 }