summaryrefslogtreecommitdiff
path: root/src/runtime/panic.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime/panic.c')
-rw-r--r--src/runtime/panic.c219
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;
+}