aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/panic.go
diff options
context:
space:
mode:
authorKeith Randall <khr@golang.org>2026-01-12 15:05:24 -0800
committerGopher Robot <gobot@golang.org>2026-03-06 10:08:12 -0800
commit50d988e4e037d9d41ac223a62706dfea47a100e4 (patch)
treecccebaec15e091c18a1da50a0c0253c9809642be /src/runtime/panic.go
parentbf84b002d64d0b150818268e520fee0172a5c462 (diff)
downloadgo-50d988e4e037d9d41ac223a62706dfea47a100e4.tar.xz
runtime: when panicking, skip ahead to previous panic
While looking up the stack for a defer to run, if we come across a panic frame we can skip ahead (up) to where the previous panic was looking for a defer to run. Switch from keeping LR (the caller's pc) to PC (the frame's PC). Seems easier to reason about. Fixes #77062 Change-Id: Idb39411ebad8c072c8f65c62a518da848bddbd61 Reviewed-on: https://go-review.googlesource.com/c/go/+/738041 Reviewed-by: Keith Randall <khr@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Diffstat (limited to 'src/runtime/panic.go')
-rw-r--r--src/runtime/panic.go95
1 files changed, 90 insertions, 5 deletions
diff --git a/src/runtime/panic.go b/src/runtime/panic.go
index d467e9305d..1429279ea2 100644
--- a/src/runtime/panic.go
+++ b/src/runtime/panic.go
@@ -920,7 +920,7 @@ func (p *_panic) start(pc uintptr, sp unsafe.Pointer) {
// caller instead, we avoid needing to unwind through an extra
// frame. It also somewhat simplifies the terminating condition for
// deferreturn.
- p.lr, p.fp = pc, sp
+ p.pc, p.sp = pc, sp
p.nextFrame()
}
@@ -995,7 +995,7 @@ func (p *_panic) nextDefer() (func(), bool) {
// nextFrame finds the next frame that contains deferred calls, if any.
func (p *_panic) nextFrame() (ok bool) {
- if p.lr == 0 {
+ if p.pc == 0 {
return false
}
@@ -1007,10 +1007,10 @@ func (p *_panic) nextFrame() (ok bool) {
}
var u unwinder
- u.initAt(p.lr, uintptr(p.fp), 0, gp, 0)
+ u.initAt(p.pc, uintptr(p.sp), 0, gp, 0)
for {
if !u.valid() {
- p.lr = 0
+ p.pc = 0
return // ok == false
}
@@ -1027,10 +1027,25 @@ func (p *_panic) nextFrame() (ok bool) {
break // found a frame with open-coded defers
}
+ if p.link != nil && uintptr(u.frame.sp) == uintptr(p.link.startSP) && uintptr(p.link.sp) > u.frame.sp {
+ // Skip ahead to where the next panic up the stack was last looking
+ // for defers. See issue 77062.
+ //
+ // The startSP condition is to check when we have walked up the stack
+ // to where the next panic up the stack started. If so, the processing
+ // of that panic has run all the defers up to its current scanning
+ // position.
+ //
+ // The final condition is just to make sure that the line below
+ // is actually helpful.
+ u.initAt(p.link.pc, uintptr(p.link.sp), 0, gp, 0)
+ continue
+ }
+
u.next()
}
- p.lr = u.frame.lr
+ p.pc = u.frame.pc
p.sp = unsafe.Pointer(u.frame.sp)
p.fp = unsafe.Pointer(u.frame.fp)
@@ -1702,3 +1717,73 @@ func isAbortPC(pc uintptr) bool {
}
return f.funcID == abi.FuncID_abort
}
+
+// For debugging only.
+//go:noinline
+//go:nosplit
+func dumpPanicDeferState(where string, gp *g) {
+ systemstack(func() {
+ println("DUMPPANICDEFERSTATE", where)
+ p := gp._panic
+ d := gp._defer
+ var u unwinder
+ for u.init(gp, 0); u.valid(); u.next() {
+ // Print frame.
+ println(" frame sp=", hex(u.frame.sp), "fp=", hex(u.frame.fp), "pc=", pcName(u.frame.pc), "+", pcOff(u.frame.pc))
+ // Print panic.
+ for p != nil && uintptr(p.sp) == u.frame.sp {
+ println(" panic", p, "sp=", p.sp, "fp=", p.fp, "arg=", p.arg, "recovered=", p.recovered, "pc=", pcName(p.pc), "+", pcOff(p.pc), "retpc=", pcName(p.retpc), "+", pcOff(p.retpc), "startsp=", p.startSP, "gopanicfp=", p.gopanicFP, "startPC=", hex(p.startPC), pcName(p.startPC), "+", pcOff(p.startPC))
+ p = p.link
+ }
+
+ // Print linked defers.
+ for d != nil && d.sp == u.frame.sp {
+ println(" defer(link)", "heap=", d.heap, "rangefunc=", d.rangefunc, fnName(d.fn))
+ d = d.link
+ }
+
+ // Print open-coded defers.
+ // (A function is all linked or all open-coded, so we don't
+ // need to interleave this loop with the one above.)
+ fd := funcdata(u.frame.fn, abi.FUNCDATA_OpenCodedDeferInfo)
+ if fd != nil {
+ deferBitsOffset, fd := readvarintUnsafe(fd)
+ m := *(*uint8)(unsafe.Pointer(u.frame.varp - uintptr(deferBitsOffset)))
+ slotsOffset, fd := readvarintUnsafe(fd)
+ slots := u.frame.varp - uintptr(slotsOffset)
+ for i := 7; i >= 0; i-- {
+ if m>>i&1 == 0 {
+ continue
+ }
+ fn := *(*func())(unsafe.Pointer(slots + uintptr(i)*goarch.PtrSize))
+ println(" defer(open)", fnName(fn))
+ }
+ }
+
+ }
+ if p != nil {
+ println(" REMAINING PANICS!", p)
+ }
+ if d != nil {
+ println(" REMAINING DEFERS!")
+ }
+ })
+}
+
+func pcName(pc uintptr) string {
+ fn := findfunc(pc)
+ if !fn.valid() {
+ return "<unk>"
+ }
+ return funcname(fn)
+}
+func pcOff(pc uintptr) hex {
+ fn := findfunc(pc)
+ if !fn.valid() {
+ return 0
+ }
+ return hex(pc - fn.entry())
+}
+func fnName(fn func()) string {
+ return pcName(**(**uintptr)(unsafe.Pointer(&fn)))
+}