]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: correct tracebacks for nascent goroutines, even closures
authorRuss Cox <rsc@golang.org>
Fri, 21 May 2010 21:40:21 +0000 (14:40 -0700)
committerRuss Cox <rsc@golang.org>
Fri, 21 May 2010 21:40:21 +0000 (14:40 -0700)
Fixes #780.

R=r
CC=golang-dev
https://golang.org/cl/1221042

src/pkg/runtime/amd64/traceback.c
src/pkg/runtime/arm/traceback.c

index 840e61bfd8b00f9453501187db2875b6b70a0b9d..20e9200e53ec6529c188d8cf774516eb37136be1 100644 (file)
@@ -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;
+}
index 5d32980f6f5be5684308b6872b0f9408c2a65a2e..0131f21d638db107a6313d6d6575450927b975fc 100644 (file)
@@ -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.