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
diff --git a/src/pkg/runtime/arch_386.h b/src/pkg/runtime/arch_386.h
index 62ed11b..7e74d8f 100644
--- a/src/pkg/runtime/arch_386.h
+++ b/src/pkg/runtime/arch_386.h
@@ -6,5 +6,6 @@
thechar = '8',
BigEndian = 0,
CacheLineSize = 64,
- appendCrossover = 16
+ appendCrossover = 16,
+ PCQuantum = 1
};
diff --git a/src/pkg/runtime/arch_amd64.h b/src/pkg/runtime/arch_amd64.h
index a5e43ca..2114411 100644
--- a/src/pkg/runtime/arch_amd64.h
+++ b/src/pkg/runtime/arch_amd64.h
@@ -6,5 +6,6 @@
thechar = '6',
BigEndian = 0,
CacheLineSize = 64,
- appendCrossover = 16
+ appendCrossover = 16,
+ PCQuantum = 1
};
diff --git a/src/pkg/runtime/arch_arm.h b/src/pkg/runtime/arch_arm.h
index 27c70c1..cab7989 100644
--- a/src/pkg/runtime/arch_arm.h
+++ b/src/pkg/runtime/arch_arm.h
@@ -6,5 +6,6 @@
thechar = '5',
BigEndian = 0,
CacheLineSize = 32,
- appendCrossover = 8
+ appendCrossover = 8,
+ PCQuantum = 4
};
diff --git a/src/pkg/runtime/panic.c b/src/pkg/runtime/panic.c
index 8d7d261..120f770 100644
--- a/src/pkg/runtime/panic.c
+++ b/src/pkg/runtime/panic.c
@@ -156,9 +156,14 @@
// is called again and again until there are no more deferred functions.
// Cannot split the stack because we reuse the caller's frame to
// 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
void
-runtime·deferreturn(uintptr arg0)
+runtime·deferreturn(uintptr arg0, ...)
{
Defer *d;
byte *argp;
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 44741a6..fffd04b 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -2496,3 +2496,12 @@
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;
+}
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index 3940c30..ce451b0 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -690,6 +690,7 @@
void runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G* gp);
void runtime·tracebackothers(G*);
bool runtime·haszeroargs(uintptr pc);
+bool runtime·topofstack(Func*);
/*
* external data
diff --git a/src/pkg/runtime/softfloat_arm.c b/src/pkg/runtime/softfloat_arm.c
index 9a54406..56a73fc 100644
--- a/src/pkg/runtime/softfloat_arm.c
+++ b/src/pkg/runtime/softfloat_arm.c
@@ -576,9 +576,12 @@
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
uint32*
-runtime·_sfloat2(uint32 *lr, uint32 r0)
+runtime·_sfloat2(uint32 *lr, uint32 r0, ...)
{
uint32 skip;
diff --git a/src/pkg/runtime/symtab.c b/src/pkg/runtime/symtab.c
index a96c0ea..7911f11 100644
--- a/src/pkg/runtime/symtab.c
+++ b/src/pkg/runtime/symtab.c
@@ -10,6 +10,7 @@
#include "os_GOOS.h"
#include "arch_GOARCH.h"
#include "malloc.h"
+#include "funcdata.h"
typedef struct Ftab Ftab;
struct Ftab
@@ -81,26 +82,17 @@
// Return associated data value for targetpc in func f.
// (Source file is f->src.)
static int32
-pcvalue(Func *f, int32 off, uintptr targetpc)
+pcvalue(Func *f, int32 off, uintptr targetpc, bool strict)
{
byte *p;
uintptr pc;
- int32 value, vdelta, pcshift;
+ int32 value, vdelta;
uint32 uvdelta, pcdelta;
enum {
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.
// Each pair states the given value is in effect up to pc.
// The value deltas are signed, zig-zag encoded.
@@ -126,7 +118,7 @@
else
uvdelta >>= 1;
vdelta = (int32)uvdelta;
- pcdelta = readvarint(&p) << pcshift;
+ pcdelta = readvarint(&p) * PCQuantum;
value += vdelta;
pc += pcdelta;
if(debug)
@@ -137,23 +129,43 @@
// If there was a table, it should have covered all program counters.
// 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",
*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");
return -1;
}
static String unknown = { (uint8*)"?", 1 };
-int32
-runtime·funcline(Func *f, uintptr targetpc, String *file)
+static int32
+funcline(Func *f, uintptr targetpc, String *file, bool strict)
{
int32 line;
int32 fileno;
*file = unknown;
- fileno = pcvalue(f, f->pcfile, targetpc);
- line = pcvalue(f, f->pcln, targetpc);
+ fileno = pcvalue(f, f->pcfile, targetpc, strict);
+ line = pcvalue(f, f->pcln, targetpc, strict);
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);
return 0;
@@ -163,11 +175,17 @@
}
int32
+runtime·funcline(Func *f, uintptr targetpc, String *file)
+{
+ return funcline(f, targetpc, file, true);
+}
+
+int32
runtime·funcspdelta(Func *f, uintptr targetpc)
{
int32 x;
- x = pcvalue(f, f->pcsp, targetpc);
+ x = pcvalue(f, f->pcsp, targetpc, true);
if(x&(sizeof(void*)-1))
runtime·printf("invalid spdelta %d %d\n", f->pcsp, x);
return x;
@@ -178,19 +196,23 @@
{
if(table < 0 || table >= f->npcdata)
return -1;
- return pcvalue(f, (&f->nfuncdata)[1+table], targetpc);
+ return pcvalue(f, (&f->nfuncdata)[1+table], targetpc, true);
}
int32
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
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);
}
diff --git a/src/pkg/runtime/traceback_arm.c b/src/pkg/runtime/traceback_arm.c
index e5a475f..6cd924d 100644
--- a/src/pkg/runtime/traceback_arm.c
+++ b/src/pkg/runtime/traceback_arm.c
@@ -6,30 +6,21 @@
#include "arch_GOARCH.h"
#include "malloc.h"
-void runtime·deferproc(void);
-void runtime·newproc(void);
-void runtime·morestack(void);
void runtime·sigpanic(void);
-void _div(void);
-void _mod(void);
-void _divu(void);
-void _modu(void);
static String unknown = { (uint8*)"?", 1 };
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)
{
- int32 i, n, nprint, skip0, line;
+ int32 i, n, nprint, line;
uintptr x, tracepc;
bool waspanic, printing;
- Func *f, *f2;
+ Func *f, *flr;
Stkframe frame;
Stktop *stk;
String file;
- skip0 = skip;
-
nprint = 0;
runtime·memclr((byte*)&frame, sizeof frame);
frame.pc = pc0;
@@ -44,6 +35,16 @@
frame.pc = frame.lr;
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;
stk = (Stktop*)gp->stackbase;
@@ -64,41 +65,57 @@
if(printing && runtime·showframe(nil, gp))
runtime·printf("----- stack segment boundary -----\n");
stk = (Stktop*)stk->stackbase;
+
+ f = runtime·findfunc(frame.pc);
+ if(f == nil) {
+ runtime·printf("runtime: unknown pc %p after stack split\n", frame.pc);
+ runtime·throw("unknown pc");
+ }
+ frame.fn = f;
continue;
}
-
- if(frame.pc <= 0x1000 || (frame.fn = f = runtime·findfunc(frame.pc)) == nil) {
- if(callback != nil) {
- runtime·printf("runtime: unknown pc %p at frame %d\n", frame.pc, skip0-skip+n);
- runtime·throw("invalid stack");
- }
- break;
- }
+ f = frame.fn;
// Found an actual function.
// Derive frame pointer and link register.
- if(frame.lr == 0)
- frame.lr = *(uintptr*)frame.sp;
if(frame.fp == 0)
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.
- frame.argp = (byte*)frame.fp + sizeof(uintptr);
- frame.arglen = 0;
- if(f->args != ArgsSizeUnknown)
- frame.arglen = f->args;
- else if(runtime·haszeroargs(f->entry))
- frame.arglen = 0;
- else if(frame.lr == (uintptr)runtime·lessstack)
- frame.arglen = stk->argsize;
- else if(f->entry == (uintptr)runtime·deferproc || f->entry == (uintptr)runtime·newproc)
- frame.arglen = 3*sizeof(uintptr) + *(int32*)frame.argp;
- else if((f2 = runtime·findfunc(frame.lr)) != nil && f2->frame >= sizeof(uintptr))
- frame.arglen = f2->frame; // conservative overestimate
- else {
- runtime·printf("runtime: unknown argument frame size for %S\n", *f->name);
- if(!printing)
- runtime·throw("invalid stack");
+ // 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);
+ if(f->args != ArgsSizeUnknown)
+ frame.arglen = f->args;
+ else if(flr == nil)
+ frame.arglen = 0;
+ else if(frame.lr == (uintptr)runtime·lessstack)
+ frame.arglen = stk->argsize;
+ else if((i = runtime·funcarglen(flr, frame.lr)) >= 0)
+ frame.arglen = i;
+ else {
+ runtime·printf("runtime: unknown argument frame size for %S called from %p [%S]\n",
+ *f->name, frame.lr, flr ? *flr->name : unknown);
+ if(!printing)
+ runtime·throw("invalid stack");
+ frame.arglen = 0;
+ }
}
// Derive location and size of local variables.
@@ -165,11 +182,12 @@
waspanic = f->entry == (uintptr)runtime·sigpanic;
// 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;
// Unwind to next frame.
frame.pc = frame.lr;
+ frame.fn = flr;
frame.lr = 0;
frame.sp = frame.fp;
frame.fp = 0;
diff --git a/src/pkg/runtime/traceback_x86.c b/src/pkg/runtime/traceback_x86.c
index 49e1c04..7c2cffb 100644
--- a/src/pkg/runtime/traceback_x86.c
+++ b/src/pkg/runtime/traceback_x86.c
@@ -48,6 +48,16 @@
frame.pc = *(uintptr*)frame.sp;
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;
stk = (Stktop*)gp->stackbase;
@@ -69,16 +79,16 @@
if(printing && runtime·showframe(nil, gp))
runtime·printf("----- stack segment boundary -----\n");
stk = (Stktop*)stk->stackbase;
+
+ f = runtime·findfunc(frame.pc);
+ if(f == nil) {
+ runtime·printf("runtime: unknown pc %p after stack split\n", frame.pc);
+ runtime·throw("unknown pc");
+ }
+ frame.fn = f;
continue;
}
f = frame.fn;
- if(f == nil && (frame.pc <= 0x1000 || (frame.fn = f = runtime·findfunc(frame.pc)) == nil)) {
- if(callback != nil) {
- runtime·printf("unknown pc %p\n", frame.pc);
- runtime·throw("unknown pc");
- }
- break;
- }
// Found an actual function.
// Derive frame pointer and link register.
@@ -86,28 +96,42 @@
frame.fp = frame.sp + runtime·funcspdelta(f, frame.pc);
frame.fp += sizeof(uintptr); // caller PC
}
- if(frame.lr == 0)
- frame.lr = ((uintptr*)frame.fp)[-1];
- flr = runtime·findfunc(frame.lr);
+ if(runtime·topofstack(f)) {
+ frame.lr = 0;
+ flr = nil;
+ } else {
+ if(frame.lr == 0)
+ frame.lr = ((uintptr*)frame.fp)[-1];
+ 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.
- frame.argp = (byte*)frame.fp;
- if(flr != nil && (i = runtime·funcarglen(flr, frame.lr)) >= 0)
- frame.arglen = i;
- else if(f->args != ArgsSizeUnknown)
- frame.arglen = f->args;
- else if(runtime·haszeroargs(f->entry))
- frame.arglen = 0;
- else if(frame.lr == (uintptr)runtime·lessstack)
- frame.arglen = stk->argsize;
- else if(f->entry == (uintptr)runtime·deferproc || f->entry == (uintptr)runtime·newproc)
- frame.arglen = 2*sizeof(uintptr) + *(int32*)frame.argp;
- else if(flr != nil && flr->frame >= sizeof(uintptr))
- frame.arglen = flr->frame; // conservative overestimate
- else {
- runtime·printf("runtime: unknown argument frame size for %S called from %p [%S]\n", *f->name, frame.lr, flr ? *flr->name : unknown);
- if(!printing)
- runtime·throw("invalid stack");
+ // 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;
+ if(f->args != ArgsSizeUnknown)
+ frame.arglen = f->args;
+ else if(flr == nil)
+ frame.arglen = 0;
+ else if(frame.lr == (uintptr)runtime·lessstack)
+ frame.arglen = stk->argsize;
+ else if((i = runtime·funcarglen(flr, frame.lr)) >= 0)
+ frame.arglen = i;
+ else {
+ runtime·printf("runtime: unknown argument frame size for %S called from %p [%S]\n",
+ *f->name, frame.lr, flr ? *flr->name : unknown);
+ if(!printing)
+ runtime·throw("invalid stack");
+ frame.arglen = 0;
+ }
}
// Derive location and size of local variables.
@@ -174,7 +198,7 @@
waspanic = f->entry == (uintptr)runtime·sigpanic;
// 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;
// Unwind to next frame.