mirror of
https://github.com/golang/go
synced 2024-11-11 17:51:49 -07:00
[release-branch.go1.17] runtime: use saved LR when unwinding through morestack
On LR machine, consider F calling G calling H, which grows stack.
The stack looks like
...
G's frame:
... locals ...
saved LR = return PC in F <- SP points here at morestack
H's frame (to be created)
At morestack, we save
gp.sched.pc = H's morestack call
gp.sched.sp = H's entry SP (the arrow above)
gp.sched.lr = return PC in G
Currently, when unwinding through morestack (if _TraceJumpStack
is set), we switch PC and SP but not LR. We then have
frame.pc = H's morestack call
frame.sp = H's entry SP (the arrow above)
As LR is not set, we load it from stack at *sp, so
frame.lr = return PC in F
As the SP hasn't decremented at the morestack call,
frame.fp = frame.sp = H's entry SP
Unwinding a frame, we have
frame.pc = old frame.lr = return PC in F
frame.sp = old frame.fp = H's entry SP a.k.a. G's SP
The PC and SP don't match. The unwinding will go off if F and G
have different frame sizes.
Fix this by preserving the LR when switching stack.
Also add code to detect infinite loop in unwinding.
TODO: add some test. I can reproduce the infinite loop (or throw
with added check) but the frequency is low.
Fixes #53111.
Updates #52116.
Change-Id: I6e1294f1c6e55f664c962767a1cf6c466a0c0eff
Reviewed-on: https://go-review.googlesource.com/c/go/+/400575
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Cherry Mui <cherryyz@google.com>
Reviewed-by: Eric Fang <eric.fang@arm.com>
Reviewed-by: Benny Siegert <bsiegert@gmail.com>
(cherry picked from commit 74f0009422
)
Reviewed-on: https://go-review.googlesource.com/c/go/+/408822
Reviewed-by: Austin Clements <austin@google.com>
This commit is contained in:
parent
1ed3c127da
commit
c25b12fb81
@ -116,6 +116,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
|
||||
}
|
||||
waspanic := false
|
||||
cgoCtxt := gp.cgoCtxt
|
||||
stack := gp.stack
|
||||
printing := pcbuf == nil && callback == nil
|
||||
|
||||
// If the PC is zero, it's likely a nil function call.
|
||||
@ -134,7 +135,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
|
||||
if !f.valid() {
|
||||
if callback != nil || printing {
|
||||
print("runtime: unknown pc ", hex(frame.pc), "\n")
|
||||
tracebackHexdump(gp.stack, &frame, 0)
|
||||
tracebackHexdump(stack, &frame, 0)
|
||||
}
|
||||
if callback != nil {
|
||||
throw("unknown pc")
|
||||
@ -194,12 +195,15 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
|
||||
frame.fn = findfunc(frame.pc)
|
||||
f = frame.fn
|
||||
flag = f.flag
|
||||
frame.lr = gp.m.curg.sched.lr
|
||||
frame.sp = gp.m.curg.sched.sp
|
||||
stack = gp.m.curg.stack
|
||||
cgoCtxt = gp.m.curg.cgoCtxt
|
||||
case funcID_systemstack:
|
||||
// systemstack returns normally, so just follow the
|
||||
// stack transition.
|
||||
frame.sp = gp.m.curg.sched.sp
|
||||
stack = gp.m.curg.stack
|
||||
cgoCtxt = gp.m.curg.cgoCtxt
|
||||
flag &^= funcFlag_SPWRITE
|
||||
}
|
||||
@ -268,7 +272,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
|
||||
}
|
||||
if callback != nil || doPrint {
|
||||
print("runtime: unexpected return pc for ", funcname(f), " called from ", hex(frame.lr), "\n")
|
||||
tracebackHexdump(gp.stack, &frame, lrPtr)
|
||||
tracebackHexdump(stack, &frame, lrPtr)
|
||||
}
|
||||
if callback != nil {
|
||||
throw("unknown caller pc")
|
||||
@ -497,6 +501,13 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
|
||||
break
|
||||
}
|
||||
|
||||
if frame.pc == frame.lr && frame.sp == frame.fp {
|
||||
// If the next frame is identical to the current frame, we cannot make progress.
|
||||
print("runtime: traceback stuck. pc=", hex(frame.pc), " sp=", hex(frame.sp), "\n")
|
||||
tracebackHexdump(stack, &frame, frame.sp)
|
||||
throw("traceback stuck")
|
||||
}
|
||||
|
||||
// Unwind to next frame.
|
||||
frame.fn = flr
|
||||
frame.pc = frame.lr
|
||||
|
Loading…
Reference in New Issue
Block a user