diff options
author | Russ Cox <rsc@golang.org> | 2010-05-21 14:40:21 -0700 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2010-05-21 14:40:21 -0700 |
commit | f58ba18a08b0cf1a2f116d26cadb00cd4f710f89 (patch) | |
tree | be6003273032ecdf86a5bf4d4f25db54874ba591 | |
parent | 48673d2745c5a778875ba3a71d1cd26658f3a7fb (diff) | |
download | go-f58ba18a08b0cf1a2f116d26cadb00cd4f710f89.tar.gz go-f58ba18a08b0cf1a2f116d26cadb00cd4f710f89.zip |
runtime: correct tracebacks for nascent goroutines, even closures
Fixes #780.
R=r
CC=golang-dev
https://golang.org/cl/1221042
-rw-r--r-- | src/pkg/runtime/amd64/traceback.c | 87 | ||||
-rw-r--r-- | src/pkg/runtime/arm/traceback.c | 7 |
2 files changed, 92 insertions, 2 deletions
diff --git a/src/pkg/runtime/amd64/traceback.c b/src/pkg/runtime/amd64/traceback.c index 840e61bfd8..20e9200e53 100644 --- a/src/pkg/runtime/amd64/traceback.c +++ b/src/pkg/runtime/amd64/traceback.c @@ -5,6 +5,8 @@ #include "runtime.h" #include "malloc.h" +static uintptr isclosureentry(uintptr); + // This code is also used for the 386 tracebacks. // Use uintptr for an appropriate word-sized integer. @@ -16,7 +18,7 @@ static int32 gentraceback(byte *pc0, byte *sp, G *g, int32 skip, uintptr *pcbuf, int32 m) { byte *p; - int32 i, n, iter; + int32 i, n, iter, nascent; uintptr pc, tracepc; Stktop *stk; Func *f; @@ -30,6 +32,14 @@ gentraceback(byte *pc0, byte *sp, G *g, int32 skip, uintptr *pcbuf, int32 m) sp += sizeof(uintptr); } + nascent = 0; + if(pc0 == g->sched.pc && sp == g->sched.sp && pc0 == (byte*)goexit) { + // Hasn't started yet. g->sched is set up for goexit + // but goroutine will start at g->entry. + nascent = 1; + pc = (uintptr)g->entry; + } + n = 0; stk = (Stktop*)g->stackbase; for(iter = 0; iter < 100 && n < m; iter++) { // iter avoids looping forever @@ -55,6 +65,10 @@ gentraceback(byte *pc0, byte *sp, G *g, int32 skip, uintptr *pcbuf, int32 m) sp += sizeof(uintptr); continue; } + + if(nascent && (pc = isclosureentry(pc)) != 0) + continue; + // Unknown pc; stop. break; } @@ -89,6 +103,13 @@ gentraceback(byte *pc0, byte *sp, G *g, int32 skip, uintptr *pcbuf, int32 m) n++; } + if(nascent) { + pc = (uintptr)g->sched.pc; + sp = g->sched.sp; + nascent = 0; + continue; + } + if(f->frame < sizeof(uintptr)) // assembly functions lie sp += sizeof(uintptr); else @@ -115,3 +136,67 @@ callers(int32 skip, uintptr *pcbuf, int32 m) return gentraceback(pc, sp, g, skip, pcbuf, m); } + +static uintptr +isclosureentry(uintptr pc) +{ + byte *p; + int32 i, siz; + + p = (byte*)pc; + if(p < mheap.min || p+32 > mheap.max) + return 0; + + // SUBQ $siz, SP + if((sizeof(uintptr) == 8 && *p++ != 0x48) || *p++ != 0x81 || *p++ != 0xec) + return 0; + siz = *(uint32*)p; + p += 4; + + // MOVQ $q, SI + if((sizeof(uintptr) == 8 && *p++ != 0x48) || *p++ != 0xbe) + return 0; + p += sizeof(uintptr); + + // MOVQ SP, DI + if((sizeof(uintptr) == 8 && *p++ != 0x48) || *p++ != 0x89 || *p++ != 0xe7) + return 0; + + // CLD on 32-bit + if(sizeof(uintptr) == 4 && *p++ != 0xfc) + return 0; + + if(siz <= 4*sizeof(uintptr)) { + // MOVSQ... + for(i=0; i<siz; i+=sizeof(uintptr)) + if((sizeof(uintptr) == 8 && *p++ != 0x48) || *p++ != 0xa5) + return 0; + } else { + // MOVQ $(siz/8), CX [32-bit immediate siz/8] + if((sizeof(uintptr) == 8 && *p++ != 0x48) || *p++ != 0xc7 || *p++ != 0xc1) + return 0; + p += 4; + + // REP MOVSQ + if(*p++ != 0xf3 || (sizeof(uintptr) == 8 && *p++ != 0x48) || *p++ != 0xa5) + return 0; + } + + // CALL fn + if(*p == 0xe8) { + p++; + return (uintptr)p+4 + *(int32*)p; + } + + // MOVQ $fn, CX; CALL *CX + if(sizeof(uintptr) != 8 || *p++ != 0x48 || *p++ != 0xb9) + return 0; + + pc = *(uintptr*)p; + p += 8; + + if(*p++ != 0xff || *p != 0xd1) + return 0; + + return pc; +} diff --git a/src/pkg/runtime/arm/traceback.c b/src/pkg/runtime/arm/traceback.c index 5d32980f6f..0131f21d63 100644 --- a/src/pkg/runtime/arm/traceback.c +++ b/src/pkg/runtime/arm/traceback.c @@ -7,7 +7,6 @@ static int32 gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr *pcbuf, int32 m) { - byte *p; int32 i, n, iter; uintptr pc, lr, tracepc; Stktop *stk; @@ -15,6 +14,12 @@ gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr *pcbuf, i pc = (uintptr)pc0; lr = (uintptr)lr0; + + // If the PC is goexit, it hasn't started yet. + if(pc == (uintptr)goexit) { + pc = (uintptr)g->entry; + lr = (uintptr)goexit; + } // If the PC is zero, it's likely a nil function call. // Start in the caller's frame. |