import (
"fmt"
+ "internal/testenv"
"reflect"
"regexp"
. "runtime"
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.
}
}
+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()
}
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
}