diff options
Diffstat (limited to 'src/runtime/panic.c')
-rw-r--r-- | src/runtime/panic.c | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/src/runtime/panic.c b/src/runtime/panic.c new file mode 100644 index 000000000..50a7d3647 --- /dev/null +++ b/src/runtime/panic.c @@ -0,0 +1,219 @@ +// 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; + + d = runtime·newdefer(siz); + d->fn = fn; + d->pc = callerpc; + d->argp = argp; + runtime·memmove(d->args, (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; + + // Unwind to the stack frame with d's arguments in it. + runtime·unwindstack(gp, argp); + + // 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 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); +} + +// Free stack frames until we hit the last one +// or until we find the one that contains the sp. +void +runtime·unwindstack(G *gp, byte *sp) +{ + Stktop *top; + byte *stk; + + // Must be called from a different goroutine, usually m->g0. + if(g == gp) + runtime·throw("unwindstack on self"); + + while((top = (Stktop*)gp->stackbase) != 0 && top->stackbase != 0) { + stk = (byte*)gp->stackguard - StackGuard; + if(stk <= sp && sp < (byte*)gp->stackbase) + break; + gp->stackbase = top->stackbase; + gp->stackguard = top->stackguard; + gp->stackguard0 = gp->stackguard; + runtime·stackfree(gp, stk, top); + } + + if(sp != nil && (sp < (byte*)gp->stackguard - StackGuard || (byte*)gp->stackbase < sp)) { + runtime·printf("recover: %p not in [%p, %p]\n", sp, gp->stackguard - StackGuard, gp->stackbase); + runtime·throw("bad unwindstack"); + } +} + +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]; + 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); +} + +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; +} |