mirror of
https://github.com/golang/go
synced 2024-11-19 09:14:40 -07:00
3e804631d9
This is to reduce the delta between dev.cc and dev.garbage to just garbage collector changes. These are the files that had merge conflicts and have been edited by hand: malloc.go mem_linux.go mgc.go os1_linux.go proc1.go panic1.go runtime1.go LGTM=austin R=austin CC=golang-codereviews https://golang.org/cl/174180043
162 lines
4.1 KiB
Go
162 lines
4.1 KiB
Go
// 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
|
|
}
|