aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/traceback.go
diff options
context:
space:
mode:
authorHeschi Kreinick <heschi@google.com>2018-08-17 16:32:08 -0400
committerHeschi Kreinick <heschi@google.com>2018-08-23 19:48:13 +0000
commitae6361e4bd78b85c284881a16b9b3f81133db1bb (patch)
tree3f6964b2fdfc39d5a881f7dc14016d83ee9b911e /src/runtime/traceback.go
parentc9986d1452f3ef226bfd044fb0f128175a7dff03 (diff)
downloadgo-ae6361e4bd78b85c284881a16b9b3f81133db1bb.tar.gz
go-ae6361e4bd78b85c284881a16b9b3f81133db1bb.zip
runtime: handle morestack system stack transition in gentraceback
gentraceback handles system stack transitions, but only when they're done by systemstack(). Handle morestack() too. I tried to do this generically but systemstack and morestack are actually *very* different functions. Most notably, systemstack returns "normally", just messes with $sp along the way. morestack never returns at all -- it calls newstack, and newstack then jumps both stacks and functions back to whoever called morestack. I couldn't think of a way to handle both of them generically. So don't. The current implementation does not include systemstack on the generated traceback. That's partly because I don't know how to find its stack frame reliably, and partly because the current structure of the code wants to do the transition before the call, not after. If we're willing to assume that morestack's stack frame is 0 size, this could probably be fixed. For posterity's sake, alternatives tried: - Have morestack put a dummy function into schedbuf, like systemstack does. This actually worked (see patchset 1) but more by a series of coincidences than by deliberate design. The biggest coincidence was that because morestack_switch was a RET and its stack was 0 size, it actually worked to jump back to it at the end of newstack -- it would just return to the caller of morestack. Way too subtle for me, and also a little slower than just jumping directly. - Put morestack's PC and SP into schedbuf, so that gentraceback could treat it like a normal function except for the changing SP. This was a terrible idea and caused newstack to reenter morestack in a completely unreasonable state. To make testing possible I did a small redesign of testCPUProfile to take a callback that defines how to check if the conditions pass to it are satisfied. This seemed better than making the syntax of the "need" strings even more complicated. Updates #25943 Change-Id: I9271a30a976f80a093a3d4d1c7e9ec226faf74b4 Reviewed-on: https://go-review.googlesource.com/126795 Run-TryBot: Heschi Kreinick <heschi@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Austin Clements <austin@google.com>
Diffstat (limited to 'src/runtime/traceback.go')
-rw-r--r--src/runtime/traceback.go31
1 files changed, 22 insertions, 9 deletions
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 78589f5ea3..8370fd7593 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -197,16 +197,29 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
// Found an actual function.
// Derive frame pointer and link register.
if frame.fp == 0 {
- // We want to jump over the systemstack switch. If we're running on the
- // g0, this systemstack is at the top of the stack.
- // if we're not on g0 or there's a no curg, then this is a regular call.
- sp := frame.sp
- if flags&_TraceJumpStack != 0 && f.funcID == funcID_systemstack && gp == gp.m.g0 && gp.m.curg != nil {
- sp = gp.m.curg.sched.sp
- frame.sp = sp
- cgoCtxt = gp.m.curg.cgoCtxt
+ // Jump over system stack transitions. If we're on g0 and there's a user
+ // goroutine, try to jump. Otherwise this is a regular call.
+ if flags&_TraceJumpStack != 0 && gp == gp.m.g0 && gp.m.curg != nil {
+ switch f.funcID {
+ case funcID_morestack:
+ // morestack does not return normally -- newstack()
+ // gogo's to curg.sched. Match that.
+ // This keeps morestack() from showing up in the backtrace,
+ // but that makes some sense since it'll never be returned
+ // to.
+ frame.pc = gp.m.curg.sched.pc
+ frame.fn = findfunc(frame.pc)
+ f = frame.fn
+ frame.sp = gp.m.curg.sched.sp
+ cgoCtxt = gp.m.curg.cgoCtxt
+ case funcID_systemstack:
+ // systemstack returns normally, so just follow the
+ // stack transition.
+ frame.sp = gp.m.curg.sched.sp
+ cgoCtxt = gp.m.curg.cgoCtxt
+ }
}
- frame.fp = sp + uintptr(funcspdelta(f, frame.pc, &cache))
+ frame.fp = frame.sp + uintptr(funcspdelta(f, frame.pc, &cache))
if !usesLR {
// On x86, call instruction pushes return PC before entering new function.
frame.fp += sys.RegSize