mirror of
https://github.com/golang/go
synced 2024-11-17 02:14:42 -07:00
runtime: move unrecoverable panic handling to the system stack
Currently parts of unrecoverable panic handling (notably, printing panic messages) can happen on the user stack. This may grow the stack, which is generally fine, but if we're handling a runtime panic, it's better to do as little as possible in case the runtime is in an inconsistent state. Hence, this commit rearranges the handling of unrecoverable panics so that it's done entirely on the system stack. This is mostly a matter of shuffling code a bit so everything can move into a systemstack block. The one slight subtlety is in the "panic during panic" case, where we now depend on startpanic_m's caller to print the stack rather than startpanic_m itself. To make this work, startpanic_m now returns a boolean indicating that the caller should avoid trying to print any panic messages and get right to the stack trace. Since the caller is already in a position to do this, this actually simplifies things a little. Change-Id: Id72febe8c0a9fb31d9369b600a1816d65a49bfed Reviewed-on: https://go-review.googlesource.com/93658 Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
parent
da022da900
commit
9d59234cbe
@ -121,7 +121,7 @@ func sighandler(_ureg *ureg, note *byte, gp *g) int {
|
||||
Throw:
|
||||
_g_.m.throwing = 1
|
||||
_g_.m.caughtsig.set(gp)
|
||||
startpanic()
|
||||
startpanic_m()
|
||||
print(notestr, "\n")
|
||||
print("PC=", hex(c.pc()), "\n")
|
||||
print("\n")
|
||||
|
@ -542,15 +542,9 @@ func gopanic(e interface{}) {
|
||||
// the world, we call preprintpanics to invoke all necessary Error
|
||||
// and String methods to prepare the panic strings before startpanic.
|
||||
preprintpanics(gp._panic)
|
||||
startpanic()
|
||||
|
||||
// startpanic set panicking, which will block main from exiting,
|
||||
// so now OK to decrement runningPanicDefers.
|
||||
atomic.Xadd(&runningPanicDefers, -1)
|
||||
|
||||
printpanics(gp._panic)
|
||||
dopanic(0) // should not return
|
||||
*(*int)(nil) = 0 // not reached
|
||||
fatalpanic(gp._panic) // should not return
|
||||
*(*int)(nil) = 0 // not reached
|
||||
}
|
||||
|
||||
// getargp returns the location where the caller
|
||||
@ -585,22 +579,6 @@ func gorecover(argp uintptr) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func startpanic() {
|
||||
systemstack(startpanic_m)
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func dopanic(unused int) {
|
||||
pc := getcallerpc()
|
||||
sp := getcallersp(unsafe.Pointer(&unused))
|
||||
gp := getg()
|
||||
systemstack(func() {
|
||||
dopanic_m(gp, pc, sp) // should never return
|
||||
})
|
||||
*(*int)(nil) = 0
|
||||
}
|
||||
|
||||
//go:linkname sync_throw sync.throw
|
||||
func sync_throw(s string) {
|
||||
throw(s)
|
||||
@ -613,8 +591,7 @@ func throw(s string) {
|
||||
if gp.m.throwing == 0 {
|
||||
gp.m.throwing = 1
|
||||
}
|
||||
startpanic()
|
||||
dopanic(0)
|
||||
fatalpanic(nil)
|
||||
*(*int)(nil) = 0 // not reached
|
||||
}
|
||||
|
||||
@ -655,13 +632,48 @@ func recovery(gp *g) {
|
||||
gogo(&gp.sched)
|
||||
}
|
||||
|
||||
// fatalpanic implements an unrecoverable panic. It freezes the
|
||||
// system, prints panic messages if msgs != nil, prints stack traces
|
||||
// starting from its caller, and terminates the process.
|
||||
//
|
||||
// If msgs != nil, it also decrements runningPanicDefers once main is
|
||||
// blocked from exiting.
|
||||
//
|
||||
//go:nosplit
|
||||
func fatalpanic(msgs *_panic) {
|
||||
pc := getcallerpc()
|
||||
sp := getcallersp(unsafe.Pointer(&msgs))
|
||||
gp := getg()
|
||||
// Switch to the system stack to avoid any stack growth, which
|
||||
// may make things worse if the runtime is in a bad state.
|
||||
systemstack(func() {
|
||||
if startpanic_m() && msgs != nil {
|
||||
// There were panic messages and startpanic_m
|
||||
// says it's okay to try to print them.
|
||||
|
||||
// startpanic_m set panicking, which will
|
||||
// block main from exiting, so now OK to
|
||||
// decrement runningPanicDefers.
|
||||
atomic.Xadd(&runningPanicDefers, -1)
|
||||
|
||||
printpanics(msgs)
|
||||
}
|
||||
|
||||
dopanic_m(gp, pc, sp) // should never return
|
||||
})
|
||||
*(*int)(nil) = 0 // not reached
|
||||
}
|
||||
|
||||
// startpanic_m prepares for an unrecoverable panic.
|
||||
//
|
||||
// It returns true if panic messages should be printed, or false if
|
||||
// the runtime is in bad shape and should just print stacks.
|
||||
//
|
||||
// It can have write barriers because the write barrier explicitly
|
||||
// ignores writes once dying > 0.
|
||||
//
|
||||
//go:yeswritebarrierrec
|
||||
func startpanic_m() {
|
||||
func startpanic_m() bool {
|
||||
_g_ := getg()
|
||||
if mheap_.cachealloc.size == 0 { // very early
|
||||
print("runtime: panic before malloc heap initialized\n")
|
||||
@ -682,15 +694,13 @@ func startpanic_m() {
|
||||
schedtrace(true)
|
||||
}
|
||||
freezetheworld()
|
||||
return
|
||||
return true
|
||||
case 1:
|
||||
// Something failed while panicking, probably the print of the
|
||||
// argument to panic(). Just print a stack trace and exit.
|
||||
// Something failed while panicking.
|
||||
// Just print a stack trace and exit.
|
||||
_g_.m.dying = 2
|
||||
print("panic during panic\n")
|
||||
dopanic(0)
|
||||
exit(3)
|
||||
fallthrough
|
||||
return false
|
||||
case 2:
|
||||
// This is a genuine bug in the runtime, we couldn't even
|
||||
// print the stack trace successfully.
|
||||
@ -701,6 +711,7 @@ func startpanic_m() {
|
||||
default:
|
||||
// Can't even print! Just exit.
|
||||
exit(5)
|
||||
return false // Need to return something.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
|
||||
_g_.m.caughtsig.set(gp)
|
||||
|
||||
if crashing == 0 {
|
||||
startpanic()
|
||||
startpanic_m()
|
||||
}
|
||||
|
||||
if sig < uint32(len(sigtable)) {
|
||||
|
Loading…
Reference in New Issue
Block a user