Commit d98553a7 authored by Russ Cox's avatar Russ Cox

[dev.cc] runtime: convert panic and stack code from C to Go

The conversion was done with an automated tool and then
modified only as necessary to make it compile and run.

[This CL is part of the removal of C code from package runtime.
See golang.org/s/dev.cc for an overview.]

LGTM=r
R=r, dave
CC=austin, dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/166520043
parent 0d49f7b5
......@@ -54,6 +54,11 @@ func throwinit() {
// The compiler turns a defer statement into a call to this.
//go:nosplit
func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
if getg().m.curg != getg() {
// go code on the m stack can't defer
gothrow("defer on m")
}
// the arguments of fn are in a perilous state. The stack map
// for deferproc does not describe them. So we can't let garbage
// collection or stack copying trigger until we've copied them out
......@@ -64,20 +69,18 @@ func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
if GOARCH == "arm" {
argp += ptrSize // skip caller's saved link register
}
mp := acquirem()
mp.scalararg[0] = uintptr(siz)
mp.ptrarg[0] = unsafe.Pointer(fn)
mp.scalararg[1] = argp
mp.scalararg[2] = getcallerpc(unsafe.Pointer(&siz))
if mp.curg != getg() {
// go code on the m stack can't defer
gothrow("defer on m")
}
onM(deferproc_m)
callerpc := getcallerpc(unsafe.Pointer(&siz))
releasem(mp)
onM(func() {
d := newdefer(siz)
if d._panic != nil {
gothrow("deferproc: d.panic != nil after newdefer")
}
d.fn = fn
d.pc = callerpc
d.argp = argp
memmove(add(unsafe.Pointer(d), unsafe.Sizeof(*d)), unsafe.Pointer(argp), uintptr(siz))
})
// deferproc returns 0 normally.
// a deferred func that stops a panic
......@@ -298,8 +301,6 @@ func Goexit() {
goexit()
}
func canpanic(*g) bool
// Print all currently active panics. Used when crashing.
func printpanics(p *_panic) {
if p.link != nil {
......@@ -318,6 +319,9 @@ func printpanics(p *_panic) {
func gopanic(e interface{}) {
gp := getg()
if gp.m.curg != gp {
print("panic: ")
printany(e)
print("\n")
gothrow("panic on m stack")
}
......@@ -414,7 +418,7 @@ func gopanic(e interface{}) {
// Pass information about recovering frame to recovery.
gp.sigcode0 = uintptr(argp)
gp.sigcode1 = pc
mcall(recovery_m)
mcall(recovery)
gothrow("recovery failed") // mcall should not return
}
}
......
......@@ -2,63 +2,30 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "runtime.h"
#include "arch_GOARCH.h"
#include "stack.h"
#include "malloc.h"
#include "textflag.h"
package runtime
import "unsafe"
// Code related to defer, panic and recover.
// TODO: Merge into panic.go.
// TODO: remove once code is moved to Go
extern Defer* runtime·newdefer(int32 siz);
extern runtime·freedefer(Defer *d);
uint32 runtime·panicking;
static Mutex paniclk;
void
runtime·deferproc_m(void)
{
int32 siz;
FuncVal *fn;
uintptr argp;
uintptr callerpc;
Defer *d;
siz = g->m->scalararg[0];
fn = g->m->ptrarg[0];
argp = g->m->scalararg[1];
callerpc = g->m->scalararg[2];
g->m->ptrarg[0] = nil;
g->m->scalararg[1] = 0;
d = runtime·newdefer(siz);
if(d->panic != nil)
runtime·throw("deferproc: d->panic != nil after newdefer");
d->fn = fn;
d->pc = callerpc;
d->argp = argp;
runtime·memmove(d+1, (void*)argp, siz);
}
//uint32 runtime·panicking;
var paniclk mutex
const hasLinkRegister = thechar == '5'
// 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.
void
runtime·recovery_m(G *gp)
{
void *argp;
uintptr pc;
func recovery(gp *g) {
// Info about defer passed in G struct.
argp = (void*)gp->sigcode0;
pc = (uintptr)gp->sigcode1;
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)) {
runtime·printf("recover: %p not in [%p, %p]\n", argp, gp->stack.lo, gp->stack.hi);
runtime·throw("bad recovery");
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,
......@@ -70,131 +37,132 @@ runtime·recovery_m(G *gp)
// (The pc we're returning to does pop pop
// before it tests the return value.)
// On the arm there are 2 saved LRs mixed in too.
if(thechar == '5')
gp->sched.sp = (uintptr)argp - 4*sizeof(uintptr);
else
gp->sched.sp = (uintptr)argp - 2*sizeof(uintptr);
gp->sched.pc = pc;
gp->sched.lr = 0;
gp->sched.ret = 1;
runtime·gogo(&gp->sched);
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)
}
void
runtime·startpanic_m(void)
{
if(runtime·mheap.cachealloc.size == 0) { // very early
runtime·printf("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 = runtime·allocmcache();
switch(g->m->dying) {
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.array = nil;
g->writebuf.len = 0;
g->writebuf.cap = 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)
}
runtime·xadd(&runtime·panicking, 1);
runtime·lock(&paniclk);
if(runtime·debug.schedtrace > 0 || runtime·debug.scheddetail > 0)
runtime·schedtrace(true);
runtime·freezetheworld();
return;
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;
runtime·printf("panic during panic\n");
runtime·dopanic(0);
runtime·exit(3);
_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;
runtime·printf("stack trace unavailable\n");
runtime·exit(4);
_g_.m.dying = 3
print("stack trace unavailable\n")
exit(4)
fallthrough
default:
// Can't even print! Just exit.
runtime·exit(5);
exit(5)
}
}
void
runtime·dopanic_m(void)
{
G *gp;
uintptr sp, pc;
static bool didothers;
bool crash;
int32 t;
gp = g->m->ptrarg[0];
g->m->ptrarg[0] = nil;
pc = g->m->scalararg[0];
sp = g->m->scalararg[1];
g->m->scalararg[1] = 0;
if(gp->sig != 0)
runtime·printf("[signal %x code=%p addr=%p pc=%p]\n",
gp->sig, gp->sigcode0, gp->sigcode1, gp->sigpc);
if((t = runtime·gotraceback(&crash)) > 0){
if(gp != gp->m->g0) {
runtime·printf("\n");
runtime·goroutineheader(gp);
runtime·traceback(pc, sp, 0, gp);
} else if(t >= 2 || g->m->throwing > 0) {
runtime·printf("\nruntime stack:\n");
runtime·traceback(pc, sp, 0, gp);
var didothers bool
var deadlock mutex
func dopanic_m() {
_g_ := getg()
gp := (*g)(_g_.m.ptrarg[0])
_g_.m.ptrarg[0] = nil
pc := uintptr(_g_.m.scalararg[0])
sp := uintptr(_g_.m.scalararg[1])
_g_.m.scalararg[1] = 0
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
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;
runtime·tracebackothers(gp);
if !didothers {
didothers = true
tracebackothers(gp)
}
}
runtime·unlock(&paniclk);
if(runtime·xadd(&runtime·panicking, -1) != 0) {
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.
static Mutex deadlock;
runtime·lock(&deadlock);
runtime·lock(&deadlock);
lock(&deadlock)
lock(&deadlock)
}
if(crash)
runtime·crash();
runtime·exit(2);
}
if docrash {
crash()
}
#pragma textflag NOSPLIT
bool
runtime·canpanic(G *gp)
{
M *m;
uint32 status;
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.
m = g->m;
_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 = runtime·readgstatus(gp);
if((status&~Gscan) != Grunning || gp->syscallsp != 0)
return false;
#ifdef GOOS_windows
if(m->libcallsp != 0)
return false;
#endif
return true;
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
}
This diff is collapsed.
// Copyright 2011 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
const (
// Goroutine preemption request.
// Stored into g->stackguard0 to cause split stack check failure.
// Must be greater than any real sp.
// 0xfffffade in hex.
stackPreempt = ^uintptr(1313)
)
......@@ -2,117 +2,24 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Stack layout parameters.
Included both by runtime (compiled via 6c) and linkers (compiled via gcc).
The per-goroutine g->stackguard is set to point StackGuard bytes
above the bottom of the stack. Each function compares its stack
pointer against g->stackguard to check for overflow. To cut one
instruction from the check sequence for functions with tiny frames,
the stack is allowed to protrude StackSmall bytes below the stack
guard. Functions with large frames don't bother with the check and
always call morestack. The sequences are (for amd64, others are
similar):
guard = g->stackguard
frame = function's stack frame size
argsize = size of function arguments (call + return)
stack frame size <= StackSmall:
CMPQ guard, SP
JHI 3(PC)
MOVQ m->morearg, $(argsize << 32)
CALL morestack(SB)
stack frame size > StackSmall but < StackBig
LEAQ (frame-StackSmall)(SP), R0
CMPQ guard, R0
JHI 3(PC)
MOVQ m->morearg, $(argsize << 32)
CALL morestack(SB)
stack frame size >= StackBig:
MOVQ m->morearg, $((argsize << 32) | frame)
CALL morestack(SB)
The bottom StackGuard - StackSmall bytes are important: there has
to be enough room to execute functions that refuse to check for
stack overflow, either because they need to be adjacent to the
actual caller's frame (deferproc) or because they handle the imminent
stack overflow (morestack).
For example, deferproc might call malloc, which does one of the
above checks (without allocating a full frame), which might trigger
a call to morestack. This sequence needs to fit in the bottom
section of the stack. On amd64, morestack's frame is 40 bytes, and
deferproc's frame is 56 bytes. That fits well within the
StackGuard - StackSmall bytes at the bottom.
The linkers explore all possible call traces involving non-splitting
functions to make sure that this limit cannot be violated.
*/
// For the linkers. Must match Go definitions.
// TODO(rsc): Share Go definitions with linkers directly.
enum {
// StackSystem is a number of additional bytes to add
// to each stack below the usual guard area for OS-specific
// purposes like signal handling. Used on Windows and on
// Plan 9 because they do not use a separate stack.
#ifdef GOOS_windows
StackSystem = 512 * sizeof(uintptr),
#else
#ifdef GOOS_plan9
// The size of the note handler frame varies among architectures,
// but 512 bytes should be enough for every implementation.
StackSystem = 512,
#else
StackSystem = 0,
#endif // Plan 9
#endif // Windows
// The minimum size of stack used by Go code
StackMin = 2048,
// The minimum stack size to allocate.
// The hackery here rounds FixedStack0 up to a power of 2.
FixedStack0 = StackMin + StackSystem,
FixedStack1 = FixedStack0 - 1,
FixedStack2 = FixedStack1 | (FixedStack1 >> 1),
FixedStack3 = FixedStack2 | (FixedStack2 >> 2),
FixedStack4 = FixedStack3 | (FixedStack3 >> 4),
FixedStack5 = FixedStack4 | (FixedStack4 >> 8),
FixedStack6 = FixedStack5 | (FixedStack5 >> 16),
FixedStack = FixedStack6 + 1,
// Functions that need frames bigger than this use an extra
// instruction to do the stack split check, to avoid overflow
// in case SP - framesize wraps below zero.
// This value can be no bigger than the size of the unmapped
// space at zero.
StackBig = 4096,
// The stack guard is a pointer this many bytes above the
// bottom of the stack.
StackGuard = 512 + StackSystem,
// After a stack split check the SP is allowed to be this
// many bytes below the stack guard. This saves an instruction
// in the checking sequence for tiny frames.
StackSmall = 128,
// The maximum number of bytes that a chain of NOSPLIT
// functions can use.
StackLimit = StackGuard - StackSystem - StackSmall,
};
// Goroutine preemption request.
// Stored into g->stackguard0 to cause split stack check failure.
// Must be greater than any real sp.
// 0xfffffade in hex.
#define StackPreempt ((uint64)-1314)
/*c2go
enum
{
StackPreempt = -1314,
};
*/
#define StackFork ((uint64)-1234)
This diff is collapsed.
// Copyright 2011 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
/*
Stack layout parameters.
Included both by runtime (compiled via 6c) and linkers (compiled via gcc).
The per-goroutine g->stackguard is set to point StackGuard bytes
above the bottom of the stack. Each function compares its stack
pointer against g->stackguard to check for overflow. To cut one
instruction from the check sequence for functions with tiny frames,
the stack is allowed to protrude StackSmall bytes below the stack
guard. Functions with large frames don't bother with the check and
always call morestack. The sequences are (for amd64, others are
similar):
guard = g->stackguard
frame = function's stack frame size
argsize = size of function arguments (call + return)
stack frame size <= StackSmall:
CMPQ guard, SP
JHI 3(PC)
MOVQ m->morearg, $(argsize << 32)
CALL morestack(SB)
stack frame size > StackSmall but < StackBig
LEAQ (frame-StackSmall)(SP), R0
CMPQ guard, R0
JHI 3(PC)
MOVQ m->morearg, $(argsize << 32)
CALL morestack(SB)
stack frame size >= StackBig:
MOVQ m->morearg, $((argsize << 32) | frame)
CALL morestack(SB)
The bottom StackGuard - StackSmall bytes are important: there has
to be enough room to execute functions that refuse to check for
stack overflow, either because they need to be adjacent to the
actual caller's frame (deferproc) or because they handle the imminent
stack overflow (morestack).
For example, deferproc might call malloc, which does one of the
above checks (without allocating a full frame), which might trigger
a call to morestack. This sequence needs to fit in the bottom
section of the stack. On amd64, morestack's frame is 40 bytes, and
deferproc's frame is 56 bytes. That fits well within the
StackGuard - StackSmall bytes at the bottom.
The linkers explore all possible call traces involving non-splitting
functions to make sure that this limit cannot be violated.
*/
const (
// StackSystem is a number of additional bytes to add
// to each stack below the usual guard area for OS-specific
// purposes like signal handling. Used on Windows and on
// Plan 9 because they do not use a separate stack.
_StackSystem = _Windows*512*ptrSize + _Plan9*512
// The minimum size of stack used by Go code
_StackMin = 2048
// The minimum stack size to allocate.
// The hackery here rounds FixedStack0 up to a power of 2.
_FixedStack0 = _StackMin + _StackSystem
_FixedStack1 = _FixedStack0 - 1
_FixedStack2 = _FixedStack1 | (_FixedStack1 >> 1)
_FixedStack3 = _FixedStack2 | (_FixedStack2 >> 2)
_FixedStack4 = _FixedStack3 | (_FixedStack3 >> 4)
_FixedStack5 = _FixedStack4 | (_FixedStack4 >> 8)
_FixedStack6 = _FixedStack5 | (_FixedStack5 >> 16)
_FixedStack = _FixedStack6 + 1
// Functions that need frames bigger than this use an extra
// instruction to do the stack split check, to avoid overflow
// in case SP - framesize wraps below zero.
// This value can be no bigger than the size of the unmapped
// space at zero.
_StackBig = 4096
// The stack guard is a pointer this many bytes above the
// bottom of the stack.
_StackGuard = 512 + _StackSystem
// After a stack split check the SP is allowed to be this
// many bytes below the stack guard. This saves an instruction
// in the checking sequence for tiny frames.
_StackSmall = 128
// The maximum number of bytes that a chain of NOSPLIT
// functions can use.
_StackLimit = _StackGuard - _StackSystem - _StackSmall
)
// Goroutine preemption request.
// Stored into g->stackguard0 to cause split stack check failure.
// Must be greater than any real sp.
// 0xfffffade in hex.
const (
_StackPreempt = uintptrMask & -1314
_StackFork = uintptrMask & -1234
)
......@@ -22,8 +22,7 @@ func (f *Func) raw() *_func {
// funcdata.h
const (
_PCDATA_ArgSize = 0
_PCDATA_StackMapIndex = 1
_PCDATA_StackMapIndex = 0
_FUNCDATA_ArgsPointerMaps = 0
_FUNCDATA_LocalsPointerMaps = 1
_FUNCDATA_DeadValueMaps = 2
......
......@@ -41,6 +41,7 @@ var (
newprocPC uintptr
rt0_goPC uintptr
sigpanicPC uintptr
switchtoMPC uintptr
externalthreadhandlerp uintptr // initialized elsewhere
)
......@@ -59,6 +60,7 @@ func tracebackinit() {
newprocPC = funcPC(newproc)
rt0_goPC = funcPC(rt0_go)
sigpanicPC = funcPC(sigpanic)
switchtoMPC = funcPC(switchtoM)
}
// Traceback over the deferred function calls.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment