| // Copyright 2012 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package runtime |
| |
| // Code related to defer, panic and recover. |
| // TODO: Merge into panic.go. |
| |
| //uint32 runtime·panicking; |
| var paniclk mutex |
| |
| const hasLinkRegister = GOARCH == "arm" || GOARCH == "arm64" || GOARCH == "ppc64" || GOARCH == "ppc64le" |
| |
| // Unwind the stack after a deferred function calls recover |
| // after a panic. Then arrange to continue running as though |
| // the caller of the deferred function returned normally. |
| func recovery(gp *g) { |
| // Info about defer passed in G struct. |
| sp := gp.sigcode0 |
| pc := gp.sigcode1 |
| |
| // d's arguments need to be in the stack. |
| if sp != 0 && (sp < gp.stack.lo || gp.stack.hi < sp) { |
| print("recover: ", hex(sp), " not in [", hex(gp.stack.lo), ", ", hex(gp.stack.hi), "]\n") |
| throw("bad recovery") |
| } |
| |
| // Make the deferproc for this d return again, |
| // this time returning 1. The calling function will |
| // jump to the standard return epilogue. |
| gp.sched.sp = sp |
| gp.sched.pc = pc |
| gp.sched.lr = 0 |
| gp.sched.ret = 1 |
| gogo(&gp.sched) |
| } |
| |
| func startpanic_m() { |
| _g_ := getg() |
| if mheap_.cachealloc.size == 0 { // very early |
| print("runtime: panic before malloc heap initialized\n") |
| _g_.m.mallocing = 1 // tell rest of panic not to try to malloc |
| } else if _g_.m.mcache == nil { // can happen if called from signal handler or throw |
| _g_.m.mcache = allocmcache() |
| } |
| |
| switch _g_.m.dying { |
| case 0: |
| _g_.m.dying = 1 |
| if _g_ != nil { |
| _g_.writebuf = nil |
| } |
| xadd(&panicking, 1) |
| lock(&paniclk) |
| if debug.schedtrace > 0 || debug.scheddetail > 0 { |
| schedtrace(true) |
| } |
| freezetheworld() |
| return |
| case 1: |
| // Something failed while panicing, probably the print of the |
| // argument to panic(). Just print a stack trace and exit. |
| _g_.m.dying = 2 |
| print("panic during panic\n") |
| dopanic(0) |
| exit(3) |
| fallthrough |
| case 2: |
| // This is a genuine bug in the runtime, we couldn't even |
| // print the stack trace successfully. |
| _g_.m.dying = 3 |
| print("stack trace unavailable\n") |
| exit(4) |
| fallthrough |
| default: |
| // Can't even print! Just exit. |
| exit(5) |
| } |
| } |
| |
| var didothers bool |
| var deadlock mutex |
| |
| func dopanic_m(gp *g, pc, sp uintptr) { |
| if gp.sig != 0 { |
| print("[signal ", hex(gp.sig), " code=", hex(gp.sigcode0), " addr=", hex(gp.sigcode1), " pc=", hex(gp.sigpc), "]\n") |
| } |
| |
| var docrash bool |
| _g_ := getg() |
| if t := gotraceback(&docrash); t > 0 { |
| if gp != gp.m.g0 { |
| print("\n") |
| goroutineheader(gp) |
| traceback(pc, sp, 0, gp) |
| } else if t >= 2 || _g_.m.throwing > 0 { |
| print("\nruntime stack:\n") |
| traceback(pc, sp, 0, gp) |
| } |
| if !didothers { |
| didothers = true |
| tracebackothers(gp) |
| } |
| } |
| unlock(&paniclk) |
| |
| if xadd(&panicking, -1) != 0 { |
| // Some other m is panicking too. |
| // Let it print what it needs to print. |
| // Wait forever without chewing up cpu. |
| // It will exit when it's done. |
| lock(&deadlock) |
| lock(&deadlock) |
| } |
| |
| if docrash { |
| crash() |
| } |
| |
| exit(2) |
| } |
| |
| //go:nosplit |
| func canpanic(gp *g) bool { |
| // Note that g is m->gsignal, different from gp. |
| // Note also that g->m can change at preemption, so m can go stale |
| // if this function ever makes a function call. |
| _g_ := getg() |
| _m_ := _g_.m |
| |
| // Is it okay for gp to panic instead of crashing the program? |
| // Yes, as long as it is running Go code, not runtime code, |
| // and not stuck in a system call. |
| if gp == nil || gp != _m_.curg { |
| return false |
| } |
| if _m_.locks-_m_.softfloat != 0 || _m_.mallocing != 0 || _m_.throwing != 0 || _m_.preemptoff != "" || _m_.dying != 0 { |
| return false |
| } |
| status := readgstatus(gp) |
| if status&^_Gscan != _Grunning || gp.syscallsp != 0 { |
| return false |
| } |
| if GOOS == "windows" && _m_.libcallsp != 0 { |
| return false |
| } |
| return true |
| } |