// 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 import "unsafe" // Code related to defer, panic and recover. // TODO: Merge into panic.go. //uint32 runtime·panicking; var paniclk mutex const hasLinkRegister = GOARCH == "arm" || GOARCH == "power64" || GOARCH == "power64le" // 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. argp := (unsafe.Pointer)(gp.sigcode0) pc := uintptr(gp.sigcode1) // d's arguments need to be in the stack. if argp != nil && (uintptr(argp) < gp.stack.lo || gp.stack.hi < uintptr(argp)) { print("recover: ", argp, " not in [", hex(gp.stack.lo), ", ", hex(gp.stack.hi), "]\n") gothrow("bad recovery") } // Make the deferproc for this d return again, // this time returning 1. The calling function will // jump to the standard return epilogue. // The -2*sizeof(uintptr) makes up for the // two extra words that are on the stack at // each call to deferproc. // (The pc we're returning to does pop pop // before it tests the return value.) // On the arm and power there are 2 saved LRs mixed in too. if hasLinkRegister { gp.sched.sp = uintptr(argp) - 4*ptrSize } else { gp.sched.sp = uintptr(argp) - 2*ptrSize } 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_.gcing != 0 || _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 }