blob: b19fdd0e1879185b280ec150e3830d67ef7cd80f [file] [log] [blame]
// 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.
#include "runtime.h"
#include "arch_GOARCH.h"
#include "stack.h"
#include "malloc.h"
#include "textflag.h"
// Code related to defer, panic and recover.
// 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);
}
// 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;
// Info about defer passed in G struct.
argp = (void*)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");
}
// 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(thechar == '5' || thechar == '9')
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);
}
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) {
case 0:
g->m->dying = 1;
if(g != nil) {
g->writebuf.array = nil;
g->writebuf.len = 0;
g->writebuf.cap = 0;
}
runtime·xadd(&runtime·panicking, 1);
runtime·lock(&paniclk);
if(runtime·debug.schedtrace > 0 || runtime·debug.scheddetail > 0)
runtime·schedtrace(true);
runtime·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);
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);
default:
// Can't even print! Just exit.
runtime·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);
}
if(!didothers) {
didothers = true;
runtime·tracebackothers(gp);
}
}
runtime·unlock(&paniclk);
if(runtime·xadd(&runtime·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);
}
if(crash)
runtime·crash();
runtime·exit(2);
}
#pragma textflag NOSPLIT
bool
runtime·canpanic(G *gp)
{
M *m;
uint32 status;
// 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;
// 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;
}