]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: move unrecoverable panic handling to the system stack
authorAustin Clements <austin@google.com>
Thu, 18 Jan 2018 21:36:20 +0000 (16:36 -0500)
committerAustin Clements <austin@google.com>
Thu, 8 Mar 2018 22:55:51 +0000 (22:55 +0000)
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>
src/runtime/os3_plan9.go
src/runtime/panic.go
src/runtime/signal_sighandler.go

index 0b313d75e3b9d37b31eff0257e9329708a5e6469..b05965b63d1aa0a8a3fc368e5fc448b917cbdb68 100644 (file)
@@ -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")
index 715e802d107fbf310bcbf19ca1dfb540fe8c0ac3..cd2b18cc51f70fd16d90d02128048dfeceeb96b3 100644 (file)
@@ -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.
        }
 }
 
index bf2237c9814c8205c94acce4793bad14d479f9f6..13448929bcec3a07ae21fe2134a1137211bc9e8b 100644 (file)
@@ -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)) {