Commit 9d59234c authored by Austin Clements's avatar Austin Clements

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: 's avatarKeith Randall <khr@golang.org>
parent da022da9
......@@ -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)) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment