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() { ...@@ -54,6 +54,11 @@ func throwinit() {
// The compiler turns a defer statement into a call to this. // The compiler turns a defer statement into a call to this.
//go:nosplit //go:nosplit
func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn 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 // the arguments of fn are in a perilous state. The stack map
// for deferproc does not describe them. So we can't let garbage // for deferproc does not describe them. So we can't let garbage
// collection or stack copying trigger until we've copied them out // 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 ...@@ -64,20 +69,18 @@ func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
if GOARCH == "arm" { if GOARCH == "arm" {
argp += ptrSize // skip caller's saved link register argp += ptrSize // skip caller's saved link register
} }
mp := acquirem() callerpc := getcallerpc(unsafe.Pointer(&siz))
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)
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. // deferproc returns 0 normally.
// a deferred func that stops a panic // a deferred func that stops a panic
...@@ -298,8 +301,6 @@ func Goexit() { ...@@ -298,8 +301,6 @@ func Goexit() {
goexit() goexit()
} }
func canpanic(*g) bool
// Print all currently active panics. Used when crashing. // Print all currently active panics. Used when crashing.
func printpanics(p *_panic) { func printpanics(p *_panic) {
if p.link != nil { if p.link != nil {
...@@ -318,6 +319,9 @@ func printpanics(p *_panic) { ...@@ -318,6 +319,9 @@ func printpanics(p *_panic) {
func gopanic(e interface{}) { func gopanic(e interface{}) {
gp := getg() gp := getg()
if gp.m.curg != gp { if gp.m.curg != gp {
print("panic: ")
printany(e)
print("\n")
gothrow("panic on m stack") gothrow("panic on m stack")
} }
...@@ -414,7 +418,7 @@ func gopanic(e interface{}) { ...@@ -414,7 +418,7 @@ func gopanic(e interface{}) {
// Pass information about recovering frame to recovery. // Pass information about recovering frame to recovery.
gp.sigcode0 = uintptr(argp) gp.sigcode0 = uintptr(argp)
gp.sigcode1 = pc gp.sigcode1 = pc
mcall(recovery_m) mcall(recovery)
gothrow("recovery failed") // mcall should not return gothrow("recovery failed") // mcall should not return
} }
} }
......
...@@ -2,63 +2,30 @@ ...@@ -2,63 +2,30 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
#include "runtime.h" package runtime
#include "arch_GOARCH.h"
#include "stack.h" import "unsafe"
#include "malloc.h"
#include "textflag.h"
// Code related to defer, panic and recover. // Code related to defer, panic and recover.
// TODO: Merge into panic.go.
// TODO: remove once code is moved to Go //uint32 runtime·panicking;
extern Defer* runtime·newdefer(int32 siz); var paniclk mutex
extern runtime·freedefer(Defer *d);
const hasLinkRegister = thechar == '5'
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);
}
// Unwind the stack after a deferred function calls recover // Unwind the stack after a deferred function calls recover
// after a panic. Then arrange to continue running as though // after a panic. Then arrange to continue running as though
// the caller of the deferred function returned normally. // the caller of the deferred function returned normally.
void func recovery(gp *g) {
runtime·recovery_m(G *gp)
{
void *argp;
uintptr pc;
// Info about defer passed in G struct. // Info about defer passed in G struct.
argp = (void*)gp->sigcode0; argp := (unsafe.Pointer)(gp.sigcode0)
pc = (uintptr)gp->sigcode1; pc := uintptr(gp.sigcode1)
// d's arguments need to be in the stack. // d's arguments need to be in the stack.
if(argp != nil && ((uintptr)argp < gp->stack.lo || gp->stack.hi < (uintptr)argp)) { 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); print("recover: ", argp, " not in [", hex(gp.stack.lo), ", ", hex(gp.stack.hi), "]\n")
runtime·throw("bad recovery"); gothrow("bad recovery")
} }
// Make the deferproc for this d return again, // Make the deferproc for this d return again,
...@@ -70,131 +37,132 @@ runtime·recovery_m(G *gp) ...@@ -70,131 +37,132 @@ runtime·recovery_m(G *gp)
// (The pc we're returning to does pop pop // (The pc we're returning to does pop pop
// before it tests the return value.) // before it tests the return value.)
// On the arm there are 2 saved LRs mixed in too. // On the arm there are 2 saved LRs mixed in too.
if(thechar == '5') if hasLinkRegister {
gp->sched.sp = (uintptr)argp - 4*sizeof(uintptr); gp.sched.sp = uintptr(argp) - 4*ptrSize
else } else {
gp->sched.sp = (uintptr)argp - 2*sizeof(uintptr); gp.sched.sp = uintptr(argp) - 2*ptrSize
gp->sched.pc = pc; }
gp->sched.lr = 0; gp.sched.pc = pc
gp->sched.ret = 1; gp.sched.lr = 0
runtime·gogo(&gp->sched); gp.sched.ret = 1
gogo(&gp.sched)
} }
void func startpanic_m() {
runtime·startpanic_m(void) _g_ := getg()
{ if mheap_.cachealloc.size == 0 { // very early
if(runtime·mheap.cachealloc.size == 0) { // very early print("runtime: panic before malloc heap initialized\n")
runtime·printf("runtime: panic before malloc heap initialized\n"); _g_.m.mallocing = 1 // tell rest of panic not to try to malloc
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
} else if(g->m->mcache == nil) // can happen if called from signal handler or throw _g_.m.mcache = allocmcache()
g->m->mcache = runtime·allocmcache(); }
switch(g->m->dying) {
switch _g_.m.dying {
case 0: case 0:
g->m->dying = 1; _g_.m.dying = 1
if(g != nil) { if _g_ != nil {
g->writebuf.array = nil; _g_.writebuf = nil
g->writebuf.len = 0; }
g->writebuf.cap = 0; xadd(&panicking, 1)
lock(&paniclk)
if debug.schedtrace > 0 || debug.scheddetail > 0 {
schedtrace(true)
} }
runtime·xadd(&runtime·panicking, 1); freezetheworld()
runtime·lock(&paniclk); return
if(runtime·debug.schedtrace > 0 || runtime·debug.scheddetail > 0)
runtime·schedtrace(true);
runtime·freezetheworld();
return;
case 1: case 1:
// Something failed while panicing, probably the print of the // Something failed while panicing, probably the print of the
// argument to panic(). Just print a stack trace and exit. // argument to panic(). Just print a stack trace and exit.
g->m->dying = 2; _g_.m.dying = 2
runtime·printf("panic during panic\n"); print("panic during panic\n")
runtime·dopanic(0); dopanic(0)
runtime·exit(3); exit(3)
fallthrough
case 2: case 2:
// This is a genuine bug in the runtime, we couldn't even // This is a genuine bug in the runtime, we couldn't even
// print the stack trace successfully. // print the stack trace successfully.
g->m->dying = 3; _g_.m.dying = 3
runtime·printf("stack trace unavailable\n"); print("stack trace unavailable\n")
runtime·exit(4); exit(4)
fallthrough
default: default:
// Can't even print! Just exit. // Can't even print! Just exit.
runtime·exit(5); exit(5)
} }
} }
void var didothers bool
runtime·dopanic_m(void) var deadlock mutex
{
G *gp; func dopanic_m() {
uintptr sp, pc; _g_ := getg()
static bool didothers;
bool crash; gp := (*g)(_g_.m.ptrarg[0])
int32 t; _g_.m.ptrarg[0] = nil
pc := uintptr(_g_.m.scalararg[0])
gp = g->m->ptrarg[0]; sp := uintptr(_g_.m.scalararg[1])
g->m->ptrarg[0] = nil; _g_.m.scalararg[1] = 0
pc = g->m->scalararg[0];
sp = g->m->scalararg[1]; if gp.sig != 0 {
g->m->scalararg[1] = 0; print("[signal ", hex(gp.sig), " code=", hex(gp.sigcode0), " addr=", hex(gp.sigcode1), " pc=", hex(gp.sigpc), "]\n")
if(gp->sig != 0) }
runtime·printf("[signal %x code=%p addr=%p pc=%p]\n",
gp->sig, gp->sigcode0, gp->sigcode1, gp->sigpc); var docrash bool
if t := gotraceback(&docrash); t > 0 {
if((t = runtime·gotraceback(&crash)) > 0){ if gp != gp.m.g0 {
if(gp != gp->m->g0) { print("\n")
runtime·printf("\n"); goroutineheader(gp)
runtime·goroutineheader(gp); traceback(pc, sp, 0, gp)
runtime·traceback(pc, sp, 0, gp); } else if t >= 2 || _g_.m.throwing > 0 {
} else if(t >= 2 || g->m->throwing > 0) { print("\nruntime stack:\n")
runtime·printf("\nruntime stack:\n"); traceback(pc, sp, 0, gp)
runtime·traceback(pc, sp, 0, gp);
} }
if(!didothers) { if !didothers {
didothers = true; didothers = true
runtime·tracebackothers(gp); tracebackothers(gp)
} }
} }
runtime·unlock(&paniclk); unlock(&paniclk)
if(runtime·xadd(&runtime·panicking, -1) != 0) {
if xadd(&panicking, -1) != 0 {
// Some other m is panicking too. // Some other m is panicking too.
// Let it print what it needs to print. // Let it print what it needs to print.
// Wait forever without chewing up cpu. // Wait forever without chewing up cpu.
// It will exit when it's done. // It will exit when it's done.
static Mutex deadlock; lock(&deadlock)
runtime·lock(&deadlock); lock(&deadlock)
runtime·lock(&deadlock);
} }
if(crash)
runtime·crash();
runtime·exit(2); if docrash {
} crash()
}
#pragma textflag NOSPLIT exit(2)
bool }
runtime·canpanic(G *gp)
{
M *m;
uint32 status;
//go:nosplit
func canpanic(gp *g) bool {
// Note that g is m->gsignal, different from gp. // Note that g is m->gsignal, different from gp.
// Note also that g->m can change at preemption, so m can go stale // Note also that g->m can change at preemption, so m can go stale
// if this function ever makes a function call. // 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? // Is it okay for gp to panic instead of crashing the program?
// Yes, as long as it is running Go code, not runtime code, // Yes, as long as it is running Go code, not runtime code,
// and not stuck in a system call. // and not stuck in a system call.
if(gp == nil || gp != m->curg) if gp == nil || gp != _m_.curg {
return false; return false
if(m->locks-m->softfloat != 0 || m->mallocing != 0 || m->throwing != 0 || m->gcing != 0 || m->dying != 0) }
return false; if _m_.locks-_m_.softfloat != 0 || _m_.mallocing != 0 || _m_.throwing != 0 || _m_.gcing != 0 || _m_.dying != 0 {
status = runtime·readgstatus(gp); return false
if((status&~Gscan) != Grunning || gp->syscallsp != 0) }
return false; status := readgstatus(gp)
#ifdef GOOS_windows if status&^_Gscan != _Grunning || gp.syscallsp != 0 {
if(m->libcallsp != 0) return false
return false; }
#endif if GOOS == "windows" && _m_.libcallsp != 0 {
return true; 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 @@ ...@@ -2,117 +2,24 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
/* // For the linkers. Must match Go definitions.
Stack layout parameters. // TODO(rsc): Share Go definitions with linkers directly.
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.
*/
enum { 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 #ifdef GOOS_windows
StackSystem = 512 * sizeof(uintptr), StackSystem = 512 * sizeof(uintptr),
#else #else
#ifdef GOOS_plan9 #ifdef GOOS_plan9
// The size of the note handler frame varies among architectures,
// but 512 bytes should be enough for every implementation.
StackSystem = 512, StackSystem = 512,
#else #else
StackSystem = 0, StackSystem = 0,
#endif // Plan 9 #endif // Plan 9
#endif // Windows #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, StackBig = 4096,
// The stack guard is a pointer this many bytes above the
// bottom of the stack.
StackGuard = 512 + StackSystem, 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, StackSmall = 128,
// The maximum number of bytes that a chain of NOSPLIT
// functions can use.
StackLimit = StackGuard - StackSystem - StackSmall, 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) #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 { ...@@ -22,8 +22,7 @@ func (f *Func) raw() *_func {
// funcdata.h // funcdata.h
const ( const (
_PCDATA_ArgSize = 0 _PCDATA_StackMapIndex = 0
_PCDATA_StackMapIndex = 1
_FUNCDATA_ArgsPointerMaps = 0 _FUNCDATA_ArgsPointerMaps = 0
_FUNCDATA_LocalsPointerMaps = 1 _FUNCDATA_LocalsPointerMaps = 1
_FUNCDATA_DeadValueMaps = 2 _FUNCDATA_DeadValueMaps = 2
......
...@@ -41,6 +41,7 @@ var ( ...@@ -41,6 +41,7 @@ var (
newprocPC uintptr newprocPC uintptr
rt0_goPC uintptr rt0_goPC uintptr
sigpanicPC uintptr sigpanicPC uintptr
switchtoMPC uintptr
externalthreadhandlerp uintptr // initialized elsewhere externalthreadhandlerp uintptr // initialized elsewhere
) )
...@@ -59,6 +60,7 @@ func tracebackinit() { ...@@ -59,6 +60,7 @@ func tracebackinit() {
newprocPC = funcPC(newproc) newprocPC = funcPC(newproc)
rt0_goPC = funcPC(rt0_go) rt0_goPC = funcPC(rt0_go)
sigpanicPC = funcPC(sigpanic) sigpanicPC = funcPC(sigpanic)
switchtoMPC = funcPC(switchtoM)
} }
// Traceback over the deferred function calls. // 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