JE call // no g; still on a system stack
MOVQ g_m(R14), R13
- // Switch to g0 stack.
+
+ // Switch to g0 stack if we aren't already on g0 or gsignal.
+ MOVQ m_gsignal(R13), R10
+ CMPQ R10, R14
+ JE call // already on gsignal
+
MOVQ m_g0(R13), R10
CMPQ R10, R14
JE call // already on g0
// Switches SP to g0 stack and calls (FARG). Arguments already set.
TEXT asancall<>(SB), NOSPLIT, $0-0
MOVD RSP, R19 // callee-saved
- CBZ g, g0stack // no g, still on a system stack
+ CBZ g, call // no g, still on a system stack
MOVD g_m(g), R10
+
+ // Switch to g0 stack if we aren't already on g0 or gsignal.
+ MOVD m_gsignal(R10), R11
+ CMP R11, g
+ BEQ call
+
MOVD m_g0(R10), R11
CMP R11, g
- BEQ g0stack
+ BEQ call
MOVD (g_sched+gobuf_sp)(R11), R5
MOVD R5, RSP
-g0stack:
+call:
BL (FARG)
MOVD R19, RSP
RET
// Switches SP to g0 stack and calls (FARG). Arguments already set.
TEXT asancall<>(SB), NOSPLIT, $0-0
MOVV R3, R23 // callee-saved
- BEQ g, g0stack // no g, still on a system stack
+ BEQ g, call // no g, still on a system stack
MOVV g_m(g), R14
+
+ // Switch to g0 stack if we aren't already on g0 or gsignal.
+ MOVV m_gsignal(R14), R15
+ BEQ R15, g, call
+
MOVV m_g0(R14), R15
- BEQ R15, g, g0stack
+ BEQ R15, g, call
MOVV (g_sched+gobuf_sp)(R15), R9
MOVV R9, R3
-g0stack:
+call:
JAL (FARG)
MOVV R23, R3
RET
MOVD 0(R10), g
MOVD g_m(g), R7 // m for g
MOVD R1, R16 // callee-saved, preserved across C call
- MOVD m_g0(R7), R10 // g0 for m
- CMP R10, g // same g0?
- BEQ call // already on g0
+
+ // Switch to g0 stack if we aren't already on g0 or gsignal.
+ MOVD m_gsignal(R7), R10
+ CMP R10, g
+ BEQ call
+
+ MOVD m_g0(R7), R10
+ CMP R10, g
+ BEQ call
+
MOVD (g_sched+gobuf_sp)(R10), R1 // switch R1
+
call:
// prepare frame for C ABI
SUB $32, R1 // create frame for callee saving LR, CR, R2 etc.
// Switches SP to g0 stack and calls (X14). Arguments already set.
TEXT asancall<>(SB), NOSPLIT, $0-0
MOV X2, X8 // callee-saved
- BEQZ g, g0stack // no g, still on a system stack
+ BEQZ g, call // no g, still on a system stack
MOV g_m(g), X21
+
+ // Switch to g0 stack if we aren't already on g0 or gsignal.
+ MOV m_gsignal(X21), X21
+ BEQ X21, g, call
+
MOV m_g0(X21), X21
- BEQ X21, g, g0stack
+ BEQ X21, g, call
MOV (g_sched+gobuf_sp)(X21), X2
-g0stack:
+call:
JALR RA, X14
MOV X8, X2
RET
}
}
+func TestCgoTracebackContextProfile(t *testing.T) {
+ t.Parallel()
+ got := runTestProg(t, "testprogcgo", "TracebackContextProfile")
+ want := "OK\n"
+ if got != want {
+ t.Errorf("expected %q got %v", want, got)
+ }
+}
+
func testCgoPprof(t *testing.T, buildArg, runArg, top, bottom string) {
t.Parallel()
if runtime.GOOS != "linux" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "ppc64le" && runtime.GOARCH != "arm64" && runtime.GOARCH != "loong64") {
JE call // no g; still on a system stack
MOVQ g_m(R14), R13
- // Switch to g0 stack.
+
+ // Switch to g0 stack if we aren't already on g0 or gsignal.
+ MOVQ m_gsignal(R13), R10
+ CMPQ R10, R14
+ JE call // already on gsignal
+
MOVQ m_g0(R13), R10
CMPQ R10, R14
JE call // already on g0
// Switches SP to g0 stack and calls (FARG). Arguments already set.
TEXT msancall<>(SB), NOSPLIT, $0-0
MOVD RSP, R19 // callee-saved
- CBZ g, g0stack // no g, still on a system stack
+ CBZ g, call // no g, still on a system stack
MOVD g_m(g), R10
+
+ // Switch to g0 stack if we aren't already on g0 or gsignal.
+ MOVD m_gsignal(R10), R11
+ CMP R11, g
+ BEQ call
+
MOVD m_g0(R10), R11
CMP R11, g
- BEQ g0stack
+ BEQ call
MOVD (g_sched+gobuf_sp)(R11), R4
MOVD R4, RSP
-g0stack:
+call:
BL (FARG)
MOVD R19, RSP
RET
// Switches SP to g0 stack and calls (FARG). Arguments already set.
TEXT msancall<>(SB), NOSPLIT, $0-0
MOVV R3, R23 // callee-saved
- BEQ g, g0stack // no g, still on a system stack
+ BEQ g, call // no g, still on a system stack
MOVV g_m(g), R14
+
+ // Switch to g0 stack if we aren't already on g0 or gsignal.
+ MOVV m_gsignal(R14), R15
+ BEQ R15, g, call
+
MOVV m_g0(R14), R15
- BEQ R15, g, g0stack
+ BEQ R15, g, call
MOVV (g_sched+gobuf_sp)(R15), R9
MOVV R9, R3
MOVQ g_m(R14), R13
// Switch to g0 stack.
MOVQ SP, R12 // callee-saved, preserved across the CALL
+
+ // Switch to g0 stack if we aren't already on g0 or gsignal.
+ MOVQ m_gsignal(R13), R10
+ CMPQ R10, R14
+ JE call // already on gsignal
+
MOVQ m_g0(R13), R10
CMPQ R10, R14
JE call // already on g0
+
MOVQ (g_sched+gobuf_sp)(R10), SP
call:
ANDQ $~15, SP // alignment for gcc ABI
// Switch to g0 stack.
MOVD RSP, R19 // callee-saved, preserved across the CALL
MOVD R30, R20 // callee-saved, preserved across the CALL
+
+ // Switch to g0 stack if we aren't already on g0 or gsignal.
+ MOVD m_gsignal(R10), R11
+ CMP R11, g
+ BEQ call
+
MOVD m_g0(R10), R11
CMP R11, g
- BEQ call // already on g0
+ BEQ call
+
MOVD (g_sched+gobuf_sp)(R11), R12
MOVD R12, RSP
call:
MOVD 0(R10), g
MOVD g_m(g), R7 // m for g
MOVD R1, R16 // callee-saved, preserved across C call
- MOVD m_g0(R7), R10 // g0 for m
- CMP R10, g // same g0?
- BEQ call // already on g0
+
+ // Switch to g0 stack if we aren't already on g0 or gsignal.
+ MOVD m_gsignal(R7), R10
+ CMP R10, g
+ BEQ call
+
+ MOVD m_g0(R7), R10
+ CMP R10, g
+ BEQ call
+
MOVD (g_sched+gobuf_sp)(R10), R1 // switch R1
call:
// prepare frame for C ABI
BL runtime·save_g(SB) // Save g for callbacks.
MOVD R15, R7 // Save SP.
MOVD g_m(g), R8 // R8 = thread.
- MOVD m_g0(R8), R8 // R8 = g0.
- CMPBEQ R8, g, call // Already on g0?
+
+ // Switch to g0 stack if we aren't already on g0 or gsignal.
+ MOVD m_gsignal(R8), R8
+ CMPBEQ R8, g, call
+
+ MOVD m_g0(R8), R8
+ CMPBEQ R8, g, call
+
MOVD (g_sched+gobuf_sp)(R8), R15 // Switch SP to g0.
+
call: SUB $160, R15 // Allocate C frame.
BL R1 // Call C code.
MOVD R7, R15 // Restore SP.
extern void tcSymbolizer(void*);
extern int getContextCount(void);
extern void TracebackContextPreemptionCallGo(int);
+extern void TracebackContextProfileCallGo(void);
*/
import "C"
import (
"fmt"
+ "io"
"runtime"
+ "runtime/pprof"
"sync"
+ "sync/atomic"
"unsafe"
)
func init() {
register("TracebackContext", TracebackContext)
register("TracebackContextPreemption", TracebackContextPreemption)
+ register("TracebackContextProfile", TracebackContextProfile)
}
var tracebackOK bool
// Do some busy work.
fmt.Sprintf("%d\n", i)
}
+
+// Regression test for issue 71395.
+//
+// The SIGPROF handler can call the SetCgoTraceback traceback function if the
+// context function is also provided. Ensure that call is safe.
+func TracebackContextProfile() {
+ runtime.SetCgoTraceback(0, unsafe.Pointer(C.tcTraceback), unsafe.Pointer(C.tcContextSimple), unsafe.Pointer(C.tcSymbolizer))
+
+ if err := pprof.StartCPUProfile(io.Discard); err != nil {
+ panic(fmt.Sprintf("error starting CPU profile: %v", err))
+ }
+ defer pprof.StopCPUProfile()
+
+ const calls = 1e5
+ var wg sync.WaitGroup
+ for i := 0; i < runtime.GOMAXPROCS(0); i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for j := 0; j < calls; j++ {
+ C.TracebackContextProfileCallGo()
+ }
+ }()
+ }
+ wg.Wait()
+
+ fmt.Println("OK")
+}
+
+var sink atomic.Pointer[byte]
+
+//export TracebackContextProfileGoFunction
+func TracebackContextProfileGoFunction() {
+ // Issue 71395 occurs when SIGPROF lands on code running on the system
+ // stack in a cgo callback. The allocator uses the system stack.
+ b := make([]byte, 128)
+ sink.Store(&b[0])
+}
extern void G1(void);
extern void G2(void);
extern void TracebackContextPreemptionGoFunction(int);
+extern void TracebackContextProfileGoFunction(void);
void C1() {
G1();
void TracebackContextPreemptionCallGo(int i) {
TracebackContextPreemptionGoFunction(i);
}
+
+void TracebackContextProfileCallGo(void) {
+ TracebackContextProfileGoFunction();
+}