Skip to content

Commit 15d6ab6

Browse files
committed
runtime: make systemstack tail call if already switched
Currently systemstack always calls its argument, even if we're already on the system stack. Unfortunately, traceback with _TraceJump stops at the first systemstack it sees, which often cuts off runtime stacks early in profiles. Fix this by performing a tail call if we're already on the system stack. This eliminates it from the traceback entirely, so it won't stop prematurely (or all get mushed into a single node in the profile graph). Change-Id: Ibc69e8765e899f8d3806078517b8c7314da196f4 Reviewed-on: https://go-review.googlesource.com/74050 Reviewed-by: Cherry Zhang <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent 67a7d5d commit 15d6ab6

11 files changed

+83
-18
lines changed

src/runtime/asm_386.s

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -474,11 +474,12 @@ switch:
474474
RET
475475

476476
noswitch:
477-
// already on system stack, just call directly
477+
// already on system stack; tail call the function
478+
// Using a tail call here cleans up tracebacks since we won't stop
479+
// at an intermediate systemstack.
478480
MOVL DI, DX
479481
MOVL 0(DI), DI
480-
CALL DI
481-
RET
482+
JMP DI
482483

483484
/*
484485
* support for morestack

src/runtime/asm_amd64.s

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -419,11 +419,12 @@ switch:
419419
RET
420420

421421
noswitch:
422-
// already on m stack, just call directly
422+
// already on m stack; tail call the function
423+
// Using a tail call here cleans up tracebacks since we won't stop
424+
// at an intermediate systemstack.
423425
MOVQ DI, DX
424426
MOVQ 0(DI), DI
425-
CALL DI
426-
RET
427+
JMP DI
427428

428429
/*
429430
* support for morestack

src/runtime/asm_amd64p32.s

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,10 +306,11 @@ switch:
306306

307307
noswitch:
308308
// already on m stack, just call directly
309+
// Using a tail call here cleans up tracebacks since we won't stop
310+
// at an intermediate systemstack.
309311
MOVL DI, DX
310312
MOVL 0(DI), DI
311-
CALL DI
312-
RET
313+
JMP DI
313314

314315
/*
315316
* support for morestack

src/runtime/asm_arm.s

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,10 +358,12 @@ switch:
358358
RET
359359

360360
noswitch:
361+
// Using a tail call here cleans up tracebacks since we won't stop
362+
// at an intermediate systemstack.
361363
MOVW R0, R7
362364
MOVW 0(R0), R0
363-
BL (R0)
364-
RET
365+
MOVW.P 4(R13), R14 // restore LR
366+
B (R0)
365367

366368
/*
367369
* support for morestack

src/runtime/asm_arm64.s

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,11 @@ switch:
239239

240240
noswitch:
241241
// already on m stack, just call directly
242+
// Using a tail call here cleans up tracebacks since we won't stop
243+
// at an intermediate systemstack.
242244
MOVD 0(R26), R3 // code pointer
243-
BL (R3)
244-
RET
245+
MOVD.P 16(RSP), R30 // restore LR
246+
B (R3)
245247

246248
/*
247249
* support for morestack

src/runtime/asm_mips64x.s

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,12 @@ switch:
214214

215215
noswitch:
216216
// already on m stack, just call directly
217+
// Using a tail call here cleans up tracebacks since we won't stop
218+
// at an intermediate systemstack.
217219
MOVV 0(REGCTXT), R4 // code pointer
218-
JAL (R4)
219-
RET
220+
MOVV 0(R29), R31 // restore LR
221+
ADDV $8, R29
222+
JMP (R4)
220223

221224
/*
222225
* support for morestack

src/runtime/asm_mipsx.s

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,12 @@ switch:
215215

216216
noswitch:
217217
// already on m stack, just call directly
218+
// Using a tail call here cleans up tracebacks since we won't stop
219+
// at an intermediate systemstack.
218220
MOVW 0(REGCTXT), R4 // code pointer
219-
JAL (R4)
220-
RET
221+
MOVW 0(R29), R31 // restore LR
222+
ADD $4, R29
223+
JMP (R4)
221224

222225
/*
223226
* support for morestack

src/runtime/asm_ppc64x.s

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,9 @@ switch:
265265

266266
noswitch:
267267
// already on m stack, just call directly
268+
// On other arches we do a tail call here, but it appears to be
269+
// impossible to tail call a function pointer in shared mode on
270+
// ppc64 because the caller is responsible for restoring the TOC.
268271
MOVD 0(R11), R12 // code pointer
269272
MOVD R12, CTR
270273
BL (CTR)

src/runtime/asm_s390x.s

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,9 +224,12 @@ switch:
224224

225225
noswitch:
226226
// already on m stack, just call directly
227+
// Using a tail call here cleans up tracebacks since we won't stop
228+
// at an intermediate systemstack.
227229
MOVD 0(R12), R3 // code pointer
228-
BL (R3)
229-
RET
230+
MOVD 0(R15), LR // restore LR
231+
ADD $8, R15
232+
BR (R3)
230233

231234
/*
232235
* support for morestack

src/runtime/export_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,3 +395,16 @@ func LockOSCounts() (external, internal uint32) {
395395
}
396396
return g.m.lockedExt, g.m.lockedInt
397397
}
398+
399+
//go:noinline
400+
func TracebackSystemstack(stk []uintptr, i int) int {
401+
if i == 0 {
402+
pc, sp := getcallerpc(), getcallersp(unsafe.Pointer(&stk))
403+
return gentraceback(pc, sp, 0, getg(), 0, &stk[0], len(stk), nil, nil, _TraceJumpStack)
404+
}
405+
n := 0
406+
systemstack(func() {
407+
n = TracebackSystemstack(stk, i-1)
408+
})
409+
return n
410+
}

src/runtime/stack_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package runtime_test
66

77
import (
8+
"bytes"
89
"fmt"
910
. "runtime"
1011
"strings"
@@ -685,3 +686,35 @@ func TestStackWrapperStack(t *testing.T) {
685686
t.Fatalf("<autogenerated> appears in stack trace:\n%s", stk)
686687
}
687688
}
689+
690+
func TestTracebackSystemstack(t *testing.T) {
691+
if GOARCH == "ppc64" || GOARCH == "ppc64le" {
692+
t.Skip("systemstack tail call not implemented on ppc64x")
693+
}
694+
695+
// Test that profiles correctly jump over systemstack,
696+
// including nested systemstack calls.
697+
pcs := make([]uintptr, 20)
698+
pcs = pcs[:TracebackSystemstack(pcs, 5)]
699+
// Check that runtime.TracebackSystemstack appears five times
700+
// and that we see TestTracebackSystemstack.
701+
countIn, countOut := 0, 0
702+
frames := CallersFrames(pcs)
703+
var tb bytes.Buffer
704+
for {
705+
frame, more := frames.Next()
706+
fmt.Fprintf(&tb, "\n%s+0x%x %s:%d", frame.Function, frame.PC-frame.Entry, frame.File, frame.Line)
707+
switch frame.Function {
708+
case "runtime.TracebackSystemstack":
709+
countIn++
710+
case "runtime_test.TestTracebackSystemstack":
711+
countOut++
712+
}
713+
if !more {
714+
break
715+
}
716+
}
717+
if countIn != 5 || countOut != 1 {
718+
t.Fatalf("expected 5 calls to TracebackSystemstack and 1 call to TestTracebackSystemstack, got:%s", tb.String())
719+
}
720+
}

0 commit comments

Comments
 (0)