Commit 15d6ab69 authored by Austin Clements's avatar Austin Clements

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/74050Reviewed-by: default avatarCherry Zhang <cherryyz@google.com>
Reviewed-by: default avatarKeith Randall <khr@golang.org>
parent 67a7d5d8
......@@ -474,11 +474,12 @@ switch:
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
......
......@@ -419,11 +419,12 @@ switch:
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
......
......@@ -306,10 +306,11 @@ switch:
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
......
......@@ -358,10 +358,12 @@ switch:
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
......
......@@ -239,9 +239,11 @@ switch:
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
......
......@@ -214,9 +214,12 @@ switch:
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
......
......@@ -215,9 +215,12 @@ switch:
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
......
......@@ -265,6 +265,9 @@ switch:
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)
......
......@@ -224,9 +224,12 @@ switch:
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
......
......@@ -395,3 +395,16 @@ func LockOSCounts() (external, internal uint32) {
}
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
}
......@@ -5,6 +5,7 @@
package runtime_test
import (
"bytes"
"fmt"
. "runtime"
"strings"
......@@ -685,3 +686,35 @@ func TestStackWrapperStack(t *testing.T) {
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())
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment