Commit a8374859 authored by Russ Cox's avatar Russ Cox

runtime: use new frame argument size information

With this CL, I believe the runtime always knows
the frame size during the gc walk. There is no fallback
to "assume entire stack frame of caller" anymore.

R=golang-dev, khr, cshapiro, dvyukov
CC=golang-dev
https://golang.org/cl/11374044
parent 7b7dac5e
...@@ -6,5 +6,6 @@ enum { ...@@ -6,5 +6,6 @@ enum {
thechar = '8', thechar = '8',
BigEndian = 0, BigEndian = 0,
CacheLineSize = 64, CacheLineSize = 64,
appendCrossover = 16 appendCrossover = 16,
PCQuantum = 1
}; };
...@@ -6,5 +6,6 @@ enum { ...@@ -6,5 +6,6 @@ enum {
thechar = '6', thechar = '6',
BigEndian = 0, BigEndian = 0,
CacheLineSize = 64, CacheLineSize = 64,
appendCrossover = 16 appendCrossover = 16,
PCQuantum = 1
}; };
...@@ -6,5 +6,6 @@ enum { ...@@ -6,5 +6,6 @@ enum {
thechar = '5', thechar = '5',
BigEndian = 0, BigEndian = 0,
CacheLineSize = 32, CacheLineSize = 32,
appendCrossover = 8 appendCrossover = 8,
PCQuantum = 4
}; };
...@@ -156,9 +156,14 @@ runtime·deferproc(int32 siz, FuncVal *fn, ...) ...@@ -156,9 +156,14 @@ runtime·deferproc(int32 siz, FuncVal *fn, ...)
// is called again and again until there are no more deferred functions. // is called again and again until there are no more deferred functions.
// Cannot split the stack because we reuse the caller's frame to // Cannot split the stack because we reuse the caller's frame to
// call the deferred function. // call the deferred function.
//
// The ... in the prototype keeps the compiler from declaring
// an argument frame size. deferreturn is a very special function,
// and if the runtime ever asks for its frame size, that means
// the traceback routines are probably broken.
#pragma textflag 7 #pragma textflag 7
void void
runtime·deferreturn(uintptr arg0) runtime·deferreturn(uintptr arg0, ...)
{ {
Defer *d; Defer *d;
byte *argp; byte *argp;
......
...@@ -2496,3 +2496,12 @@ runtime·haszeroargs(uintptr pc) ...@@ -2496,3 +2496,12 @@ runtime·haszeroargs(uintptr pc)
pc == (uintptr)_rt0_go; pc == (uintptr)_rt0_go;
} }
// Does f mark the top of a goroutine stack?
bool
runtime·topofstack(Func *f)
{
return f->entry == (uintptr)runtime·goexit ||
f->entry == (uintptr)runtime·mstart ||
f->entry == (uintptr)runtime·mcall ||
f->entry == (uintptr)_rt0_go;
}
...@@ -690,6 +690,7 @@ int32 runtime·gentraceback(uintptr, uintptr, uintptr, G*, int32, uintptr*, int3 ...@@ -690,6 +690,7 @@ int32 runtime·gentraceback(uintptr, uintptr, uintptr, G*, int32, uintptr*, int3
void runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G* gp); void runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G* gp);
void runtime·tracebackothers(G*); void runtime·tracebackothers(G*);
bool runtime·haszeroargs(uintptr pc); bool runtime·haszeroargs(uintptr pc);
bool runtime·topofstack(Func*);
/* /*
* external data * external data
......
...@@ -576,9 +576,12 @@ done: ...@@ -576,9 +576,12 @@ done:
return 0; return 0;
} }
// The ... here is because there are actually 16 registers
// being passed (r0, r1, and so on) amd we are too lazy
// to list them all.
#pragma textflag 7 #pragma textflag 7
uint32* uint32*
runtime·_sfloat2(uint32 *lr, uint32 r0) runtime·_sfloat2(uint32 *lr, uint32 r0, ...)
{ {
uint32 skip; uint32 skip;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "os_GOOS.h" #include "os_GOOS.h"
#include "arch_GOARCH.h" #include "arch_GOARCH.h"
#include "malloc.h" #include "malloc.h"
#include "funcdata.h"
typedef struct Ftab Ftab; typedef struct Ftab Ftab;
struct Ftab struct Ftab
...@@ -81,26 +82,17 @@ funcdata(Func *f, int32 i) ...@@ -81,26 +82,17 @@ funcdata(Func *f, int32 i)
// Return associated data value for targetpc in func f. // Return associated data value for targetpc in func f.
// (Source file is f->src.) // (Source file is f->src.)
static int32 static int32
pcvalue(Func *f, int32 off, uintptr targetpc) pcvalue(Func *f, int32 off, uintptr targetpc, bool strict)
{ {
byte *p; byte *p;
uintptr pc; uintptr pc;
int32 value, vdelta, pcshift; int32 value, vdelta;
uint32 uvdelta, pcdelta; uint32 uvdelta, pcdelta;
enum { enum {
debug = 0 debug = 0
}; };
switch(thechar) {
case '5':
pcshift = 2;
break;
default: // 6, 8
pcshift = 0;
break;
}
// The table is a delta-encoded sequence of (value, pc) pairs. // The table is a delta-encoded sequence of (value, pc) pairs.
// Each pair states the given value is in effect up to pc. // Each pair states the given value is in effect up to pc.
// The value deltas are signed, zig-zag encoded. // The value deltas are signed, zig-zag encoded.
...@@ -126,7 +118,7 @@ pcvalue(Func *f, int32 off, uintptr targetpc) ...@@ -126,7 +118,7 @@ pcvalue(Func *f, int32 off, uintptr targetpc)
else else
uvdelta >>= 1; uvdelta >>= 1;
vdelta = (int32)uvdelta; vdelta = (int32)uvdelta;
pcdelta = readvarint(&p) << pcshift; pcdelta = readvarint(&p) * PCQuantum;
value += vdelta; value += vdelta;
pc += pcdelta; pc += pcdelta;
if(debug) if(debug)
...@@ -137,23 +129,43 @@ pcvalue(Func *f, int32 off, uintptr targetpc) ...@@ -137,23 +129,43 @@ pcvalue(Func *f, int32 off, uintptr targetpc)
// If there was a table, it should have covered all program counters. // If there was a table, it should have covered all program counters.
// If not, something is wrong. // If not, something is wrong.
if(runtime·panicking || !strict)
return -1;
runtime·printf("runtime: invalid pc-encoded table f=%S pc=%p targetpc=%p tab=%p\n", runtime·printf("runtime: invalid pc-encoded table f=%S pc=%p targetpc=%p tab=%p\n",
*f->name, pc, targetpc, p); *f->name, pc, targetpc, p);
p = (byte*)f + off;
pc = f->entry;
value = -1;
for(;;) {
uvdelta = readvarint(&p);
if(uvdelta == 0 && pc != f->entry)
break;
if(uvdelta&1)
uvdelta = ~(uvdelta>>1);
else
uvdelta >>= 1;
vdelta = (int32)uvdelta;
pcdelta = readvarint(&p) * PCQuantum;
value += vdelta;
pc += pcdelta;
runtime·printf("\tvalue=%d until pc=%p\n", value, pc);
}
runtime·throw("invalid runtime symbol table"); runtime·throw("invalid runtime symbol table");
return -1; return -1;
} }
static String unknown = { (uint8*)"?", 1 }; static String unknown = { (uint8*)"?", 1 };
int32 static int32
runtime·funcline(Func *f, uintptr targetpc, String *file) funcline(Func *f, uintptr targetpc, String *file, bool strict)
{ {
int32 line; int32 line;
int32 fileno; int32 fileno;
*file = unknown; *file = unknown;
fileno = pcvalue(f, f->pcfile, targetpc); fileno = pcvalue(f, f->pcfile, targetpc, strict);
line = pcvalue(f, f->pcln, targetpc); line = pcvalue(f, f->pcln, targetpc, strict);
if(fileno == -1 || line == -1 || fileno >= nfiletab) { if(fileno == -1 || line == -1 || fileno >= nfiletab) {
// runtime·printf("looking for %p in %S got file=%d line=%d\n", targetpc, *f->name, fileno, line); // runtime·printf("looking for %p in %S got file=%d line=%d\n", targetpc, *f->name, fileno, line);
return 0; return 0;
...@@ -162,12 +174,18 @@ runtime·funcline(Func *f, uintptr targetpc, String *file) ...@@ -162,12 +174,18 @@ runtime·funcline(Func *f, uintptr targetpc, String *file)
return line; return line;
} }
int32
runtime·funcline(Func *f, uintptr targetpc, String *file)
{
return funcline(f, targetpc, file, true);
}
int32 int32
runtime·funcspdelta(Func *f, uintptr targetpc) runtime·funcspdelta(Func *f, uintptr targetpc)
{ {
int32 x; int32 x;
x = pcvalue(f, f->pcsp, targetpc); x = pcvalue(f, f->pcsp, targetpc, true);
if(x&(sizeof(void*)-1)) if(x&(sizeof(void*)-1))
runtime·printf("invalid spdelta %d %d\n", f->pcsp, x); runtime·printf("invalid spdelta %d %d\n", f->pcsp, x);
return x; return x;
...@@ -178,19 +196,23 @@ pcdatavalue(Func *f, int32 table, uintptr targetpc) ...@@ -178,19 +196,23 @@ pcdatavalue(Func *f, int32 table, uintptr targetpc)
{ {
if(table < 0 || table >= f->npcdata) if(table < 0 || table >= f->npcdata)
return -1; return -1;
return pcvalue(f, (&f->nfuncdata)[1+table], targetpc); return pcvalue(f, (&f->nfuncdata)[1+table], targetpc, true);
} }
int32 int32
runtime·funcarglen(Func *f, uintptr targetpc) runtime·funcarglen(Func *f, uintptr targetpc)
{ {
return pcdatavalue(f, 0, targetpc); if(targetpc == f->entry)
return 0;
return pcdatavalue(f, PCDATA_ArgSize, targetpc-PCQuantum);
} }
void void
runtime·funcline_go(Func *f, uintptr targetpc, String retfile, intgo retline) runtime·funcline_go(Func *f, uintptr targetpc, String retfile, intgo retline)
{ {
retline = runtime·funcline(f, targetpc, &retfile); // Pass strict=false here, because anyone can call this function,
// and they might just be wrong about targetpc belonging to f.
retline = funcline(f, targetpc, &retfile, false);
FLUSH(&retline); FLUSH(&retline);
} }
......
...@@ -6,30 +6,21 @@ ...@@ -6,30 +6,21 @@
#include "arch_GOARCH.h" #include "arch_GOARCH.h"
#include "malloc.h" #include "malloc.h"
void runtime·deferproc(void);
void runtime·newproc(void);
void runtime·morestack(void);
void runtime·sigpanic(void); void runtime·sigpanic(void);
void _div(void);
void _mod(void);
void _divu(void);
void _modu(void);
static String unknown = { (uint8*)"?", 1 }; static String unknown = { (uint8*)"?", 1 };
int32 int32
runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max, void (*callback)(Stkframe*, void*), void *v, bool printall) runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max, void (*callback)(Stkframe*, void*), void *v, bool printall)
{ {
int32 i, n, nprint, skip0, line; int32 i, n, nprint, line;
uintptr x, tracepc; uintptr x, tracepc;
bool waspanic, printing; bool waspanic, printing;
Func *f, *f2; Func *f, *flr;
Stkframe frame; Stkframe frame;
Stktop *stk; Stktop *stk;
String file; String file;
skip0 = skip;
nprint = 0; nprint = 0;
runtime·memclr((byte*)&frame, sizeof frame); runtime·memclr((byte*)&frame, sizeof frame);
frame.pc = pc0; frame.pc = pc0;
...@@ -45,6 +36,16 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, ...@@ -45,6 +36,16 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
frame.lr = 0; frame.lr = 0;
} }
f = runtime·findfunc(frame.pc);
if(f == nil) {
if(callback != nil) {
runtime·printf("runtime: unknown pc %p\n", frame.pc);
runtime·throw("unknown pc");
}
return 0;
}
frame.fn = f;
n = 0; n = 0;
stk = (Stktop*)gp->stackbase; stk = (Stktop*)gp->stackbase;
while(n < max) { while(n < max) {
...@@ -64,41 +65,57 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, ...@@ -64,41 +65,57 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
if(printing && runtime·showframe(nil, gp)) if(printing && runtime·showframe(nil, gp))
runtime·printf("----- stack segment boundary -----\n"); runtime·printf("----- stack segment boundary -----\n");
stk = (Stktop*)stk->stackbase; stk = (Stktop*)stk->stackbase;
continue;
}
if(frame.pc <= 0x1000 || (frame.fn = f = runtime·findfunc(frame.pc)) == nil) { f = runtime·findfunc(frame.pc);
if(callback != nil) { if(f == nil) {
runtime·printf("runtime: unknown pc %p at frame %d\n", frame.pc, skip0-skip+n); runtime·printf("runtime: unknown pc %p after stack split\n", frame.pc);
runtime·throw("invalid stack"); runtime·throw("unknown pc");
} }
break; frame.fn = f;
continue;
} }
f = frame.fn;
// Found an actual function. // Found an actual function.
// Derive frame pointer and link register. // Derive frame pointer and link register.
if(frame.lr == 0)
frame.lr = *(uintptr*)frame.sp;
if(frame.fp == 0) if(frame.fp == 0)
frame.fp = frame.sp + runtime·funcspdelta(f, frame.pc); frame.fp = frame.sp + runtime·funcspdelta(f, frame.pc);
if(runtime·topofstack(f)) {
frame.lr = 0;
flr = nil;
} else {
if(frame.lr == 0)
frame.lr = *(uintptr*)frame.sp;
flr = runtime·findfunc(frame.lr);
if(flr == nil) {
runtime·printf("runtime: unexpected return pc for %S called from %p", *f->name, frame.lr);
runtime·throw("unknown caller pc");
}
}
// Derive size of arguments. // Derive size of arguments.
// Most functions have a fixed-size argument block,
// so we can use metadata about the function f.
// Not all, though: there are some variadic functions
// in package runtime, and for those we use call-specific
// metadata recorded by f's caller.
if(callback != nil || printing) {
frame.argp = (byte*)frame.fp + sizeof(uintptr); frame.argp = (byte*)frame.fp + sizeof(uintptr);
frame.arglen = 0;
if(f->args != ArgsSizeUnknown) if(f->args != ArgsSizeUnknown)
frame.arglen = f->args; frame.arglen = f->args;
else if(runtime·haszeroargs(f->entry)) else if(flr == nil)
frame.arglen = 0; frame.arglen = 0;
else if(frame.lr == (uintptr)runtime·lessstack) else if(frame.lr == (uintptr)runtime·lessstack)
frame.arglen = stk->argsize; frame.arglen = stk->argsize;
else if(f->entry == (uintptr)runtime·deferproc || f->entry == (uintptr)runtime·newproc) else if((i = runtime·funcarglen(flr, frame.lr)) >= 0)
frame.arglen = 3*sizeof(uintptr) + *(int32*)frame.argp; frame.arglen = i;
else if((f2 = runtime·findfunc(frame.lr)) != nil && f2->frame >= sizeof(uintptr))
frame.arglen = f2->frame; // conservative overestimate
else { else {
runtime·printf("runtime: unknown argument frame size for %S\n", *f->name); runtime·printf("runtime: unknown argument frame size for %S called from %p [%S]\n",
*f->name, frame.lr, flr ? *flr->name : unknown);
if(!printing) if(!printing)
runtime·throw("invalid stack"); runtime·throw("invalid stack");
frame.arglen = 0;
}
} }
// Derive location and size of local variables. // Derive location and size of local variables.
...@@ -165,11 +182,12 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, ...@@ -165,11 +182,12 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
waspanic = f->entry == (uintptr)runtime·sigpanic; waspanic = f->entry == (uintptr)runtime·sigpanic;
// Do not unwind past the bottom of the stack. // Do not unwind past the bottom of the stack.
if(f->entry == (uintptr)runtime·goexit || f->entry == (uintptr)runtime·mstart || f->entry == (uintptr)runtime·mcall || f->entry == (uintptr)_rt0_go) if(flr == nil)
break; break;
// Unwind to next frame. // Unwind to next frame.
frame.pc = frame.lr; frame.pc = frame.lr;
frame.fn = flr;
frame.lr = 0; frame.lr = 0;
frame.sp = frame.fp; frame.sp = frame.fp;
frame.fp = 0; frame.fp = 0;
......
...@@ -49,6 +49,16 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, ...@@ -49,6 +49,16 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
frame.sp += sizeof(uintptr); frame.sp += sizeof(uintptr);
} }
f = runtime·findfunc(frame.pc);
if(f == nil) {
if(callback != nil) {
runtime·printf("runtime: unknown pc %p\n", frame.pc);
runtime·throw("unknown pc");
}
return 0;
}
frame.fn = f;
n = 0; n = 0;
stk = (Stktop*)gp->stackbase; stk = (Stktop*)gp->stackbase;
while(n < max) { while(n < max) {
...@@ -69,16 +79,16 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, ...@@ -69,16 +79,16 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
if(printing && runtime·showframe(nil, gp)) if(printing && runtime·showframe(nil, gp))
runtime·printf("----- stack segment boundary -----\n"); runtime·printf("----- stack segment boundary -----\n");
stk = (Stktop*)stk->stackbase; stk = (Stktop*)stk->stackbase;
continue;
} f = runtime·findfunc(frame.pc);
f = frame.fn; if(f == nil) {
if(f == nil && (frame.pc <= 0x1000 || (frame.fn = f = runtime·findfunc(frame.pc)) == nil)) { runtime·printf("runtime: unknown pc %p after stack split\n", frame.pc);
if(callback != nil) {
runtime·printf("unknown pc %p\n", frame.pc);
runtime·throw("unknown pc"); runtime·throw("unknown pc");
} }
break; frame.fn = f;
continue;
} }
f = frame.fn;
// Found an actual function. // Found an actual function.
// Derive frame pointer and link register. // Derive frame pointer and link register.
...@@ -86,28 +96,42 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, ...@@ -86,28 +96,42 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
frame.fp = frame.sp + runtime·funcspdelta(f, frame.pc); frame.fp = frame.sp + runtime·funcspdelta(f, frame.pc);
frame.fp += sizeof(uintptr); // caller PC frame.fp += sizeof(uintptr); // caller PC
} }
if(runtime·topofstack(f)) {
frame.lr = 0;
flr = nil;
} else {
if(frame.lr == 0) if(frame.lr == 0)
frame.lr = ((uintptr*)frame.fp)[-1]; frame.lr = ((uintptr*)frame.fp)[-1];
flr = runtime·findfunc(frame.lr); flr = runtime·findfunc(frame.lr);
if(flr == nil) {
runtime·printf("runtime: unexpected return pc for %S called from %p", *f->name, frame.lr);
runtime·throw("unknown caller pc");
}
}
// Derive size of arguments. // Derive size of arguments.
// Most functions have a fixed-size argument block,
// so we can use metadata about the function f.
// Not all, though: there are some variadic functions
// in package runtime, and for those we use call-specific
// metadata recorded by f's caller.
if(callback != nil || printing) {
frame.argp = (byte*)frame.fp; frame.argp = (byte*)frame.fp;
if(flr != nil && (i = runtime·funcarglen(flr, frame.lr)) >= 0) if(f->args != ArgsSizeUnknown)
frame.arglen = i;
else if(f->args != ArgsSizeUnknown)
frame.arglen = f->args; frame.arglen = f->args;
else if(runtime·haszeroargs(f->entry)) else if(flr == nil)
frame.arglen = 0; frame.arglen = 0;
else if(frame.lr == (uintptr)runtime·lessstack) else if(frame.lr == (uintptr)runtime·lessstack)
frame.arglen = stk->argsize; frame.arglen = stk->argsize;
else if(f->entry == (uintptr)runtime·deferproc || f->entry == (uintptr)runtime·newproc) else if((i = runtime·funcarglen(flr, frame.lr)) >= 0)
frame.arglen = 2*sizeof(uintptr) + *(int32*)frame.argp; frame.arglen = i;
else if(flr != nil && flr->frame >= sizeof(uintptr))
frame.arglen = flr->frame; // conservative overestimate
else { else {
runtime·printf("runtime: unknown argument frame size for %S called from %p [%S]\n", *f->name, frame.lr, flr ? *flr->name : unknown); runtime·printf("runtime: unknown argument frame size for %S called from %p [%S]\n",
*f->name, frame.lr, flr ? *flr->name : unknown);
if(!printing) if(!printing)
runtime·throw("invalid stack"); runtime·throw("invalid stack");
frame.arglen = 0;
}
} }
// Derive location and size of local variables. // Derive location and size of local variables.
...@@ -174,7 +198,7 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, ...@@ -174,7 +198,7 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
waspanic = f->entry == (uintptr)runtime·sigpanic; waspanic = f->entry == (uintptr)runtime·sigpanic;
// Do not unwind past the bottom of the stack. // Do not unwind past the bottom of the stack.
if(f->entry == (uintptr)runtime·goexit || f->entry == (uintptr)runtime·mstart || f->entry == (uintptr)runtime·mcall || f->entry == (uintptr)_rt0_go) if(flr == nil)
break; break;
// Unwind to next frame. // Unwind to next frame.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment