diff options
Diffstat (limited to 'src/pkg/runtime/panic.c')
-rw-r--r-- | src/pkg/runtime/panic.c | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/src/pkg/runtime/panic.c b/src/pkg/runtime/panic.c new file mode 100644 index 000000000..2f553f417 --- /dev/null +++ b/src/pkg/runtime/panic.c @@ -0,0 +1,487 @@ +// 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" + +// Code related to defer, panic and recover. + +uint32 runtime·panicking; +static Lock paniclk; + +enum +{ + DeferChunkSize = 2048 +}; + +// Allocate a Defer, usually as part of the larger frame of deferred functions. +// Each defer must be released with both popdefer and freedefer. +static Defer* +newdefer(int32 siz) +{ + int32 total; + DeferChunk *c; + Defer *d; + + c = g->dchunk; + total = sizeof(*d) + ROUND(siz, sizeof(uintptr)) - sizeof(d->args); + if(c == nil || total > DeferChunkSize - c->off) { + if(total > DeferChunkSize / 2) { + // Not worth putting in any chunk. + // Allocate a separate block. + d = runtime·malloc(total); + d->siz = siz; + d->special = 1; + d->free = 1; + d->link = g->defer; + g->defer = d; + return d; + } + + // Cannot fit in current chunk. + // Switch to next chunk, allocating if necessary. + c = g->dchunknext; + if(c == nil) + c = runtime·malloc(DeferChunkSize); + c->prev = g->dchunk; + c->off = sizeof(*c); + g->dchunk = c; + g->dchunknext = nil; + } + + d = (Defer*)((byte*)c + c->off); + c->off += total; + d->siz = siz; + d->special = 0; + d->free = 0; + d->link = g->defer; + g->defer = d; + return d; +} + +// Pop the current defer from the defer stack. +// Its contents are still valid until the goroutine begins executing again. +// In particular it is safe to call reflect.call(d->fn, d->argp, d->siz) after +// popdefer returns. +static void +popdefer(void) +{ + Defer *d; + DeferChunk *c; + int32 total; + + d = g->defer; + if(d == nil) + runtime·throw("runtime: popdefer nil"); + g->defer = d->link; + if(d->special) { + // Nothing else to do. + return; + } + total = sizeof(*d) + ROUND(d->siz, sizeof(uintptr)) - sizeof(d->args); + c = g->dchunk; + if(c == nil || (byte*)d+total != (byte*)c+c->off) + runtime·throw("runtime: popdefer phase error"); + c->off -= total; + if(c->off == sizeof(*c)) { + // Chunk now empty, so pop from stack. + // Save in dchunknext both to help with pingponging between frames + // and to make sure d is still valid on return. + if(g->dchunknext != nil) + runtime·free(g->dchunknext); + g->dchunknext = c; + g->dchunk = c->prev; + } +} + +// Free the given defer. +// For defers in the per-goroutine chunk this just clears the saved arguments. +// For large defers allocated on the heap, this frees them. +// The defer cannot be used after this call. +static void +freedefer(Defer *d) +{ + if(d->special) { + if(d->free) + runtime·free(d); + } else { + runtime·memclr((byte*)d->args, d->siz); + } +} + +// Create a new deferred function fn with siz bytes of arguments. +// The compiler turns a defer statement into a call to this. +// Cannot split the stack because it assumes that the arguments +// are available sequentially after &fn; they would not be +// copied if a stack split occurred. It's OK for this to call +// functions that split the stack. +#pragma textflag 7 +uintptr +runtime·deferproc(int32 siz, FuncVal *fn, ...) +{ + Defer *d; + + d = newdefer(siz); + d->fn = fn; + d->pc = runtime·getcallerpc(&siz); + if(thechar == '5') + d->argp = (byte*)(&fn+2); // skip caller's saved link register + else + d->argp = (byte*)(&fn+1); + runtime·memmove(d->args, d->argp, d->siz); + + // deferproc returns 0 normally. + // a deferred func that stops a panic + // makes the deferproc return 1. + // the code the compiler generates always + // checks the return value and jumps to the + // end of the function if deferproc returns != 0. + return 0; +} + +// Run a deferred function if there is one. +// The compiler inserts a call to this at the end of any +// function which calls defer. +// If there is a deferred function, this will call runtime·jmpdefer, +// which will jump to the deferred function such that it appears +// to have been called by the caller of deferreturn at the point +// just before deferreturn was called. The effect is that deferreturn +// is called again and again until there are no more deferred functions. +// Cannot split the stack because we reuse the caller's frame to +// call the deferred function. +#pragma textflag 7 +void +runtime·deferreturn(uintptr arg0) +{ + Defer *d; + byte *argp; + FuncVal *fn; + + d = g->defer; + if(d == nil) + return; + argp = (byte*)&arg0; + if(d->argp != argp) + return; + runtime·memmove(argp, d->args, d->siz); + fn = d->fn; + popdefer(); + freedefer(d); + runtime·jmpdefer(fn, argp); +} + +// Run all deferred functions for the current goroutine. +static void +rundefer(void) +{ + Defer *d; + + while((d = g->defer) != nil) { + popdefer(); + reflect·call(d->fn, (byte*)d->args, d->siz); + freedefer(d); + } +} + +// Print all currently active panics. Used when crashing. +static void +printpanics(Panic *p) +{ + if(p->link) { + printpanics(p->link); + runtime·printf("\t"); + } + runtime·printf("panic: "); + runtime·printany(p->arg); + if(p->recovered) + runtime·printf(" [recovered]"); + runtime·printf("\n"); +} + +static void recovery(G*); + +// The implementation of the predeclared function panic. +void +runtime·panic(Eface e) +{ + Defer *d; + Panic *p; + void *pc, *argp; + + p = runtime·mal(sizeof *p); + p->arg = e; + p->link = g->panic; + p->stackbase = (byte*)g->stackbase; + g->panic = p; + + for(;;) { + d = g->defer; + if(d == nil) + break; + // take defer off list in case of recursive panic + popdefer(); + g->ispanic = true; // rock for newstack, where reflect.call ends up + argp = d->argp; + pc = d->pc; + reflect·call(d->fn, (byte*)d->args, d->siz); + freedefer(d); + if(p->recovered) { + g->panic = p->link; + if(g->panic == nil) // must be done with signal + g->sig = 0; + runtime·free(p); + // Pass information about recovering frame to recovery. + g->sigcode0 = (uintptr)argp; + g->sigcode1 = (uintptr)pc; + runtime·mcall(recovery); + runtime·throw("recovery failed"); // mcall should not return + } + } + + // ran out of deferred calls - old-school panic now + runtime·startpanic(); + printpanics(g->panic); + runtime·dopanic(0); +} + +// 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. +static void +recovery(G *gp) +{ + void *argp; + void *pc; + + // Info about defer passed in G struct. + argp = (void*)gp->sigcode0; + pc = (void*)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') + gp->sched.sp = (uintptr)argp - 4*sizeof(uintptr); + else + gp->sched.sp = (uintptr)argp - 2*sizeof(uintptr); + gp->sched.pc = pc; + runtime·gogo(&gp->sched, 1); +} + +// 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) != nil && top->stackbase != nil) { + stk = (byte*)gp->stackguard - StackGuard; + if(stk <= sp && sp < (byte*)gp->stackbase) + break; + gp->stackbase = (uintptr)top->stackbase; + gp->stackguard = (uintptr)top->stackguard; + if(top->free != 0) + runtime·stackfree(stk, top->free); + } + + 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"); + } +} + +// The implementation of the predeclared function recover. +// Cannot split the stack because it needs to reliably +// find the stack segment of its caller. +#pragma textflag 7 +void +runtime·recover(byte *argp, Eface ret) +{ + Stktop *top, *oldtop; + Panic *p; + + // Must be a panic going on. + if((p = g->panic) == nil || p->recovered) + goto nomatch; + + // Frame must be at the top of the stack segment, + // because each deferred call starts a new stack + // segment as a side effect of using reflect.call. + // (There has to be some way to remember the + // variable argument frame size, and the segment + // code already takes care of that for us, so we + // reuse it.) + // + // As usual closures complicate things: the fp that + // the closure implementation function claims to have + // is where the explicit arguments start, after the + // implicit pointer arguments and PC slot. + // If we're on the first new segment for a closure, + // then fp == top - top->args is correct, but if + // the closure has its own big argument frame and + // allocated a second segment (see below), + // the fp is slightly above top - top->args. + // That condition can't happen normally though + // (stack pointers go down, not up), so we can accept + // any fp between top and top - top->args as + // indicating the top of the segment. + top = (Stktop*)g->stackbase; + if(argp < (byte*)top - top->argsize || (byte*)top < argp) + goto nomatch; + + // The deferred call makes a new segment big enough + // for the argument frame but not necessarily big + // enough for the function's local frame (size unknown + // at the time of the call), so the function might have + // made its own segment immediately. If that's the + // case, back top up to the older one, the one that + // reflect.call would have made for the panic. + // + // The fp comparison here checks that the argument + // frame that was copied during the split (the top->args + // bytes above top->fp) abuts the old top of stack. + // This is a correct test for both closure and non-closure code. + oldtop = (Stktop*)top->stackbase; + if(oldtop != nil && top->argp == (byte*)oldtop - top->argsize) + top = oldtop; + + // Now we have the segment that was created to + // run this call. It must have been marked as a panic segment. + if(!top->panic) + goto nomatch; + + // Okay, this is the top frame of a deferred call + // in response to a panic. It can see the panic argument. + p->recovered = 1; + ret = p->arg; + FLUSH(&ret); + return; + +nomatch: + ret.type = nil; + ret.data = nil; + FLUSH(&ret); +} + +void +runtime·startpanic(void) +{ + if(m->mcache == nil) // can happen if called from signal handler or throw + m->mcache = runtime·allocmcache(); + if(m->dying) { + runtime·printf("panic during panic\n"); + runtime·exit(3); + } + m->dying = 1; + runtime·xadd(&runtime·panicking, 1); + runtime·lock(&paniclk); +} + +void +runtime·dopanic(int32 unused) +{ + static bool didothers; + + if(g->sig != 0) + runtime·printf("[signal %x code=%p addr=%p pc=%p]\n", + g->sig, g->sigcode0, g->sigcode1, g->sigpc); + + if(runtime·gotraceback()){ + if(g != m->g0) { + runtime·printf("\n"); + runtime·goroutineheader(g); + runtime·traceback(runtime·getcallerpc(&unused), runtime·getcallersp(&unused), 0, g); + } + if(!didothers) { + didothers = true; + runtime·tracebackothers(g); + } + } + 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 Lock deadlock; + runtime·lock(&deadlock); + runtime·lock(&deadlock); + } + + runtime·exit(2); +} + +void +runtime·panicindex(void) +{ + runtime·panicstring("index out of range"); +} + +void +runtime·panicslice(void) +{ + runtime·panicstring("slice bounds out of range"); +} + +void +runtime·throwreturn(void) +{ + // can only happen if compiler is broken + runtime·throw("no return at end of a typed function - compiler is broken"); +} + +void +runtime·throwinit(void) +{ + // can only happen with linker skew + runtime·throw("recursive call during initialization - linker skew"); +} + +void +runtime·throw(int8 *s) +{ + if(m->throwing == 0) + m->throwing = 1; + runtime·startpanic(); + runtime·printf("fatal error: %s\n", s); + runtime·dopanic(0); + *(int32*)0 = 0; // not reached + runtime·exit(1); // even more not reached +} + +void +runtime·panicstring(int8 *s) +{ + Eface err; + + if(m->gcing) { + runtime·printf("panic: %s\n", s); + runtime·throw("panic during gc"); + } + runtime·newErrorString(runtime·gostringnocopy((byte*)s), &err); + runtime·panic(err); +} + +void +runtime·Goexit(void) +{ + rundefer(); + runtime·goexit(); +} |