RET
noswitch:
- // already on system stack, just call directly
+ // already on system stack; tail call the function
+ // Using a tail call here cleans up tracebacks since we won't stop
+ // at an intermediate systemstack.
MOVL DI, DX
MOVL 0(DI), DI
- CALL DI
- RET
+ JMP DI
/*
* support for morestack
RET
noswitch:
- // already on m stack, just call directly
+ // already on m stack; tail call the function
+ // Using a tail call here cleans up tracebacks since we won't stop
+ // at an intermediate systemstack.
MOVQ DI, DX
MOVQ 0(DI), DI
- CALL DI
- RET
+ JMP DI
/*
* support for morestack
noswitch:
// already on m stack, just call directly
+ // Using a tail call here cleans up tracebacks since we won't stop
+ // at an intermediate systemstack.
MOVL DI, DX
MOVL 0(DI), DI
- CALL DI
- RET
+ JMP DI
/*
* support for morestack
RET
noswitch:
+ // Using a tail call here cleans up tracebacks since we won't stop
+ // at an intermediate systemstack.
MOVW R0, R7
MOVW 0(R0), R0
- BL (R0)
- RET
+ MOVW.P 4(R13), R14 // restore LR
+ B (R0)
/*
* support for morestack
noswitch:
// already on m stack, just call directly
+ // Using a tail call here cleans up tracebacks since we won't stop
+ // at an intermediate systemstack.
MOVD 0(R26), R3 // code pointer
- BL (R3)
- RET
+ MOVD.P 16(RSP), R30 // restore LR
+ B (R3)
/*
* support for morestack
noswitch:
// already on m stack, just call directly
+ // Using a tail call here cleans up tracebacks since we won't stop
+ // at an intermediate systemstack.
MOVV 0(REGCTXT), R4 // code pointer
- JAL (R4)
- RET
+ MOVV 0(R29), R31 // restore LR
+ ADDV $8, R29
+ JMP (R4)
/*
* support for morestack
noswitch:
// already on m stack, just call directly
+ // Using a tail call here cleans up tracebacks since we won't stop
+ // at an intermediate systemstack.
MOVW 0(REGCTXT), R4 // code pointer
- JAL (R4)
- RET
+ MOVW 0(R29), R31 // restore LR
+ ADD $4, R29
+ JMP (R4)
/*
* support for morestack
noswitch:
// already on m stack, just call directly
+ // On other arches we do a tail call here, but it appears to be
+ // impossible to tail call a function pointer in shared mode on
+ // ppc64 because the caller is responsible for restoring the TOC.
MOVD 0(R11), R12 // code pointer
MOVD R12, CTR
BL (CTR)
noswitch:
// already on m stack, just call directly
+ // Using a tail call here cleans up tracebacks since we won't stop
+ // at an intermediate systemstack.
MOVD 0(R12), R3 // code pointer
- BL (R3)
- RET
+ MOVD 0(R15), LR // restore LR
+ ADD $8, R15
+ BR (R3)
/*
* support for morestack
}
return g.m.lockedExt, g.m.lockedInt
}
+
+//go:noinline
+func TracebackSystemstack(stk []uintptr, i int) int {
+ if i == 0 {
+ pc, sp := getcallerpc(), getcallersp(unsafe.Pointer(&stk))
+ return gentraceback(pc, sp, 0, getg(), 0, &stk[0], len(stk), nil, nil, _TraceJumpStack)
+ }
+ n := 0
+ systemstack(func() {
+ n = TracebackSystemstack(stk, i-1)
+ })
+ return n
+}
package runtime_test
import (
+ "bytes"
"fmt"
. "runtime"
"strings"
t.Fatalf("<autogenerated> appears in stack trace:\n%s", stk)
}
}
+
+func TestTracebackSystemstack(t *testing.T) {
+ if GOARCH == "ppc64" || GOARCH == "ppc64le" {
+ t.Skip("systemstack tail call not implemented on ppc64x")
+ }
+
+ // Test that profiles correctly jump over systemstack,
+ // including nested systemstack calls.
+ pcs := make([]uintptr, 20)
+ pcs = pcs[:TracebackSystemstack(pcs, 5)]
+ // Check that runtime.TracebackSystemstack appears five times
+ // and that we see TestTracebackSystemstack.
+ countIn, countOut := 0, 0
+ frames := CallersFrames(pcs)
+ var tb bytes.Buffer
+ for {
+ frame, more := frames.Next()
+ fmt.Fprintf(&tb, "\n%s+0x%x %s:%d", frame.Function, frame.PC-frame.Entry, frame.File, frame.Line)
+ switch frame.Function {
+ case "runtime.TracebackSystemstack":
+ countIn++
+ case "runtime_test.TestTracebackSystemstack":
+ countOut++
+ }
+ if !more {
+ break
+ }
+ }
+ if countIn != 5 || countOut != 1 {
+ t.Fatalf("expected 5 calls to TracebackSystemstack and 1 call to TestTracebackSystemstack, got:%s", tb.String())
+ }
+}