Commit e844f53a authored by Russ Cox's avatar Russ Cox

runtime: stop scanning stack frames/args conservatively

The goal here is to commit fully to having precise information
about stack frames. If we need information we don't have,
crash instead of assuming we should scan conservatively.

Since the stack copying assumes fully precise information,
any crashes during garbage collection that are introduced by
this CL are crashes that could have happened during stack
copying instead. Those are harder to find because stacks are
copied much less often than the garbage collector is invoked.

In service of that goal, remove ARGSIZE macros from
asm_*.s, change switchtoM to have no arguments
(it doesn't have any live arguments), and add
args and locals information to some frames that
can call back into Go.

LGTM=khr
R=khr, rlh
CC=golang-codereviews
https://golang.org/cl/137540043
parent a7c6d891
...@@ -102,9 +102,7 @@ ok: ...@@ -102,9 +102,7 @@ ok:
// create a new goroutine to start program // create a new goroutine to start program
PUSHL $runtime·main·f(SB) // entry PUSHL $runtime·main·f(SB) // entry
PUSHL $0 // arg size PUSHL $0 // arg size
ARGSIZE(8)
CALL runtime·newproc(SB) CALL runtime·newproc(SB)
ARGSIZE(-1)
POPL AX POPL AX
POPL AX POPL AX
...@@ -206,7 +204,7 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-4 ...@@ -206,7 +204,7 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-4
// lives at the bottom of the G stack from the one that lives // lives at the bottom of the G stack from the one that lives
// at the top of the M stack because the one at the top of // at the top of the M stack because the one at the top of
// the M stack terminates the stack walk (see topofstack()). // the M stack terminates the stack walk (see topofstack()).
TEXT runtime·switchtoM(SB), NOSPLIT, $0-4 TEXT runtime·switchtoM(SB), NOSPLIT, $0-0
RET RET
// func onM_signalok(fn func()) // func onM_signalok(fn func())
...@@ -263,7 +261,6 @@ oncurg: ...@@ -263,7 +261,6 @@ oncurg:
MOVL BX, SP MOVL BX, SP
// call target function // call target function
ARGSIZE(0)
MOVL DI, DX MOVL DI, DX
MOVL 0(DI), DI MOVL 0(DI), DI
CALL DI CALL DI
...@@ -656,6 +653,7 @@ TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8 ...@@ -656,6 +653,7 @@ TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8
RET RET
TEXT runtime·asmcgocall_errno(SB),NOSPLIT,$0-12 TEXT runtime·asmcgocall_errno(SB),NOSPLIT,$0-12
GO_ARGS
MOVL fn+0(FP), AX MOVL fn+0(FP), AX
MOVL arg+4(FP), BX MOVL arg+4(FP), BX
CALL asmcgocall<>(SB) CALL asmcgocall<>(SB)
...@@ -716,6 +714,9 @@ TEXT runtime·cgocallback(SB),NOSPLIT,$12-12 ...@@ -716,6 +714,9 @@ TEXT runtime·cgocallback(SB),NOSPLIT,$12-12
// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize) // cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize)
// See cgocall.c for more details. // See cgocall.c for more details.
TEXT runtime·cgocallback_gofunc(SB),NOSPLIT,$12-12 TEXT runtime·cgocallback_gofunc(SB),NOSPLIT,$12-12
GO_ARGS
NO_LOCAL_POINTERS
// If g is nil, Go did not create the current thread. // If g is nil, Go did not create the current thread.
// Call needm to obtain one for temporary use. // Call needm to obtain one for temporary use.
// In this case, we're running on the thread stack, so there's // In this case, we're running on the thread stack, so there's
......
...@@ -98,9 +98,7 @@ ok: ...@@ -98,9 +98,7 @@ ok:
MOVQ $runtime·main·f(SB), BP // entry MOVQ $runtime·main·f(SB), BP // entry
PUSHQ BP PUSHQ BP
PUSHQ $0 // arg size PUSHQ $0 // arg size
ARGSIZE(16)
CALL runtime·newproc(SB) CALL runtime·newproc(SB)
ARGSIZE(-1)
POPQ AX POPQ AX
POPQ AX POPQ AX
...@@ -183,7 +181,6 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-8 ...@@ -183,7 +181,6 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-8
MOVQ SI, g(CX) // g = m->g0 MOVQ SI, g(CX) // g = m->g0
MOVQ (g_sched+gobuf_sp)(SI), SP // sp = m->g0->sched.sp MOVQ (g_sched+gobuf_sp)(SI), SP // sp = m->g0->sched.sp
PUSHQ AX PUSHQ AX
ARGSIZE(8)
MOVQ DI, DX MOVQ DI, DX
MOVQ 0(DI), DI MOVQ 0(DI), DI
CALL DI CALL DI
...@@ -197,7 +194,7 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-8 ...@@ -197,7 +194,7 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-8
// lives at the bottom of the G stack from the one that lives // lives at the bottom of the G stack from the one that lives
// at the top of the M stack because the one at the top of // at the top of the M stack because the one at the top of
// the M stack terminates the stack walk (see topofstack()). // the M stack terminates the stack walk (see topofstack()).
TEXT runtime·switchtoM(SB), NOSPLIT, $0-8 TEXT runtime·switchtoM(SB), NOSPLIT, $0-0
RET RET
// func onM_signalok(fn func()) // func onM_signalok(fn func())
...@@ -255,7 +252,6 @@ oncurg: ...@@ -255,7 +252,6 @@ oncurg:
MOVQ BX, SP MOVQ BX, SP
// call target function // call target function
ARGSIZE(0)
MOVQ DI, DX MOVQ DI, DX
MOVQ 0(DI), DI MOVQ 0(DI), DI
CALL DI CALL DI
...@@ -634,6 +630,7 @@ TEXT runtime·asmcgocall(SB),NOSPLIT,$0-16 ...@@ -634,6 +630,7 @@ TEXT runtime·asmcgocall(SB),NOSPLIT,$0-16
RET RET
TEXT runtime·asmcgocall_errno(SB),NOSPLIT,$0-20 TEXT runtime·asmcgocall_errno(SB),NOSPLIT,$0-20
GO_ARGS
MOVQ fn+0(FP), AX MOVQ fn+0(FP), AX
MOVQ arg+8(FP), BX MOVQ arg+8(FP), BX
CALL asmcgocall<>(SB) CALL asmcgocall<>(SB)
...@@ -703,6 +700,9 @@ TEXT runtime·cgocallback(SB),NOSPLIT,$24-24 ...@@ -703,6 +700,9 @@ TEXT runtime·cgocallback(SB),NOSPLIT,$24-24
// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize) // cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize)
// See cgocall.c for more details. // See cgocall.c for more details.
TEXT runtime·cgocallback_gofunc(SB),NOSPLIT,$8-24 TEXT runtime·cgocallback_gofunc(SB),NOSPLIT,$8-24
GO_ARGS
NO_LOCAL_POINTERS
// If g is nil, Go did not create the current thread. // If g is nil, Go did not create the current thread.
// Call needm to obtain one m for temporary use. // Call needm to obtain one m for temporary use.
// In this case, we're running on the thread stack, so there's // In this case, we're running on the thread stack, so there's
......
...@@ -75,9 +75,7 @@ ok: ...@@ -75,9 +75,7 @@ ok:
MOVL $runtime·main·f(SB), AX // entry MOVL $runtime·main·f(SB), AX // entry
MOVL $0, 0(SP) MOVL $0, 0(SP)
MOVL AX, 4(SP) MOVL AX, 4(SP)
ARGSIZE(8)
CALL runtime·newproc(SB) CALL runtime·newproc(SB)
ARGSIZE(-1)
// start this M // start this M
CALL runtime·mstart(SB) CALL runtime·mstart(SB)
...@@ -158,7 +156,6 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-4 ...@@ -158,7 +156,6 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-4
MOVL SI, g(CX) // g = m->g0 MOVL SI, g(CX) // g = m->g0
MOVL (g_sched+gobuf_sp)(SI), SP // sp = m->g0->sched.sp MOVL (g_sched+gobuf_sp)(SI), SP // sp = m->g0->sched.sp
PUSHQ AX PUSHQ AX
ARGSIZE(8)
MOVL DI, DX MOVL DI, DX
MOVL 0(DI), DI MOVL 0(DI), DI
CALL DI CALL DI
...@@ -172,7 +169,7 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-4 ...@@ -172,7 +169,7 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-4
// lives at the bottom of the G stack from the one that lives // lives at the bottom of the G stack from the one that lives
// at the top of the M stack because the one at the top of // at the top of the M stack because the one at the top of
// the M stack terminates the stack walk (see topofstack()). // the M stack terminates the stack walk (see topofstack()).
TEXT runtime·switchtoM(SB), NOSPLIT, $0-4 TEXT runtime·switchtoM(SB), NOSPLIT, $0-0
RET RET
// func onM_signalok(fn func()) // func onM_signalok(fn func())
...@@ -225,7 +222,6 @@ oncurg: ...@@ -225,7 +222,6 @@ oncurg:
MOVL (g_sched+gobuf_sp)(DX), SP MOVL (g_sched+gobuf_sp)(DX), SP
// call target function // call target function
ARGSIZE(0)
MOVL DI, DX MOVL DI, DX
MOVL 0(DI), DI MOVL 0(DI), DI
CALL DI CALL DI
......
...@@ -77,9 +77,7 @@ nocgo: ...@@ -77,9 +77,7 @@ nocgo:
MOVW.W R0, -4(R13) MOVW.W R0, -4(R13)
MOVW $0, R0 MOVW $0, R0
MOVW.W R0, -4(R13) // push $0 as guard MOVW.W R0, -4(R13) // push $0 as guard
ARGSIZE(12)
BL runtime·newproc(SB) BL runtime·newproc(SB)
ARGSIZE(-1)
MOVW $12(R13), R13 // pop args and LR MOVW $12(R13), R13 // pop args and LR
// start this M // start this M
...@@ -197,7 +195,7 @@ TEXT runtime·mcall(SB),NOSPLIT,$-4-4 ...@@ -197,7 +195,7 @@ TEXT runtime·mcall(SB),NOSPLIT,$-4-4
// lives at the bottom of the G stack from the one that lives // lives at the bottom of the G stack from the one that lives
// at the top of the M stack because the one at the top of // at the top of the M stack because the one at the top of
// the M stack terminates the stack walk (see topofstack()). // the M stack terminates the stack walk (see topofstack()).
TEXT runtime·switchtoM(SB),NOSPLIT,$0-4 TEXT runtime·switchtoM(SB),NOSPLIT,$0-0
MOVW $0, R0 MOVW $0, R0
BL (R0) // clobber lr to ensure push {lr} is kept BL (R0) // clobber lr to ensure push {lr} is kept
RET RET
...@@ -258,7 +256,6 @@ oncurg: ...@@ -258,7 +256,6 @@ oncurg:
MOVW R3, SP MOVW R3, SP
// call target function // call target function
ARGSIZE(0)
MOVW R0, R7 MOVW R0, R7
MOVW 0(R0), R0 MOVW 0(R0), R0
BL (R0) BL (R0)
...@@ -490,6 +487,7 @@ TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8 ...@@ -490,6 +487,7 @@ TEXT runtime·asmcgocall(SB),NOSPLIT,$0-8
RET RET
TEXT runtime·asmcgocall_errno(SB),NOSPLIT,$0-12 TEXT runtime·asmcgocall_errno(SB),NOSPLIT,$0-12
GO_ARGS
MOVW fn+0(FP), R1 MOVW fn+0(FP), R1
MOVW arg+4(FP), R0 MOVW arg+4(FP), R0
BL asmcgocall<>(SB) BL asmcgocall<>(SB)
...@@ -553,6 +551,9 @@ TEXT runtime·cgocallback(SB),NOSPLIT,$12-12 ...@@ -553,6 +551,9 @@ TEXT runtime·cgocallback(SB),NOSPLIT,$12-12
// cgocallback_gofunc(void (*fn)(void*), void *frame, uintptr framesize) // cgocallback_gofunc(void (*fn)(void*), void *frame, uintptr framesize)
// See cgocall.c for more details. // See cgocall.c for more details.
TEXT runtime·cgocallback_gofunc(SB),NOSPLIT,$8-12 TEXT runtime·cgocallback_gofunc(SB),NOSPLIT,$8-12
GO_ARGS
NO_LOCAL_POINTERS
// Load m and g from thread-local storage. // Load m and g from thread-local storage.
MOVB runtime·iscgo(SB), R0 MOVB runtime·iscgo(SB), R0
CMP $0, R0 CMP $0, R0
......
...@@ -65,7 +65,6 @@ ...@@ -65,7 +65,6 @@
enum { enum {
Debug = 0, Debug = 0,
ConcurrentSweep = 1, ConcurrentSweep = 1,
PreciseScan = 1,
WorkbufSize = 4*1024, WorkbufSize = 4*1024,
FinBlockSize = 4*1024, FinBlockSize = 4*1024,
...@@ -239,16 +238,6 @@ scanblock(byte *b, uintptr n, byte *ptrmask) ...@@ -239,16 +238,6 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
ptrmask = nil; // use GC bitmap for pointer info ptrmask = nil; // use GC bitmap for pointer info
scanobj: scanobj:
if(!PreciseScan) {
if(ptrmask == nil) {
// Heap obj, obtain real size.
if(!runtime·mlookup(b, &p, &n, nil))
continue; // not an allocated obj
if(b != p)
runtime·throw("bad heap object");
}
ptrmask = ScanConservatively;
}
// Find bits of the beginning of the object. // Find bits of the beginning of the object.
if(ptrmask == nil) { if(ptrmask == nil) {
off = (uintptr*)b - (uintptr*)arena_start; off = (uintptr*)b - (uintptr*)arena_start;
...@@ -620,7 +609,7 @@ scanframe(Stkframe *frame, void *unused) ...@@ -620,7 +609,7 @@ scanframe(Stkframe *frame, void *unused)
Func *f; Func *f;
StackMap *stackmap; StackMap *stackmap;
BitVector bv; BitVector bv;
uintptr size; uintptr size, minsize;
uintptr targetpc; uintptr targetpc;
int32 pcdata; int32 pcdata;
...@@ -644,25 +633,21 @@ scanframe(Stkframe *frame, void *unused) ...@@ -644,25 +633,21 @@ scanframe(Stkframe *frame, void *unused)
} }
// Scan local variables if stack frame has been allocated. // Scan local variables if stack frame has been allocated.
// Use pointer information if known. size = frame->varp - frame->sp;
stackmap = runtime·funcdata(f, FUNCDATA_LocalsPointerMaps); minsize = 0;
if(stackmap == nil) { if(thechar != '6' && thechar != '8')
// No locals information, scan everything. minsize = sizeof(uintptr);
size = frame->varp - frame->sp; if(size > minsize) {
if(Debug > 2) stackmap = runtime·funcdata(f, FUNCDATA_LocalsPointerMaps);
runtime·printf("frame %s unsized locals %p+%p\n", runtime·funcname(f), (byte*)(frame->varp-size), size); if(stackmap == nil || stackmap->n <= 0) {
scanblock((byte*)(frame->varp - size), size, ScanConservatively); runtime·printf("runtime: frame %s untyped locals %p+%p\n", runtime·funcname(f), (byte*)(frame->varp-size), size);
} else if(stackmap->n < 0) { runtime·throw("missing stackmap");
// Locals size information, scan just the locals. }
size = -stackmap->n;
if(Debug > 2)
runtime·printf("frame %s conservative locals %p+%p\n", runtime·funcname(f), (byte*)(frame->varp-size), size);
scanblock((byte*)(frame->varp - size), size, ScanConservatively);
} else if(stackmap->n > 0) {
// Locals bitmap information, scan just the pointers in locals. // Locals bitmap information, scan just the pointers in locals.
if(pcdata < 0 || pcdata >= stackmap->n) { if(pcdata < 0 || pcdata >= stackmap->n) {
// don't know where we are // don't know where we are
runtime·printf("pcdata is %d and %d stack map entries for %s (targetpc=%p)\n", runtime·printf("runtime: pcdata is %d and %d locals stack map entries for %s (targetpc=%p)\n",
pcdata, stackmap->n, runtime·funcname(f), targetpc); pcdata, stackmap->n, runtime·funcname(f), targetpc);
runtime·throw("scanframe: bad symbol table"); runtime·throw("scanframe: bad symbol table");
} }
...@@ -672,19 +657,26 @@ scanframe(Stkframe *frame, void *unused) ...@@ -672,19 +657,26 @@ scanframe(Stkframe *frame, void *unused)
} }
// Scan arguments. // Scan arguments.
// Use pointer information if known. if(frame->arglen > 0) {
if(frame->argmap != nil) { if(frame->argmap != nil)
bv = *frame->argmap; bv = *frame->argmap;
scanblock((byte*)frame->argp, bv.n/BitsPerPointer*PtrSize, bv.bytedata); else {
} else if((stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps)) != nil) { stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps);
bv = runtime·stackmapdata(stackmap, pcdata); if(stackmap == nil || stackmap->n <= 0) {
scanblock((byte*)frame->argp, bv.n/BitsPerPointer*PtrSize, bv.bytedata); runtime·printf("runtime: frame %s untyped args %p+%p\n", runtime·funcname(f), frame->argp, (uintptr)frame->arglen);
} else { runtime·throw("missing stackmap");
if(Debug > 2) }
runtime·printf("frame %s conservative args %p+%p\n", runtime·funcname(f), frame->argp, (uintptr)frame->arglen); if(pcdata < 0 || pcdata >= stackmap->n) {
scanblock((byte*)frame->argp, frame->arglen, ScanConservatively); // don't know where we are
} runtime·printf("runtime: pcdata is %d and %d args stack map entries for %s (targetpc=%p)\n",
return true; pcdata, stackmap->n, runtime·funcname(f), targetpc);
runtime·throw("scanframe: bad symbol table");
}
bv = runtime·stackmapdata(stackmap, pcdata);
}
scanblock((byte*)frame->argp, bv.n/BitsPerPointer*PtrSize, bv.bytedata);
}
return true;
} }
static void static void
......
...@@ -806,7 +806,6 @@ void runtime·signalstack(byte*, int32); ...@@ -806,7 +806,6 @@ void runtime·signalstack(byte*, int32);
void runtime·symtabinit(void); void runtime·symtabinit(void);
Func* runtime·findfunc(uintptr); Func* runtime·findfunc(uintptr);
int32 runtime·funcline(Func*, uintptr, String*); int32 runtime·funcline(Func*, uintptr, String*);
int32 runtime·funcarglen(Func*, uintptr);
int32 runtime·funcspdelta(Func*, uintptr); int32 runtime·funcspdelta(Func*, uintptr);
int8* runtime·funcname(Func*); int8* runtime·funcname(Func*);
int32 runtime·pcdatavalue(Func*, int32, uintptr); int32 runtime·pcdatavalue(Func*, int32, uintptr);
......
...@@ -237,13 +237,6 @@ func pcdatavalue(f *_func, table int32, targetpc uintptr) int32 { ...@@ -237,13 +237,6 @@ func pcdatavalue(f *_func, table int32, targetpc uintptr) int32 {
return pcvalue(f, off, targetpc, true) return pcvalue(f, off, targetpc, true)
} }
func funcarglen(f *_func, targetpc uintptr) int32 {
if targetpc == f.entry {
return 0
}
return pcdatavalue(f, _PCDATA_ArgSize, targetpc-_PCQuantum)
}
func funcdata(f *_func, i int32) unsafe.Pointer { func funcdata(f *_func, i int32) unsafe.Pointer {
if i < 0 || i >= f.nfuncdata { if i < 0 || i >= f.nfuncdata {
return nil return nil
......
...@@ -187,41 +187,23 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf ...@@ -187,41 +187,23 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf
if usesLR { if usesLR {
frame.argp += ptrSize frame.argp += ptrSize
} }
if f.args != _ArgsSizeUnknown { frame.arglen = uintptr(f.args)
frame.arglen = uintptr(f.args) if callback != nil && f.args == _ArgsSizeUnknown {
} else if callback != nil && (gofuncname(f) == "reflect.makeFuncStub" || gofuncname(f) == "reflect.methodValueCall") { // Extract argument bitmaps for reflect stubs from the calls they made to reflect.
// NOTE: Two calls to gofuncname on line above will be switch gofuncname(f) {
// collapsed to one when we pull out all the imprecise fallback code. case "reflect.makeFuncStub", "reflect.methodValueCall":
arg0 := frame.sp arg0 := frame.sp
if usesLR { if usesLR {
arg0 += ptrSize arg0 += ptrSize
}
fn := *(**[2]uintptr)(unsafe.Pointer(arg0))
if fn[0] != f.entry {
print("runtime: confused by ", gofuncname(f), "\n")
gothrow("reflect mismatch")
}
bv := (*bitvector)(unsafe.Pointer(fn[1]))
frame.arglen = uintptr(bv.n / 2 * ptrSize)
frame.argmap = bv
} else if flr == nil {
frame.arglen = 0
} else {
i := funcarglen(flr, frame.lr)
if i >= 0 {
frame.arglen = uintptr(i)
} else {
var tmp string
if flr != nil {
tmp = gofuncname(flr)
} else {
tmp = "?"
} }
print("runtime: unknown argument frame size for ", gofuncname(f), " called from ", hex(frame.lr), " [", tmp, "]\n") fn := *(**[2]uintptr)(unsafe.Pointer(arg0))
if callback != nil { if fn[0] != f.entry {
gothrow("invalid stack") print("runtime: confused by ", gofuncname(f), "\n")
gothrow("reflect mismatch")
} }
frame.arglen = 0 bv := (*bitvector)(unsafe.Pointer(fn[1]))
frame.arglen = uintptr(bv.n / 2 * ptrSize)
frame.argmap = bv
} }
} }
} }
......
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