summaryrefslogtreecommitdiff
path: root/src/pkg/runtime/panic.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/runtime/panic.c')
-rw-r--r--src/pkg/runtime/panic.c153
1 files changed, 74 insertions, 79 deletions
diff --git a/src/pkg/runtime/panic.c b/src/pkg/runtime/panic.c
index d0cf3ad6f..8227a444d 100644
--- a/src/pkg/runtime/panic.c
+++ b/src/pkg/runtime/panic.c
@@ -6,6 +6,7 @@
#include "arch_GOARCH.h"
#include "stack.h"
#include "malloc.h"
+#include "../../cmd/ld/textflag.h"
// Code related to defer, panic and recover.
@@ -104,11 +105,15 @@ popdefer(void)
static void
freedefer(Defer *d)
{
+ int32 total;
+
if(d->special) {
if(d->free)
runtime·free(d);
} else {
- runtime·memclr((byte*)d->args, d->siz);
+ // Wipe out any possible pointers in argp/pc/fn/args.
+ total = sizeof(*d) + ROUND(d->siz, sizeof(uintptr)) - sizeof(d->args);
+ runtime·memclr((byte*)d, total);
}
}
@@ -118,7 +123,7 @@ freedefer(Defer *d)
// 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
+#pragma textflag NOSPLIT
uintptr
runtime·deferproc(int32 siz, FuncVal *fn, ...)
{
@@ -152,9 +157,14 @@ runtime·deferproc(int32 siz, FuncVal *fn, ...)
// 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
+//
+// The ... in the prototype keeps the compiler from declaring
+// an argument frame size. deferreturn is a very special function,
+// and if the runtime ever asks for its frame size, that means
+// the traceback routines are probably broken.
+#pragma textflag NOSPLIT
void
-runtime·deferreturn(uintptr arg0)
+runtime·deferreturn(uintptr arg0, ...)
{
Defer *d;
byte *argp;
@@ -166,10 +176,19 @@ runtime·deferreturn(uintptr arg0)
argp = (byte*)&arg0;
if(d->argp != argp)
return;
+
+ // Moving arguments around.
+ // Do not allow preemption here, because the garbage collector
+ // won't know the form of the arguments until the jmpdefer can
+ // flip the PC over to fn.
+ m->locks++;
runtime·memmove(argp, d->args, d->siz);
fn = d->fn;
popdefer();
freedefer(d);
+ m->locks--;
+ if(m->locks == 0 && g->preempt)
+ g->stackguard0 = StackPreempt;
runtime·jmpdefer(fn, argp);
}
@@ -214,7 +233,7 @@ runtime·panic(Eface e)
p = runtime·mal(sizeof *p);
p->arg = e;
p->link = g->panic;
- p->stackbase = (byte*)g->stackbase;
+ p->stackbase = g->stackbase;
g->panic = p;
for(;;) {
@@ -223,10 +242,10 @@ runtime·panic(Eface e)
break;
// take defer off list in case of recursive panic
popdefer();
- g->ispanic = true; // rock for newstack, where reflect.call ends up
+ g->ispanic = true; // rock for newstack, where reflect.newstackcall ends up
argp = d->argp;
pc = d->pc;
- reflect·call(d->fn, (byte*)d->args, d->siz);
+ runtime·newstackcall(d->fn, (byte*)d->args, d->siz);
freedefer(d);
if(p->recovered) {
g->panic = p->link;
@@ -254,11 +273,11 @@ static void
recovery(G *gp)
{
void *argp;
- void *pc;
+ uintptr pc;
// Info about defer passed in G struct.
argp = (void*)gp->sigcode0;
- pc = (void*)gp->sigcode1;
+ pc = (uintptr)gp->sigcode1;
// Unwind to the stack frame with d's arguments in it.
runtime·unwindstack(gp, argp);
@@ -277,7 +296,9 @@ recovery(G *gp)
else
gp->sched.sp = (uintptr)argp - 2*sizeof(uintptr);
gp->sched.pc = pc;
- runtime·gogo(&gp->sched, 1);
+ gp->sched.lr = 0;
+ gp->sched.ret = 1;
+ runtime·gogo(&gp->sched);
}
// Free stack frames until we hit the last one
@@ -292,14 +313,17 @@ runtime·unwindstack(G *gp, byte *sp)
if(g == gp)
runtime·throw("unwindstack on self");
- while((top = (Stktop*)gp->stackbase) != nil && top->stackbase != nil) {
+ while((top = (Stktop*)gp->stackbase) != 0 && top->stackbase != 0) {
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)
+ gp->stackbase = top->stackbase;
+ gp->stackguard = top->stackguard;
+ gp->stackguard0 = gp->stackguard;
+ if(top->free != 0) {
+ gp->stacksize -= top->free;
runtime·stackfree(stk, top->free);
+ }
}
if(sp != nil && (sp < (byte*)gp->stackguard - StackGuard || (byte*)gp->stackbase < sp)) {
@@ -311,80 +335,38 @@ runtime·unwindstack(G *gp, byte *sp)
// 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
+#pragma textflag NOSPLIT
void
runtime·recover(byte *argp, Eface ret)
{
- Stktop *top, *oldtop;
Panic *p;
+ Stktop *top;
- // 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.
+ // Must be an unrecovered panic in progress.
+ // Must be on a stack segment created for a deferred call during a panic.
+ // Must be at the top of that segment, meaning the deferred call itself
+ // and not something it called. The top frame in the segment will have
+ // argument pointer argp == top - top->argsize.
+ // The subtraction of g->panicwrap allows wrapper functions that
+ // do not count as official calls to adjust what we consider the top frame
+ // while they are active on the stack. The linker emits adjustments of
+ // g->panicwrap in the prologue and epilogue of functions marked as wrappers.
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;
+ p = g->panic;
+ if(p != nil && !p->recovered && top->panic && argp == (byte*)top - top->argsize - g->panicwrap) {
+ p->recovered = 1;
+ ret = p->arg;
+ } else {
+ ret.type = nil;
+ ret.data = nil;
+ }
FLUSH(&ret);
}
void
runtime·startpanic(void)
{
- if(runtime·mheap == 0 || runtime·mheap->cachealloc.size == 0) { // very early
+ if(runtime·mheap.cachealloc.size == 0) { // very early
runtime·printf("runtime: panic before malloc heap initialized\n");
m->mallocing = 1; // tell rest of panic not to try to malloc
} else if(m->mcache == nil) // can happen if called from signal handler or throw
@@ -394,8 +376,13 @@ runtime·startpanic(void)
runtime·exit(3);
}
m->dying = 1;
+ if(g != nil)
+ g->writebuf = nil;
runtime·xadd(&runtime·panicking, 1);
runtime·lock(&paniclk);
+ if(runtime·debug.schedtrace > 0 || runtime·debug.scheddetail > 0)
+ runtime·schedtrace(true);
+ runtime·freezetheworld();
}
void
@@ -403,16 +390,20 @@ runtime·dopanic(int32 unused)
{
static bool didothers;
bool crash;
+ int32 t;
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(&crash)){
+ if((t = runtime·gotraceback(&crash)) > 0){
if(g != m->g0) {
runtime·printf("\n");
runtime·goroutineheader(g);
- runtime·traceback(runtime·getcallerpc(&unused), runtime·getcallersp(&unused), 0, g);
+ runtime·traceback((uintptr)runtime·getcallerpc(&unused), (uintptr)runtime·getcallersp(&unused), 0, g);
+ } else if(t >= 2 || m->throwing > 0) {
+ runtime·printf("\nruntime stack:\n");
+ runtime·traceback((uintptr)runtime·getcallerpc(&unused), (uintptr)runtime·getcallersp(&unused), 0, g);
}
if(!didothers) {
didothers = true;
@@ -479,11 +470,15 @@ runtime·panicstring(int8 *s)
{
Eface err;
+ if(m->mallocing) {
+ runtime·printf("panic: %s\n", s);
+ runtime·throw("panic during malloc");
+ }
if(m->gcing) {
runtime·printf("panic: %s\n", s);
runtime·throw("panic during gc");
}
- runtime·newErrorString(runtime·gostringnocopy((byte*)s), &err);
+ runtime·newErrorCString(s, &err);
runtime·panic(err);
}