From f58ba18a08b0cf1a2f116d26cadb00cd4f710f89 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 21 May 2010 14:40:21 -0700 Subject: [PATCH] runtime: correct tracebacks for nascent goroutines, even closures Fixes #780. R=r CC=golang-dev https://golang.org/cl/1221042 --- src/pkg/runtime/amd64/traceback.c | 87 ++++++++++++++++++++++++++++++- 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 840e61bfd8b..20e9200e53e 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; ientry; + lr = (uintptr)goexit; + } // If the PC is zero, it's likely a nil function call. // Start in the caller's frame.