aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/traceback.go
diff options
context:
space:
mode:
authorKeith Randall <khr@golang.org>2019-06-05 18:42:31 +0000
committerKeith Randall <khr@golang.org>2019-06-05 19:50:09 +0000
commit49200e3f3e61f505acb152e150d054ef1db03b3e (patch)
treea0449ffcfb21af960ab9053eac39e6e42318b34e /src/runtime/traceback.go
parente9a136d185af8dcdb270096af520087c92c8b4af (diff)
downloadgo-49200e3f3e61f505acb152e150d054ef1db03b3e.tar.gz
go-49200e3f3e61f505acb152e150d054ef1db03b3e.zip
Revert "cmd/compile,runtime: allocate defer records on the stack"
This reverts commit fff4f599fe1c21e411a99de5c9b3777d06ce0ce6. Reason for revert: Seems to still have issues around GC. Fixes #32452 Change-Id: Ibe7af629f9ad6a3d5312acd7b066123f484da7f0 Reviewed-on: https://go-review.googlesource.com/c/go/+/180761 Run-TryBot: Keith Randall <khr@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
Diffstat (limited to 'src/runtime/traceback.go')
-rw-r--r--src/runtime/traceback.go42
1 files changed, 35 insertions, 7 deletions
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index ef48c9fa1f..d817018501 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -148,6 +148,11 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
waspanic := false
cgoCtxt := gp.cgoCtxt
printing := pcbuf == nil && callback == nil
+ _defer := gp._defer
+
+ for _defer != nil && _defer.sp == _NoArgs {
+ _defer = _defer.link
+ }
// If the PC is zero, it's likely a nil function call.
// Start in the caller's frame.
@@ -314,14 +319,15 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
// In the latter case, use a deferreturn call site as the continuation pc.
frame.continpc = frame.pc
if waspanic {
- if frame.fn.deferreturn != 0 {
+ // We match up defers with frames using the SP.
+ // However, if the function has an empty stack
+ // frame, then it's possible (on LR machines)
+ // for multiple call frames to have the same
+ // SP. But, since a function with no frame
+ // can't push a defer, the defer can't belong
+ // to that frame.
+ if _defer != nil && _defer.sp == frame.sp && frame.sp != frame.fp {
frame.continpc = frame.fn.entry + uintptr(frame.fn.deferreturn) + 1
- // Note: this may perhaps keep return variables alive longer than
- // strictly necessary, as we are using "function has a defer statement"
- // as a proxy for "function actually deferred something". It seems
- // to be a minor drawback. (We used to actually look through the
- // gp._defer for a defer corresponding to this function, but that
- // is hard to do with defer records on the stack during a stack copy.)
// Note: the +1 is to offset the -1 that
// stack.go:getStackMap does to back up a return
// address make sure the pc is in the CALL instruction.
@@ -330,6 +336,11 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
}
}
+ // Unwind our local defer stack past this frame.
+ for _defer != nil && ((_defer.sp == frame.sp && frame.sp != frame.fp) || _defer.sp == _NoArgs) {
+ _defer = _defer.link
+ }
+
if callback != nil {
if !callback((*stkframe)(noescape(unsafe.Pointer(&frame))), v) {
return n
@@ -499,6 +510,13 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
n = nprint
}
+ // If callback != nil, we're being called to gather stack information during
+ // garbage collection or stack growth. In that context, require that we used
+ // up the entire defer stack. If not, then there is a bug somewhere and the
+ // garbage collection or stack growth may not have seen the correct picture
+ // of the stack. Crash now instead of silently executing the garbage collection
+ // or stack copy incorrectly and setting up for a mysterious crash later.
+ //
// Note that panic != nil is okay here: there can be leftover panics,
// because the defers on the panic stack do not nest in frame order as
// they do on the defer stack. If you have:
@@ -539,6 +557,16 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
// At other times, such as when gathering a stack for a profiling signal
// or when printing a traceback during a crash, everything may not be
// stopped nicely, and the stack walk may not be able to complete.
+ // It's okay in those situations not to use up the entire defer stack:
+ // incomplete information then is still better than nothing.
+ if callback != nil && n < max && _defer != nil {
+ print("runtime: g", gp.goid, ": leftover defer sp=", hex(_defer.sp), " pc=", hex(_defer.pc), "\n")
+ for _defer = gp._defer; _defer != nil; _defer = _defer.link {
+ print("\tdefer ", _defer, " sp=", hex(_defer.sp), " pc=", hex(_defer.pc), "\n")
+ }
+ throw("traceback has leftover defers")
+ }
+
if callback != nil && n < max && frame.sp != gp.stktopsp {
print("runtime: g", gp.goid, ": frame.sp=", hex(frame.sp), " top=", hex(gp.stktopsp), "\n")
print("\tstack=[", hex(gp.stack.lo), "-", hex(gp.stack.hi), "] n=", n, " max=", max, "\n")