// 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.
gcUnwindBarriers(gp, sp)
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
}