summaryrefslogtreecommitdiff
path: root/src/pkg
diff options
context:
space:
mode:
authorKeith Randall <khr@golang.org>2014-09-05 10:04:16 -0400
committerKeith Randall <khr@golang.org>2014-09-05 10:04:16 -0400
commit3f94b79d4827411c92a95d8e3c76049a3784e3e0 (patch)
treeeac953b858b2d8f49e96e9853740e879663975b4 /src/pkg
parent1dba806d6a948976a2ecdb08a7c4839ff0a42766 (diff)
downloadgo-3f94b79d4827411c92a95d8e3c76049a3784e3e0.tar.gz
runtime: convert panic/recover to Go
created panic1.go just so diffs were available. After this CL is in, I'd like to move panic.go -> defer.go and panic1.go -> panic.go. LGTM=rsc R=rsc, khr CC=golang-codereviews https://codereview.appspot.com/133530045 Committer: Russ Cox <rsc@golang.org>
Diffstat (limited to 'src/pkg')
-rw-r--r--src/pkg/runtime/cgo/callbacks.c2
-rw-r--r--src/pkg/runtime/panic.c213
-rw-r--r--src/pkg/runtime/panic1.go183
-rw-r--r--src/pkg/runtime/runtime.h5
-rw-r--r--src/pkg/runtime/stack.c24
-rw-r--r--src/pkg/runtime/stack_test.go15
-rw-r--r--src/pkg/runtime/stubs.go7
7 files changed, 242 insertions, 207 deletions
diff --git a/src/pkg/runtime/cgo/callbacks.c b/src/pkg/runtime/cgo/callbacks.c
index 954a1cdcc..eb69255db 100644
--- a/src/pkg/runtime/cgo/callbacks.c
+++ b/src/pkg/runtime/cgo/callbacks.c
@@ -73,7 +73,7 @@ _cgo_panic_internal(byte *p)
s = runtime·gostring(p);
·cgoStringToEface(s, &err);
- runtime·panic(err);
+ runtime·gopanic(err);
}
#pragma cgo_export_static _cgo_panic
diff --git a/src/pkg/runtime/panic.c b/src/pkg/runtime/panic.c
index 152c9a3d5..e38ce740b 100644
--- a/src/pkg/runtime/panic.c
+++ b/src/pkg/runtime/panic.c
@@ -38,109 +38,11 @@ runtime·deferproc_m(void) {
runtime·memmove(d->args, (void*)argp, siz);
}
-// 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*);
-static void abortpanic(Panic*);
-static FuncVal abortpanicV = { (void(*)(void))abortpanic };
-
-// The implementation of the predeclared function panic.
-void
-runtime·panic(Eface e)
-{
- Defer *d, dabort;
- Panic p;
- uintptr pc, argp;
- void (*fn)(G*);
-
- runtime·memclr((byte*)&p, sizeof p);
- p.arg = e;
- p.link = g->panic;
- p.stackbase = g->stackbase;
- g->panic = &p;
-
- dabort.fn = &abortpanicV;
- dabort.siz = sizeof(&p);
- dabort.args[0] = &p;
- dabort.argp = NoArgs;
- dabort.special = true;
-
- for(;;) {
- d = g->defer;
- if(d == nil)
- break;
- // take defer off list in case of recursive panic
- g->defer = d->link;
- g->ispanic = true; // rock for runtime·newstack, where runtime·newstackcall ends up
- argp = d->argp;
- pc = d->pc;
-
- // The deferred function may cause another panic,
- // so newstackcall may not return. Set up a defer
- // to mark this panic aborted if that happens.
- dabort.link = g->defer;
- g->defer = &dabort;
- p.defer = d;
-
- runtime·newstackcall(d->fn, (byte*)d->args, d->siz);
-
- // Newstackcall did not panic. Remove dabort.
- if(g->defer != &dabort)
- runtime·throw("bad defer entry in panic");
- g->defer = dabort.link;
-
- runtime·freedefer(d);
- if(p.recovered) {
- g->panic = p.link;
- // Aborted panics are marked but remain on the g->panic list.
- // Recovery will unwind the stack frames containing their Panic structs.
- // Remove them from the list and free the associated defers.
- while(g->panic && g->panic->aborted) {
- runtime·freedefer(g->panic->defer);
- g->panic = g->panic->link;
- }
- if(g->panic == nil) // must be done with signal
- g->sig = 0;
- // Pass information about recovering frame to recovery.
- g->sigcode0 = (uintptr)argp;
- g->sigcode1 = (uintptr)pc;
- fn = recovery;
- runtime·mcall(&fn);
- 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); // should not return
- runtime·exit(1); // not reached
-}
-
-static void
-abortpanic(Panic *p)
-{
- p->aborted = true;
-}
-
// 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
+runtime·recovery_m(G *gp)
{
void *argp;
uintptr pc;
@@ -199,40 +101,8 @@ 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 NOSPLIT
-void
-runtime·recover(byte *argp, GoOutput retbase, ...)
-{
- Panic *p;
- Stktop *top;
- Eface *ret;
-
- // 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.
- ret = (Eface*)&retbase;
- top = (Stktop*)g->stackbase;
- 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;
- }
-}
-
void
-runtime·startpanic(void)
+runtime·startpanic_m(void)
{
if(runtime·mheap.cachealloc.size == 0) { // very early
runtime·printf("runtime: panic before malloc heap initialized\n");
@@ -273,28 +143,34 @@ runtime·startpanic(void)
}
void
-runtime·dopanic(int32 unused)
+runtime·dopanic_m(void)
{
+ G *gp;
+ uintptr sp, pc;
static bool didothers;
bool crash;
int32 t;
- if(g->sig != 0)
+ 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",
- g->sig, g->sigcode0, g->sigcode1, g->sigpc);
+ gp->sig, gp->sigcode0, gp->sigcode1, gp->sigpc);
if((t = runtime·gotraceback(&crash)) > 0){
- if(g != g->m->g0) {
+ if(gp != gp->m->g0) {
runtime·printf("\n");
- runtime·goroutineheader(g);
- runtime·traceback((uintptr)runtime·getcallerpc(&unused), (uintptr)runtime·getcallersp(&unused), 0, g);
+ runtime·goroutineheader(gp);
+ runtime·traceback(pc, sp, 0, gp);
} else if(t >= 2 || g->m->throwing > 0) {
runtime·printf("\nruntime stack:\n");
- runtime·traceback((uintptr)runtime·getcallerpc(&unused), (uintptr)runtime·getcallersp(&unused), 0, g);
+ runtime·traceback(pc, sp, 0, gp);
}
if(!didothers) {
didothers = true;
- runtime·tracebackothers(g);
+ runtime·tracebackothers(gp);
}
}
runtime·unlock(&paniclk);
@@ -341,58 +217,3 @@ runtime·canpanic(G *gp)
#endif
return true;
}
-
-void
-runtime·throw(int8 *s)
-{
- if(g->m->throwing == 0)
- g->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·gothrow(String s)
-{
- if(g->m->throwing == 0)
- g->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;
-
- // m->softfloat is set during software floating point,
- // which might cause a fault during a memory load.
- // It increments m->locks to avoid preemption.
- // If we're panicking, the software floating point frames
- // will be unwound, so decrement m->locks as they would.
- if(g->m->softfloat) {
- g->m->locks--;
- g->m->softfloat = 0;
- }
-
- if(g->m->mallocing) {
- runtime·printf("panic: %s\n", s);
- runtime·throw("panic during malloc");
- }
- if(g->m->gcing) {
- runtime·printf("panic: %s\n", s);
- runtime·throw("panic during gc");
- }
- if(g->m->locks) {
- runtime·printf("panic: %s\n", s);
- runtime·throw("panic holding locks");
- }
- runtime·newErrorCString(s, &err);
- runtime·panic(err);
-}
diff --git a/src/pkg/runtime/panic1.go b/src/pkg/runtime/panic1.go
new file mode 100644
index 000000000..6d939703d
--- /dev/null
+++ b/src/pkg/runtime/panic1.go
@@ -0,0 +1,183 @@
+// 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.
+
+package runtime
+
+import "unsafe"
+
+// Print all currently active panics. Used when crashing.
+func printpanics(p *_panic) {
+ if p.link != nil {
+ printpanics(p.link)
+ print("\t")
+ }
+ print("panic: ")
+ printany(p.arg)
+ if p.recovered {
+ print(" [recovered]")
+ }
+ print("\n")
+}
+
+// The implementation of the predeclared function panic.
+func gopanic(e interface{}) {
+ gp := getg()
+ if gp.m.curg != gp {
+ gothrow("panic on m stack")
+ }
+ var p _panic
+ var dabort _defer
+ p.arg = e
+ p.link = gp._panic
+ gp._panic = (*_panic)(noescape(unsafe.Pointer(&p)))
+
+ fn := abortpanic
+ dabort.fn = *(**funcval)(unsafe.Pointer(&fn))
+ dabort.siz = ptrSize
+ dabort.args[0] = noescape((unsafe.Pointer)(&p)) // TODO(khr): why do I need noescape here?
+ dabort.argp = _NoArgs
+ dabort.special = true
+
+ for {
+ d := gp._defer
+ if d == nil {
+ break
+ }
+ // take defer off list in case of recursive panic
+ gp._defer = d.link
+ gp.ispanic = true // rock for runtime·newstack, where runtime·newstackcall ends up
+ argp := unsafe.Pointer(d.argp) // must be pointer so it gets adjusted during stack copy
+ pc := d.pc
+
+ // The deferred function may cause another panic,
+ // so newstackcall may not return. Set up a defer
+ // to mark this panic aborted if that happens.
+ dabort.link = gp._defer
+ gp._defer = (*_defer)(noescape(unsafe.Pointer(&dabort)))
+ p._defer = d
+
+ newstackcall(d.fn, unsafe.Pointer(&d.args), uint32(d.siz))
+
+ // Newstackcall did not panic. Remove dabort.
+ if gp._defer != &dabort {
+ gothrow("bad defer entry in panic")
+ }
+ gp._defer = dabort.link
+
+ // trigger shrinkage to test stack copy. See stack_test.go:TestStackPanic
+ //GC()
+
+ freedefer(d)
+ if p.recovered {
+ gp._panic = p.link
+ // Aborted panics are marked but remain on the g.panic list.
+ // Remove them from the list and free the associated defers.
+ for gp._panic != nil && gp._panic.aborted {
+ freedefer(gp._panic._defer)
+ gp._panic = gp._panic.link
+ }
+ if gp._panic == nil { // must be done with signal
+ gp.sig = 0
+ }
+ // Pass information about recovering frame to recovery.
+ gp.sigcode0 = uintptr(argp)
+ gp.sigcode1 = pc
+ mcall(recovery_m)
+ gothrow("recovery failed") // mcall should not return
+ }
+ }
+
+ // ran out of deferred calls - old-school panic now
+ startpanic()
+ printpanics(gp._panic)
+ dopanic(0) // should not return
+ *(*int)(nil) = 0 // not reached
+}
+
+func abortpanic(p *_panic) {
+ p.aborted = true
+}
+
+// The implementation of the predeclared function recover.
+// Cannot split the stack because it needs to reliably
+// find the stack segment of its caller.
+//go:nosplit
+func gorecover(argp uintptr) interface{} {
+ // 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.
+ gp := getg()
+ top := (*stktop)(unsafe.Pointer(gp.stackbase))
+ p := gp._panic
+ if p != nil && !p.recovered && top._panic && argp == gp.stackbase-uintptr(top.argsize+gp.panicwrap) {
+ p.recovered = true
+ return p.arg
+ }
+ return nil
+}
+
+func startpanic() {
+ onM(startpanic_m)
+}
+
+func dopanic(unused int) {
+ gp := getg()
+ mp := acquirem()
+ mp.ptrarg[0] = unsafe.Pointer(gp)
+ mp.scalararg[0] = getcallerpc((unsafe.Pointer)(&unused))
+ mp.scalararg[1] = getcallersp((unsafe.Pointer)(&unused))
+ onM(dopanic_m) // should never return
+ *(*int)(nil) = 0
+}
+
+func throw(s *byte) {
+ gothrow(gostringnocopy(s))
+}
+
+func gothrow(s string) {
+ gp := getg()
+ if gp.m.throwing == 0 {
+ gp.m.throwing = 1
+ }
+ startpanic()
+ print("fatal error: ", s, "\n")
+ dopanic(0)
+ *(*int)(nil) = 0 // not reached
+}
+
+func panicstring(s *int8) {
+ // m.softfloat is set during software floating point,
+ // which might cause a fault during a memory load.
+ // It increments m.locks to avoid preemption.
+ // If we're panicking, the software floating point frames
+ // will be unwound, so decrement m.locks as they would.
+ gp := getg()
+ if gp.m.softfloat != 0 {
+ gp.m.locks--
+ gp.m.softfloat = 0
+ }
+
+ if gp.m.mallocing != 0 {
+ print("panic: ", s, "\n")
+ gothrow("panic during malloc")
+ }
+ if gp.m.gcing != 0 {
+ print("panic: ", s, "\n")
+ gothrow("panic during gc")
+ }
+ if gp.m.locks != 0 {
+ print("panic: ", s, "\n")
+ gothrow("panic holding locks")
+ }
+
+ var err interface{}
+ newErrorCString(unsafe.Pointer(s), &err)
+ gopanic(err)
+}
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index 7007957b6..b3d1a9422 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -656,15 +656,12 @@ struct Defer
struct Panic
{
Eface arg; // argument to panic
- uintptr stackbase; // g->stackbase in panic
Panic* link; // link to earlier panic
Defer* defer; // current executing defer
bool recovered; // whether this panic is over
bool aborted; // the panic was aborted
};
-typedef struct XXX XXX;
-
/*
* stack traces
*/
@@ -1020,7 +1017,7 @@ void runtime·printcomplex(Complex128);
*/
void runtime·newstackcall(FuncVal*, byte*, uint32);
void reflect·call(FuncVal*, byte*, uint32, uint32);
-void runtime·panic(Eface);
+void runtime·gopanic(Eface);
void runtime·panicindex(void);
void runtime·panicslice(void);
void runtime·panicdivide(void);
diff --git a/src/pkg/runtime/stack.c b/src/pkg/runtime/stack.c
index 3993a372e..f0861e408 100644
--- a/src/pkg/runtime/stack.c
+++ b/src/pkg/runtime/stack.c
@@ -714,8 +714,8 @@ adjustdefers(G *gp, AdjustInfo *adjinfo)
if(adjinfo->oldstk <= (byte*)d && (byte*)d < adjinfo->oldbase) {
// The Defer record is on the stack. Its fields will
// get adjusted appropriately.
- // This only happens for runtime.main now, but a compiler
- // optimization could do more of this.
+ // This only happens for runtime.main and runtime.gopanic now,
+ // but a compiler optimization could do more of this.
*dp = (Defer*)((byte*)d + adjinfo->delta);
continue;
}
@@ -753,6 +753,25 @@ adjustdefers(G *gp, AdjustInfo *adjinfo)
}
static void
+adjustpanics(G *gp, AdjustInfo *adjinfo)
+{
+ Panic *p;
+
+ // only the topmost panic is on the current stack
+ p = gp->panic;
+ if(p == nil)
+ return;
+ if(p->link != nil) {
+ // only the topmost panic can be on the current stack
+ // (because panic runs defers on a new stack)
+ if(adjinfo->oldstk <= (byte*)p->link && (byte*)p->link < adjinfo->oldbase)
+ runtime·throw("two panics on one stack");
+ }
+ if(adjinfo->oldstk <= (byte*)p && (byte*)p < adjinfo->oldbase)
+ gp->panic = (Panic*)((byte*)p + adjinfo->delta);
+}
+
+static void
adjustsudogs(G *gp, AdjustInfo *adjinfo)
{
SudoG *s;
@@ -811,6 +830,7 @@ copystack(G *gp, uintptr nframes, uintptr newsize)
// adjust other miscellaneous things that have pointers into stacks.
adjustctxt(gp, &adjinfo);
adjustdefers(gp, &adjinfo);
+ adjustpanics(gp, &adjinfo);
adjustsudogs(gp, &adjinfo);
// copy the stack (including Stktop) to the new location
diff --git a/src/pkg/runtime/stack_test.go b/src/pkg/runtime/stack_test.go
index a822d73db..7b9412af4 100644
--- a/src/pkg/runtime/stack_test.go
+++ b/src/pkg/runtime/stack_test.go
@@ -350,3 +350,18 @@ func TestStackAllOutput(t *testing.T) {
t.Errorf("Stack output should begin with \"goroutine \"")
}
}
+
+func TestStackPanic(t *testing.T) {
+ // Test that stack copying copies panics correctly. This is difficult
+ // to test because it is very unlikely that the stack will be copied
+ // in the middle of gopanic. But it can happen.
+ // To make this test effective, edit panic.go:gopanic and uncomment
+ // the GC() call just before freedefer(d).
+ defer func() {
+ if x := recover(); x == nil {
+ t.Errorf("recover failed")
+ }
+ }()
+ useStack(32)
+ panic("test panic")
+}
diff --git a/src/pkg/runtime/stubs.go b/src/pkg/runtime/stubs.go
index 8e67114f3..7ed4aaa55 100644
--- a/src/pkg/runtime/stubs.go
+++ b/src/pkg/runtime/stubs.go
@@ -83,6 +83,7 @@ func badonm() {
// Call using mcall.
func gosched_m(*g)
func park_m(*g)
+func recovery_m(*g)
// More C functions that run on the M stack.
// Call using onM.
@@ -100,6 +101,8 @@ func setmaxthreads_m()
func ready_m()
func deferproc_m()
func goexit_m()
+func startpanic_m()
+func dopanic_m()
// memclr clears n bytes starting at ptr.
// in memclr_*.s
@@ -133,10 +136,6 @@ func memeq(a, b unsafe.Pointer, size uintptr) bool
var nohashcode uintptr
var noequalcode uintptr
-// Go version of runtime.throw.
-// in panic.c
-func gothrow(s string)
-
// noescape hides a pointer from escape analysis. noescape is
// the identity function but escape analysis doesn't think the
// output depends on the input. noescape is inlined and currently