From: Russ Cox Date: Wed, 24 Mar 2010 00:01:17 +0000 (-0700) Subject: runtime: add Callers X-Git-Tag: weekly.2010-03-30~81 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=2b7d147f1a916f26df6ee15ed0d54c30c7bede43;p=gostls13.git runtime: add Callers cut copies of traceback from 6 to 1. R=r CC=golang-dev https://golang.org/cl/703041 --- diff --git a/src/pkg/runtime/386/traceback.c b/src/pkg/runtime/386/traceback.c deleted file mode 100644 index 6b6a7aa1c4..0000000000 --- a/src/pkg/runtime/386/traceback.c +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2009 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" - -// TODO(rsc): Move this into portable code, with calls to a -// machine-dependent isclosure() function. - -void -traceback(byte *pc0, byte *sp, G *g) -{ - Stktop *stk; - uintptr pc, tracepc; - int32 i, n; - Func *f; - byte *p; - - pc = (uintptr)pc0; - - // If the PC is zero, it's likely a nil function call. - // Start in the caller's frame. - if(pc == 0) { - pc = *(uintptr*)sp; - sp += sizeof(uintptr); - } - - stk = (Stktop*)g->stackbase; - for(n=0; n<100; n++) { - if(pc == (uint64)·lessstack) { - // printf("--\n"); - // pop to earlier stack block - pc = (uintptr)stk->gobuf.pc; - sp = stk->gobuf.sp; - stk = (Stktop*)stk->stackbase; - } - p = (byte*)pc; - tracepc = pc; - if(n > 0 && pc != (uint64)goexit) - tracepc--; // get to CALL instruction - f = findfunc(tracepc); - if(f == nil) { - // dangerous, but poke around to see if it is a closure - // ADDL $xxx, SP; RET - if(pc > 0x1000 && p[0] == 0x81 && p[1] == 0xc4 && p[6] == 0xc3) { - sp += *(uint32*)(p+2) + 8; - pc = *(uintptr*)(sp - 8); - if(pc <= 0x1000) - return; - continue; - } - printf("%p unknown pc\n", pc); - return; - } - if(f->frame < sizeof(uintptr)) // assembly funcs say 0 but lie - sp += sizeof(uintptr); - else - sp += f->frame; - - // print this frame - // main+0xf /home/rsc/go/src/runtime/x.go:23 - // main(0x1, 0x2, 0x3) - printf("%S", f->name); - if(pc > f->entry) - printf("+%p", (uintptr)(pc - f->entry)); - printf(" %S:%d\n", f->src, funcline(f, tracepc)); - printf("\t%S(", f->name); - for(i = 0; i < f->args; i++) { - if(i != 0) - prints(", "); - ·printhex(((uint32*)sp)[i]); - if(i >= 4) { - prints(", ..."); - break; - } - } - prints(")\n"); - - pc = *(uintptr*)(sp-sizeof(uintptr)); - if(pc <= 0x1000) - return; - } - prints("...\n"); -} - -// func caller(n int) (pc uintptr, file string, line int, ok bool) -void -·Caller(int32 n, uintptr retpc, String retfile, int32 retline, bool retbool) -{ - uintptr pc; - byte *sp; - byte *p; - Stktop *stk; - Func *f; - - // our caller's pc, sp. - sp = (byte*)&n; - pc = *((uintptr*)sp - 1); - if((f = findfunc(pc)) == nil) { - error: - retpc = 0; - retline = 0; - retfile = emptystring; - retbool = false; - FLUSH(&retpc); - FLUSH(&retfile); - FLUSH(&retline); - FLUSH(&retbool); - return; - } - - // now unwind n levels - stk = (Stktop*)g->stackbase; - while(n-- > 0) { - while(pc == (uintptr)·lessstack) { - pc = (uintptr)stk->gobuf.pc; - sp = stk->gobuf.sp; - stk = (Stktop*)stk->stackbase; - } - - if(f->frame < sizeof(uintptr)) // assembly functions lie - sp += sizeof(uintptr); - else - sp += f->frame; - - loop: - pc = *((uintptr*)sp - 1); - if(pc <= 0x1000 || (f = findfunc(pc)) == nil) { - // dangerous, but let's try this. - // see if it is a closure. - p = (byte*)pc; - // ADDL $xxx, SP; RET - if(pc > 0x1000 && p[0] == 0x81 && p[1] == 0xc4 && p[6] == 0xc3) { - sp += *(uint32*)(p+2) + sizeof(uintptr); - goto loop; - } - goto error; - } - } - - retpc = pc; - retfile = f->src; - retline = funcline(f, pc-1); - retbool = true; - FLUSH(&retpc); - FLUSH(&retfile); - FLUSH(&retline); - FLUSH(&retbool); -} diff --git a/src/pkg/runtime/Makefile b/src/pkg/runtime/Makefile index 71ab072e01..8327709736 100644 --- a/src/pkg/runtime/Makefile +++ b/src/pkg/runtime/Makefile @@ -149,3 +149,11 @@ version.go: mkversion # for discovering offsets inside structs when debugging runtime.acid.$(GOARCH): runtime.h proc.c $(QUOTED_GOBIN)/$(CC) $(CFLAGS) -a proc.c >$@ + +# 386 traceback is really amd64 traceback +ifeq ($(GOARCH),386) + +traceback.$O: amd64/traceback.c + $(QUOTED_GOBIN)/$(CC) $(CFLAGS) $< + +endif diff --git a/src/pkg/runtime/amd64/traceback.c b/src/pkg/runtime/amd64/traceback.c index 8fe23f32ee..37c06d092d 100644 --- a/src/pkg/runtime/amd64/traceback.c +++ b/src/pkg/runtime/amd64/traceback.c @@ -3,144 +3,115 @@ // license that can be found in the LICENSE file. #include "runtime.h" +#include "malloc.h" -void -traceback(byte *pc0, byte *sp, G *g) +// This code is also used for the 386 tracebacks. +// Use uintptr for an appropriate word-sized integer. + +// Generic traceback. Handles runtime stack prints (pcbuf == nil) +// as well as the runtime.Callers function (pcbuf != nil). +// A little clunky to merge the two but avoids duplicating +// the code and all its subtlety. +static int32 +gentraceback(byte *pc0, byte *sp, G *g, int32 skip, uintptr *pcbuf, int32 m) { + byte *p; + int32 i, n, iter; + uintptr pc, tracepc; Stktop *stk; - uint64 pc, tracepc; - int32 i, n; Func *f; - byte *p; - - pc = (uint64)pc0; + + pc = (uintptr)pc0; // If the PC is zero, it's likely a nil function call. // Start in the caller's frame. if(pc == 0) { - pc = *(uint64*)sp; - sp += 8; + pc = *(uintptr*)sp; + sp += sizeof(uintptr); } - + + n = 0; stk = (Stktop*)g->stackbase; - for(n=0; n<100; n++) { - if(pc == (uint64)·lessstack) { - // pop to earlier stack block - // printf("-- stack jump %p => %p\n", sp, stk->gobuf.sp); + for(iter = 0; iter < 100 && n < m; iter++) { // iter avoids looping forever + if(pc == (uintptr)·lessstack) { + // Hit top of stack segment. Unwind to next segment. pc = (uintptr)stk->gobuf.pc; sp = stk->gobuf.sp; stk = (Stktop*)stk->stackbase; + continue; } - p = (byte*)pc; - tracepc = pc; // used for line number, function - if(n > 0 && pc != (uint64)goexit) - tracepc--; // get to CALL instruction - f = findfunc(tracepc); - if(f == nil) { - // dangerous, but poke around to see if it is a closure - // ADDQ $xxx, SP; RET - if(p[0] == 0x48 && p[1] == 0x81 && p[2] == 0xc4 && p[7] == 0xc3) { - sp += *(uint32*)(p+3) + 8; - pc = *(uint64*)(sp - 8); - if(pc <= 0x1000) - return; + + if(pc <= 0x1000 || (f = findfunc(pc)) == nil) { + // Dangerous, but worthwhile: see if this is a closure: + // ADDQ $wwxxyyzz, SP; RET + // [48] 81 c4 zz yy xx ww c3 + // The 0x48 byte is only on amd64. + p = (byte*)pc; + if(mheap.min < p && p+8 < mheap.max && // pointer in allocated memory + (sizeof(uintptr) != 8 || *p++ == 0x48) && // skip 0x48 byte on amd64 + p[0] == 0x81 && p[1] == 0xc4 && p[6] == 0xc3) { + sp += *(uint32*)(p+2); + pc = *(uintptr*)sp; + sp += sizeof(uintptr); continue; } - printf("%p unknown pc\n", pc); - return; + // Unknown pc; stop. + break; } - if(f->frame < sizeof(uintptr)) // assembly funcs say 0 but lie - sp += sizeof(uintptr); - else - sp += f->frame; - // print this frame - // main+0xf /home/rsc/go/src/runtime/x.go:23 - // main(0x1, 0x2, 0x3) - printf("%S", f->name); - if(pc > f->entry) - printf("+%p", (uintptr)(pc - f->entry)); - printf(" %S:%d\n", f->src, funcline(f, tracepc)); - printf("\t%S(", f->name); - for(i = 0; i < f->args; i++) { - if(i != 0) - prints(", "); - ·printhex(((uint32*)sp)[i]); - if(i >= 4) { - prints(", ..."); - break; + // Found an actual function worth reporting. + if(skip > 0) + skip--; + else if(pcbuf != nil) + pcbuf[n++] = pc; + else { + // Print during crash. + // main+0xf /home/rsc/go/src/runtime/x.go:23 + // main(0x1, 0x2, 0x3) + printf("%S", f->name); + if(pc > f->entry) + printf("+%p", (uintptr)(pc - f->entry)); + tracepc = pc; // back up to CALL instruction for funcline. + if(n > 0 && pc > f->entry) + tracepc--; + printf(" %S:%d\n", f->src, funcline(f, tracepc)); + printf("\t%S(", f->name); + for(i = 0; i < f->args; i++) { + if(i != 0) + prints(", "); + ·printhex(((uintptr*)sp)[i]); + if(i >= 4) { + prints(", ..."); + break; + } } + prints(")\n"); + n++; } - prints(")\n"); - - pc = *(uintptr*)(sp-sizeof(uintptr)); - if(pc <= 0x1000) - return; + + if(f->frame < sizeof(uintptr)) // assembly functions lie + sp += sizeof(uintptr); + else + sp += f->frame; + pc = *((uintptr*)sp - 1); } - prints("...\n"); + return n; } -// func caller(n int) (pc uint64, file string, line int, ok bool) void -·Caller(int32 n, uint64 retpc, String retfile, int32 retline, bool retbool) +traceback(byte *pc0, byte *sp, G *g) { - uint64 pc; - byte *sp; - byte *p; - Stktop *stk; - Func *f; - - // our caller's pc, sp. - sp = (byte*)&n; - pc = *(uint64*)(sp-8); - if((f = findfunc(pc)) == nil) { - error: - retpc = 0; - retline = 0; - retfile = emptystring; - retbool = false; - FLUSH(&retpc); - FLUSH(&retfile); - FLUSH(&retline); - FLUSH(&retbool); - return; - } - - // now unwind n levels - stk = (Stktop*)g->stackbase; - while(n-- > 0) { - while(pc == (uintptr)·lessstack) { - pc = (uintptr)stk->gobuf.pc; - sp = stk->gobuf.sp; - stk = (Stktop*)stk->stackbase; - } + gentraceback(pc0, sp, g, 0, nil, 100); +} - if(f->frame < sizeof(uintptr)) // assembly functions lie - sp += sizeof(uintptr); - else - sp += f->frame; +int32 +callers(int32 skip, uintptr *pcbuf, int32 m) +{ + byte *pc, *sp; - loop: - pc = *((uintptr*)sp - 1); - if(pc <= 0x1000 || (f = findfunc(pc)) == nil) { - // dangerous, but let's try this. - // see if it is a closure. - p = (byte*)pc; - // ADDQ $xxx, SP; RET - if(pc > 0x1000 && p[0] == 0x48 && p[1] == 0x81 && p[2] == 0xc4 && p[7] == 0xc3) { - sp += *(uint32*)(p+3) + 8; - goto loop; - } - goto error; - } - } + // our caller's pc, sp. + sp = (byte*)&skip; + pc = *(byte**)(sp-sizeof(uintptr)); - retpc = pc; - retfile = f->src; - retline = funcline(f, pc-1); - retbool = true; - FLUSH(&retpc); - FLUSH(&retfile); - FLUSH(&retline); - FLUSH(&retbool); + return gentraceback(pc, sp, g, skip, pcbuf, m); } diff --git a/src/pkg/runtime/arm/traceback.c b/src/pkg/runtime/arm/traceback.c index 5c68c15c26..edddafe865 100644 --- a/src/pkg/runtime/arm/traceback.c +++ b/src/pkg/runtime/arm/traceback.c @@ -10,138 +10,11 @@ void traceback(byte *pc0, byte *sp, G *g) { -// Stktop *stk; -// uintptr pc; -// int32 i, n; -// Func *f; -// byte *p; - -// pc = (uintptr)pc0; - -// // If the PC is zero, it's likely a nil function call. -// // Start in the caller's frame. -// if(pc == 0) { -// pc = *(uintptr*)sp; -// sp += sizeof(uintptr); -// } - -// stk = (Stktop*)g->stackbase; -// for(n=0; n<100; n++) { -// while(pc == (uintptr)retfromnewstack) { -// // pop to earlier stack block -// sp = stk->oldsp; -// stk = (Stktop*)stk->oldbase; -// pc = *(uintptr*)(sp+sizeof(uintptr)); -// sp += 2*sizeof(uintptr); // two irrelevant calls on stack: morestack plus its call -// } -// f = findfunc(pc); -// if(f == nil) { -// // dangerous, but poke around to see if it is a closure -// p = (byte*)pc; -// // ADDL $xxx, SP; RET -// if(p[0] == 0x81 && p[1] == 0xc4 && p[6] == 0xc3) { -// sp += *(uint32*)(p+2) + 8; -// pc = *(uintptr*)(sp - 8); -// if(pc <= 0x1000) -// return; -// continue; -// } -// printf("%p unknown pc\n", pc); -// return; -// } -// if(f->frame < sizeof(uintptr)) // assembly funcs say 0 but lie -// sp += sizeof(uintptr); -// else -// sp += f->frame; - -// // print this frame -// // main+0xf /home/rsc/go/src/runtime/x.go:23 -// // main(0x1, 0x2, 0x3) -// printf("%S", f->name); -// if(pc > f->entry) -// printf("+%p", (uintptr)(pc - f->entry)); -// printf(" %S:%d\n", f->src, funcline(f, pc-1)); // -1 to get to CALL instr. -// printf("\t%S(", f->name); -// for(i = 0; i < f->args; i++) { -// if(i != 0) -// prints(", "); -// ·printhex(((uint32*)sp)[i]); -// if(i >= 4) { -// prints(", ..."); -// break; -// } -// } -// prints(")\n"); - -// pc = *(uintptr*)(sp-sizeof(uintptr)); -// if(pc <= 0x1000) -// return; -// } -// prints("...\n"); } // func caller(n int) (pc uintptr, file string, line int, ok bool) -void -·Caller(int32 n, uintptr retpc, String retfile, int32 retline, bool retbool) +int32 +callers(int32 skip, uintptr *pcbuf, int32 m) { -// uintptr pc; -// byte *sp; -// byte *p; -// Stktop *stk; -// Func *f; - -// // our caller's pc, sp. -// sp = (byte*)&n; -// pc = *((uintptr*)sp - 1); -// if((f = findfunc(pc)) == nil) { -// error: -// retpc = 0; -// retline = 0; -// retfile = emptystring; -// retbool = false; -// FLUSH(&retpc); -// FLUSH(&retfile); -// FLUSH(&retline); -// FLUSH(&retbool); -// return; -// } - -// // now unwind n levels -// stk = (Stktop*)g->stackbase; -// while(n-- > 0) { -// while(pc == (uintptr)retfromnewstack) { -// sp = stk->oldsp; -// stk = (Stktop*)stk->oldbase; -// pc = *((uintptr*)sp + 1); -// sp += 2*sizeof(uintptr); -// } - -// if(f->frame < sizeof(uintptr)) // assembly functions lie -// sp += sizeof(uintptr); -// else -// sp += f->frame; - -// loop: -// pc = *((uintptr*)sp - 1); -// if(pc <= 0x1000 || (f = findfunc(pc)) == nil) { -// // dangerous, but let's try this. -// // see if it is a closure. -// p = (byte*)pc; -// // ADDL $xxx, SP; RET -// if(p[0] == 0x81 && p[1] == 0xc4 && p[6] == 0xc3) { -// sp += *(uint32*)(p+2) + sizeof(uintptr); -// goto loop; -// } -// goto error; -// } -// } - -// retpc = pc; -// retfile = f->src; -// retline = funcline(f, pc-1); -// retbool = true; -// FLUSH(&retpc); -// FLUSH(&retfile); -// FLUSH(&retline); -// FLUSH(&retbool); + return 0; } diff --git a/src/pkg/runtime/extern.go b/src/pkg/runtime/extern.go index b4d903c30c..2ee20cd35a 100644 --- a/src/pkg/runtime/extern.go +++ b/src/pkg/runtime/extern.go @@ -10,8 +10,6 @@ */ package runtime -// These functions are implemented in the base runtime library, ../../runtime/. - // Gosched yields the processor, allowing other goroutines to run. It does not // suspend the current goroutine, so execution resumes automatically. func Gosched() diff --git a/src/pkg/runtime/runtime.c b/src/pkg/runtime/runtime.c index ed1bdcab8a..f4882d8bdf 100644 --- a/src/pkg/runtime/runtime.c +++ b/src/pkg/runtime/runtime.c @@ -481,3 +481,29 @@ nanotime(void) gettime(&sec, &usec); return sec*1000000000 + (int64)usec*1000; } + +void +·Caller(int32 skip, uintptr retpc, String retfile, int32 retline, bool retbool) +{ + Func *f; + + if(callers(skip, &retpc, 1) == 0 || (f = findfunc(retpc-1)) == nil) { + retfile = emptystring; + retline = 0; + retbool = false; + } else { + retfile = f->src; + retline = funcline(f, retpc-1); + retbool = true; + } + FLUSH(&retfile); + FLUSH(&retline); + FLUSH(&retbool); +} + +void +·Callers(int32 skip, Slice pc, int32 retn) +{ + retn = callers(skip, (uintptr*)pc.array, pc.len); + FLUSH(&retn); +} diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h index 194503ec8a..622f680388 100644 --- a/src/pkg/runtime/runtime.h +++ b/src/pkg/runtime/runtime.h @@ -407,6 +407,7 @@ void newproc1(byte*, byte*, int32, int32); void siginit(void); bool sigsend(int32 sig); void gettime(int64*, int32*); +int32 callers(int32, uintptr*, int32); int64 nanotime(void); #pragma varargck argpos printf 1