Commit d67e7e3a authored by Russ Cox's avatar Russ Cox

runtime: add lr, ctxt, ret to Gobuf

Add gostartcall and gostartcallfn.
The old gogocall = gostartcall + gogo.
The old gogocallfn = gostartcallfn + gogo.

R=dvyukov, minux.ma
CC=golang-dev
https://golang.org/cl/10036044
parent a8ad859c
...@@ -148,7 +148,7 @@ func testCallbackCallers(t *testing.T) { ...@@ -148,7 +148,7 @@ func testCallbackCallers(t *testing.T) {
"test.goCallback", "test.goCallback",
"runtime.cgocallbackg", "runtime.cgocallbackg",
"runtime.cgocallback_gofunc", "runtime.cgocallback_gofunc",
"return", "runtime.asmcgocall",
"runtime.cgocall", "runtime.cgocall",
"test._Cfunc_callback", "test._Cfunc_callback",
"test.nestedCall", "test.nestedCall",
......
...@@ -134,6 +134,8 @@ TEXT runtime·gosave(SB), 7, $0 ...@@ -134,6 +134,8 @@ TEXT runtime·gosave(SB), 7, $0
MOVL BX, gobuf_sp(AX) MOVL BX, gobuf_sp(AX)
MOVL 0(SP), BX // caller's PC MOVL 0(SP), BX // caller's PC
MOVL BX, gobuf_pc(AX) MOVL BX, gobuf_pc(AX)
MOVL $0, gobuf_ret(AX)
MOVL $0, gobuf_ctxt(AX)
get_tls(CX) get_tls(CX)
MOVL g(CX), BX MOVL g(CX), BX
MOVL BX, gobuf_g(AX) MOVL BX, gobuf_g(AX)
...@@ -142,50 +144,20 @@ TEXT runtime·gosave(SB), 7, $0 ...@@ -142,50 +144,20 @@ TEXT runtime·gosave(SB), 7, $0
// void gogo(Gobuf*, uintptr) // void gogo(Gobuf*, uintptr)
// restore state from Gobuf; longjmp // restore state from Gobuf; longjmp
TEXT runtime·gogo(SB), 7, $0 TEXT runtime·gogo(SB), 7, $0
MOVL 8(SP), AX // return 2nd arg
MOVL 4(SP), BX // gobuf MOVL 4(SP), BX // gobuf
MOVL gobuf_g(BX), DX MOVL gobuf_g(BX), DX
MOVL 0(DX), CX // make sure g != nil MOVL 0(DX), CX // make sure g != nil
get_tls(CX) get_tls(CX)
MOVL DX, g(CX) MOVL DX, g(CX)
MOVL gobuf_sp(BX), SP // restore SP MOVL gobuf_sp(BX), SP // restore SP
MOVL gobuf_ret(BX), AX
MOVL gobuf_ctxt(BX), DX
MOVL $0, gobuf_sp(BX) // clear to help garbage collector
MOVL $0, gobuf_ret(BX)
MOVL $0, gobuf_ctxt(BX)
MOVL gobuf_pc(BX), BX MOVL gobuf_pc(BX), BX
JMP BX JMP BX
// void gogocall(Gobuf*, void (*fn)(void), uintptr r0)
// restore state from Gobuf but then call fn.
// (call fn, returning to state in Gobuf)
TEXT runtime·gogocall(SB), 7, $0
MOVL 12(SP), DX // context
MOVL 8(SP), AX // fn
MOVL 4(SP), BX // gobuf
MOVL gobuf_g(BX), DI
get_tls(CX)
MOVL DI, g(CX)
MOVL 0(DI), CX // make sure g != nil
MOVL gobuf_sp(BX), SP // restore SP
MOVL gobuf_pc(BX), BX
PUSHL BX
JMP AX
POPL BX // not reached
// void gogocallfn(Gobuf*, FuncVal*)
// restore state from Gobuf but then call fn.
// (call fn, returning to state in Gobuf)
TEXT runtime·gogocallfn(SB), 7, $0
MOVL 8(SP), DX // fn
MOVL 4(SP), BX // gobuf
MOVL gobuf_g(BX), DI
get_tls(CX)
MOVL DI, g(CX)
MOVL 0(DI), CX // make sure g != nil
MOVL gobuf_sp(BX), SP // restore SP
MOVL gobuf_pc(BX), BX
PUSHL BX
MOVL 0(DX), BX
JMP BX
POPL BX // not reached
// void mcall(void (*fn)(G*)) // void mcall(void (*fn)(G*))
// Switch to m->g0's stack, call fn(g). // Switch to m->g0's stack, call fn(g).
// Fn must never return. It should gogo(&g->sched) // Fn must never return. It should gogo(&g->sched)
...@@ -469,11 +441,20 @@ TEXT runtime·jmpdefer(SB), 7, $0 ...@@ -469,11 +441,20 @@ TEXT runtime·jmpdefer(SB), 7, $0
MOVL 0(DX), BX MOVL 0(DX), BX
JMP BX // but first run the deferred function JMP BX // but first run the deferred function
// Dummy function to use in saved gobuf.PC, // Save state of caller into g->sched.
// to match SP pointing at a return address. TEXT gosave<>(SB),7,$0
// The gobuf.PC is unused by the contortions here PUSHL AX
// but setting it to return will make the traceback code work. PUSHL BX
TEXT return<>(SB),7,$0 get_tls(BX)
MOVL g(BX), BX
LEAL arg+0(FP), AX
MOVL AX, (g_sched+gobuf_sp)(BX)
MOVL -4(AX), AX
MOVL AX, (g_sched+gobuf_pc)(BX)
MOVL $0, (g_sched+gobuf_ret)(BX)
MOVL $0, (g_sched+gobuf_ctxt)(BX)
POPL BX
POPL AX
RET RET
// asmcgocall(void(*fn)(void*), void *arg) // asmcgocall(void(*fn)(void*), void *arg)
...@@ -493,10 +474,8 @@ TEXT runtime·asmcgocall(SB),7,$0 ...@@ -493,10 +474,8 @@ TEXT runtime·asmcgocall(SB),7,$0
MOVL m_g0(BP), SI MOVL m_g0(BP), SI
MOVL g(CX), DI MOVL g(CX), DI
CMPL SI, DI CMPL SI, DI
JEQ 6(PC) JEQ 4(PC)
MOVL SP, (g_sched+gobuf_sp)(DI) CALL gosave<>(SB)
MOVL $return<>(SB), (g_sched+gobuf_pc)(DI)
MOVL DI, (g_sched+gobuf_g)(DI)
MOVL SI, g(CX) MOVL SI, g(CX)
MOVL (g_sched+gobuf_sp)(SI), SP MOVL (g_sched+gobuf_sp)(SI), SP
......
...@@ -121,6 +121,8 @@ TEXT runtime·gosave(SB), 7, $0 ...@@ -121,6 +121,8 @@ TEXT runtime·gosave(SB), 7, $0
MOVQ BX, gobuf_sp(AX) MOVQ BX, gobuf_sp(AX)
MOVQ 0(SP), BX // caller's PC MOVQ 0(SP), BX // caller's PC
MOVQ BX, gobuf_pc(AX) MOVQ BX, gobuf_pc(AX)
MOVQ $0, gobuf_ret(AX)
MOVQ $0, gobuf_ctxt(AX)
get_tls(CX) get_tls(CX)
MOVQ g(CX), BX MOVQ g(CX), BX
MOVQ BX, gobuf_g(AX) MOVQ BX, gobuf_g(AX)
...@@ -129,50 +131,20 @@ TEXT runtime·gosave(SB), 7, $0 ...@@ -129,50 +131,20 @@ TEXT runtime·gosave(SB), 7, $0
// void gogo(Gobuf*, uintptr) // void gogo(Gobuf*, uintptr)
// restore state from Gobuf; longjmp // restore state from Gobuf; longjmp
TEXT runtime·gogo(SB), 7, $0 TEXT runtime·gogo(SB), 7, $0
MOVQ 16(SP), AX // return 2nd arg
MOVQ 8(SP), BX // gobuf MOVQ 8(SP), BX // gobuf
MOVQ gobuf_g(BX), DX MOVQ gobuf_g(BX), DX
MOVQ 0(DX), CX // make sure g != nil MOVQ 0(DX), CX // make sure g != nil
get_tls(CX) get_tls(CX)
MOVQ DX, g(CX) MOVQ DX, g(CX)
MOVQ gobuf_sp(BX), SP // restore SP MOVQ gobuf_sp(BX), SP // restore SP
MOVQ gobuf_ret(BX), AX
MOVQ gobuf_ctxt(BX), DX
MOVQ $0, gobuf_sp(BX) // clear to help garbage collector
MOVQ $0, gobuf_ret(BX)
MOVQ $0, gobuf_ctxt(BX)
MOVQ gobuf_pc(BX), BX MOVQ gobuf_pc(BX), BX
JMP BX JMP BX
// void gogocall(Gobuf*, void (*fn)(void), uintptr r0)
// restore state from Gobuf but then call fn.
// (call fn, returning to state in Gobuf)
TEXT runtime·gogocall(SB), 7, $0
MOVQ 24(SP), DX // context
MOVQ 16(SP), AX // fn
MOVQ 8(SP), BX // gobuf
MOVQ gobuf_g(BX), DI
get_tls(CX)
MOVQ DI, g(CX)
MOVQ 0(DI), CX // make sure g != nil
MOVQ gobuf_sp(BX), SP // restore SP
MOVQ gobuf_pc(BX), BX
PUSHQ BX
JMP AX
POPQ BX // not reached
// void gogocallfn(Gobuf*, FuncVal*)
// restore state from Gobuf but then call fn.
// (call fn, returning to state in Gobuf)
TEXT runtime·gogocallfn(SB), 7, $0
MOVQ 16(SP), DX // fn
MOVQ 8(SP), BX // gobuf
MOVQ gobuf_g(BX), AX
get_tls(CX)
MOVQ AX, g(CX)
MOVQ 0(AX), CX // make sure g != nil
MOVQ gobuf_sp(BX), SP // restore SP
MOVQ gobuf_pc(BX), BX
PUSHQ BX
MOVQ 0(DX), BX
JMP BX
POPQ BX // not reached
// void mcall(void (*fn)(G*)) // void mcall(void (*fn)(G*))
// Switch to m->g0's stack, call fn(g). // Switch to m->g0's stack, call fn(g).
// Fn must never return. It should gogo(&g->sched) // Fn must never return. It should gogo(&g->sched)
...@@ -505,11 +477,16 @@ TEXT runtime·jmpdefer(SB), 7, $0 ...@@ -505,11 +477,16 @@ TEXT runtime·jmpdefer(SB), 7, $0
MOVQ 0(DX), BX MOVQ 0(DX), BX
JMP BX // but first run the deferred function JMP BX // but first run the deferred function
// Dummy function to use in saved gobuf.PC, // Save state of caller into g->sched. Smashes R8, R9.
// to match SP pointing at a return address. TEXT gosave<>(SB),7,$0
// The gobuf.PC is unused by the contortions here get_tls(R8)
// but setting it to return will make the traceback code work. MOVQ g(R8), R8
TEXT return<>(SB),7,$0 MOVQ 0(SP), R9
MOVQ R9, (g_sched+gobuf_pc)(R8)
LEAQ 8(SP), R9
MOVQ R9, (g_sched+gobuf_sp)(R8)
MOVQ $0, (g_sched+gobuf_ret)(R8)
MOVQ $0, (g_sched+gobuf_ctxt)(R8)
RET RET
// asmcgocall(void(*fn)(void*), void *arg) // asmcgocall(void(*fn)(void*), void *arg)
...@@ -529,10 +506,8 @@ TEXT runtime·asmcgocall(SB),7,$0 ...@@ -529,10 +506,8 @@ TEXT runtime·asmcgocall(SB),7,$0
MOVQ m_g0(BP), SI MOVQ m_g0(BP), SI
MOVQ g(CX), DI MOVQ g(CX), DI
CMPQ SI, DI CMPQ SI, DI
JEQ 6(PC) JEQ 4(PC)
MOVQ SP, (g_sched+gobuf_sp)(DI) CALL gosave<>(SB)
MOVQ $return<>(SB), (g_sched+gobuf_pc)(DI)
MOVQ DI, (g_sched+gobuf_g)(DI)
MOVQ SI, g(CX) MOVQ SI, g(CX)
MOVQ (g_sched+gobuf_sp)(SI), SP MOVQ (g_sched+gobuf_sp)(SI), SP
......
...@@ -102,6 +102,10 @@ TEXT runtime·gosave(SB), 7, $-4 ...@@ -102,6 +102,10 @@ TEXT runtime·gosave(SB), 7, $-4
MOVW SP, gobuf_sp(R0) MOVW SP, gobuf_sp(R0)
MOVW LR, gobuf_pc(R0) MOVW LR, gobuf_pc(R0)
MOVW g, gobuf_g(R0) MOVW g, gobuf_g(R0)
MOVW $0, R11
MOVW R11, gobuf_lr(R0)
MOVW R11, gobuf_ret(R0)
MOVW R11, gobuf_ctxt(R0)
RET RET
// void gogo(Gobuf*, uintptr) // void gogo(Gobuf*, uintptr)
...@@ -113,44 +117,17 @@ TEXT runtime·gogo(SB), 7, $-4 ...@@ -113,44 +117,17 @@ TEXT runtime·gogo(SB), 7, $-4
MOVW _cgo_save_gm(SB), R2 MOVW _cgo_save_gm(SB), R2
CMP $0, R2 // if in Cgo, we have to save g and m CMP $0, R2 // if in Cgo, we have to save g and m
BL.NE (R2) // this call will clobber R0 BL.NE (R2) // this call will clobber R0
MOVW 4(FP), R0 // return 2nd arg
MOVW gobuf_sp(R1), SP // restore SP MOVW gobuf_sp(R1), SP // restore SP
MOVW gobuf_lr(R1), LR
MOVW gobuf_ret(R1), R0
MOVW gobuf_ctxt(R1), R7
MOVW $0, R11
MOVW R11, gobuf_sp(R1) // clear to help garbage collector
MOVW R11, gobuf_ret(R1)
MOVW R11, gobuf_lr(R1)
MOVW R11, gobuf_ctxt(R1)
MOVW gobuf_pc(R1), PC MOVW gobuf_pc(R1), PC
// void gogocall(Gobuf*, void (*fn)(void), uintptr r7)
// restore state from Gobuf but then call fn.
// (call fn, returning to state in Gobuf)
// using frame size $-4 means do not save LR on stack.
TEXT runtime·gogocall(SB), 7, $-4
MOVW 0(FP), R3 // gobuf
MOVW 4(FP), R1 // fn
MOVW gobuf_g(R3), g
MOVW 0(g), R0 // make sure g != nil
MOVW _cgo_save_gm(SB), R0
CMP $0, R0 // if in Cgo, we have to save g and m
BL.NE (R0) // this call will clobber R0
MOVW 8(FP), R7 // context
MOVW gobuf_sp(R3), SP // restore SP
MOVW gobuf_pc(R3), LR
MOVW R1, PC
// void gogocallfn(Gobuf*, FuncVal*)
// restore state from Gobuf but then call fn.
// (call fn, returning to state in Gobuf)
// using frame size $-4 means do not save LR on stack.
TEXT runtime·gogocallfn(SB), 7, $-4
MOVW 0(FP), R3 // gobuf
MOVW 4(FP), R1 // fn
MOVW gobuf_g(R3), g
MOVW 0(g), R0 // make sure g != nil
MOVW _cgo_save_gm(SB), R0
CMP $0, R0 // if in Cgo, we have to save g and m
BL.NE (R0) // this call will clobber R0
MOVW gobuf_sp(R3), SP // restore SP
MOVW gobuf_pc(R3), LR
MOVW R1, R7
MOVW 0(R1), PC
// void mcall(void (*fn)(G*)) // void mcall(void (*fn)(G*))
// Switch to m->g0's stack, call fn(g). // Switch to m->g0's stack, call fn(g).
// Fn must never return. It should gogo(&g->sched) // Fn must never return. It should gogo(&g->sched)
...@@ -271,11 +248,14 @@ TEXT runtime·jmpdefer(SB), 7, $0 ...@@ -271,11 +248,14 @@ TEXT runtime·jmpdefer(SB), 7, $0
MOVW 0(R7), R1 MOVW 0(R7), R1
B (R1) B (R1)
// Dummy function to use in saved gobuf.PC, // Save state of caller into g->sched. Smashes R11.
// to match SP pointing at a return address. TEXT gosave<>(SB),7,$0
// The gobuf.PC is unused by the contortions here MOVW LR, (g_sched+gobuf_pc)(g)
// but setting it to return will make the traceback code work. MOVW R13, (g_sched+gobuf_sp)(g)
TEXT return<>(SB),7,$0 MOVW $0, R11
MOVW R11, (g_sched+gobuf_lr)(g)
MOVW R11, (g_sched+gobuf_ret)(g)
MOVW R11, (g_sched+gobuf_ctxt)(g)
RET RET
// asmcgocall(void(*fn)(void*), void *arg) // asmcgocall(void(*fn)(void*), void *arg)
...@@ -293,11 +273,8 @@ TEXT runtime·asmcgocall(SB),7,$0 ...@@ -293,11 +273,8 @@ TEXT runtime·asmcgocall(SB),7,$0
// come in on the m->g0 stack already. // come in on the m->g0 stack already.
MOVW m_g0(m), R3 MOVW m_g0(m), R3
CMP R3, g CMP R3, g
BEQ 7(PC) BEQ 4(PC)
MOVW R13, (g_sched + gobuf_sp)(g) BL gosave<>(SB)
MOVW $return<>(SB), R4
MOVW R4, (g_sched+gobuf_pc)(g)
MOVW g, (g_sched+gobuf_g)(g)
MOVW R3, g MOVW R3, g
MOVW (g_sched+gobuf_sp)(g), R13 MOVW (g_sched+gobuf_sp)(g), R13
......
...@@ -1428,7 +1428,9 @@ addstackroots(G *gp) ...@@ -1428,7 +1428,9 @@ addstackroots(G *gp)
M *mp; M *mp;
int32 n; int32 n;
Stktop *stk; Stktop *stk;
uintptr sp, guard, pc; uintptr sp, guard, pc, lr;
void *base;
uintptr size;
stk = (Stktop*)gp->stackbase; stk = (Stktop*)gp->stackbase;
guard = gp->stackguard; guard = gp->stackguard;
...@@ -1445,6 +1447,7 @@ addstackroots(G *gp) ...@@ -1445,6 +1447,7 @@ addstackroots(G *gp)
// the system call instead, since that won't change underfoot. // the system call instead, since that won't change underfoot.
sp = gp->gcsp; sp = gp->gcsp;
pc = gp->gcpc; pc = gp->gcpc;
lr = 0;
stk = (Stktop*)gp->gcstack; stk = (Stktop*)gp->gcstack;
guard = gp->gcguard; guard = gp->gcguard;
} else { } else {
...@@ -1452,11 +1455,16 @@ addstackroots(G *gp) ...@@ -1452,11 +1455,16 @@ addstackroots(G *gp)
// The goroutine is usually asleep (the world is stopped). // The goroutine is usually asleep (the world is stopped).
sp = gp->sched.sp; sp = gp->sched.sp;
pc = gp->sched.pc; pc = gp->sched.pc;
lr = gp->sched.lr;
// For function about to start, context argument is a root too.
if(gp->sched.ctxt != 0 && runtime·mlookup(gp->sched.ctxt, &base, &size, nil))
addroot((Obj){base, size, 0});
} }
if(ScanStackByFrames) { if(ScanStackByFrames) {
USED(stk); USED(stk);
USED(guard); USED(guard);
runtime·gentraceback(pc, sp, 0, gp, 0, nil, 0x7fffffff, addframeroots, nil); runtime·gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, addframeroots, nil);
} else { } else {
USED(pc); USED(pc);
n = 0; n = 0;
...@@ -2031,7 +2039,7 @@ mgc(G *gp) ...@@ -2031,7 +2039,7 @@ mgc(G *gp)
gc(gp->param); gc(gp->param);
gp->status = Grunning; gp->status = Grunning;
gp->param = nil; gp->param = nil;
runtime·gogo(&gp->sched, 0); runtime·gogo(&gp->sched);
} }
static void static void
......
...@@ -277,7 +277,8 @@ recovery(G *gp) ...@@ -277,7 +277,8 @@ recovery(G *gp)
else else
gp->sched.sp = (uintptr)argp - 2*sizeof(uintptr); gp->sched.sp = (uintptr)argp - 2*sizeof(uintptr);
gp->sched.pc = pc; gp->sched.pc = pc;
runtime·gogo(&gp->sched, 1); gp->sched.ret = 1;
runtime·gogo(&gp->sched);
} }
// Free stack frames until we hit the last one // Free stack frames until we hit the last one
......
...@@ -1000,9 +1000,7 @@ execute(G *gp) ...@@ -1000,9 +1000,7 @@ execute(G *gp)
if(m->profilehz != hz) if(m->profilehz != hz)
runtime·resetcpuprofiler(hz); runtime·resetcpuprofiler(hz);
if(gp->sched.pc == (uintptr)runtime·goexit) // kickoff runtime·gogo(&gp->sched);
runtime·gogocallfn(&gp->sched, gp->fnstart);
runtime·gogo(&gp->sched, 0);
} }
// Finds a runnable goroutine to execute. // Finds a runnable goroutine to execute.
...@@ -1254,7 +1252,6 @@ static void ...@@ -1254,7 +1252,6 @@ static void
goexit0(G *gp) goexit0(G *gp)
{ {
gp->status = Gdead; gp->status = Gdead;
gp->fnstart = nil;
gp->m = nil; gp->m = nil;
gp->lockedm = nil; gp->lockedm = nil;
m->curg = nil; m->curg = nil;
...@@ -1269,6 +1266,19 @@ goexit0(G *gp) ...@@ -1269,6 +1266,19 @@ goexit0(G *gp)
schedule(); schedule();
} }
static void
save(void *pc, uintptr sp)
{
g->gcpc = (uintptr)pc;
g->gcsp = sp;
g->sched.pc = (uintptr)pc;
g->sched.sp = sp;
g->sched.lr = 0;
g->sched.ret = 0;
g->sched.ctxt = 0;
g->sched.g = g;
}
// The goroutine g is about to enter a system call. // The goroutine g is about to enter a system call.
// Record that it's not using the cpu anymore. // Record that it's not using the cpu anymore.
// This is called only from the go syscall library and cgocall, // This is called only from the go syscall library and cgocall,
...@@ -1285,11 +1295,8 @@ void ...@@ -1285,11 +1295,8 @@ void
runtime·setprof(false); runtime·setprof(false);
// Leave SP around for gc and traceback. // Leave SP around for gc and traceback.
g->sched.sp = (uintptr)runtime·getcallersp(&dummy); save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy));
g->sched.pc = (uintptr)runtime·getcallerpc(&dummy);
g->sched.g = g;
g->gcsp = g->sched.sp;
g->gcpc = g->sched.pc;
g->gcstack = g->stackbase; g->gcstack = g->stackbase;
g->gcguard = g->stackguard; g->gcguard = g->stackguard;
g->status = Gsyscall; g->status = Gsyscall;
...@@ -1306,7 +1313,7 @@ void ...@@ -1306,7 +1313,7 @@ void
runtime·notewakeup(&runtime·sched.sysmonnote); runtime·notewakeup(&runtime·sched.sysmonnote);
} }
runtime·unlock(&runtime·sched); runtime·unlock(&runtime·sched);
runtime·gosave(&g->sched); // re-save for traceback save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy));
} }
m->mcache = nil; m->mcache = nil;
...@@ -1320,7 +1327,7 @@ void ...@@ -1320,7 +1327,7 @@ void
runtime·notewakeup(&runtime·sched.stopnote); runtime·notewakeup(&runtime·sched.stopnote);
} }
runtime·unlock(&runtime·sched); runtime·unlock(&runtime·sched);
runtime·gosave(&g->sched); // re-save for traceback save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy));
} }
} }
...@@ -1335,9 +1342,7 @@ void ...@@ -1335,9 +1342,7 @@ void
runtime·setprof(false); runtime·setprof(false);
// Leave SP around for gc and traceback. // Leave SP around for gc and traceback.
g->sched.sp = runtime·getcallersp(&dummy); save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy));
g->sched.pc = (uintptr)runtime·getcallerpc(&dummy);
g->sched.g = g;
g->gcsp = g->sched.sp; g->gcsp = g->sched.sp;
g->gcpc = g->sched.pc; g->gcpc = g->sched.pc;
g->gcstack = g->stackbase; g->gcstack = g->stackbase;
...@@ -1353,7 +1358,9 @@ void ...@@ -1353,7 +1358,9 @@ void
handoffp(p); handoffp(p);
if(g->isbackground) // do not consider blocked scavenger for deadlock detection if(g->isbackground) // do not consider blocked scavenger for deadlock detection
inclocked(1); inclocked(1);
runtime·gosave(&g->sched); // re-save for traceback
// Resave for traceback during blocked call.
save(runtime·getcallerpc(&dummy), runtime·getcallersp(&dummy));
} }
// The goroutine g exited its system call. // The goroutine g exited its system call.
...@@ -1450,7 +1457,7 @@ static void ...@@ -1450,7 +1457,7 @@ static void
mstackalloc(G *gp) mstackalloc(G *gp)
{ {
gp->param = runtime·stackalloc((uintptr)gp->param); gp->param = runtime·stackalloc((uintptr)gp->param);
runtime·gogo(&gp->sched, 0); runtime·gogo(&gp->sched);
} }
// Allocate a new g, with a stack big enough for stacksize bytes. // Allocate a new g, with a stack big enough for stacksize bytes.
...@@ -1552,10 +1559,11 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp ...@@ -1552,10 +1559,11 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp
*(void**)sp = nil; *(void**)sp = nil;
} }
runtime·memclr((byte*)&newg->sched, sizeof newg->sched);
newg->sched.sp = (uintptr)sp; newg->sched.sp = (uintptr)sp;
newg->sched.pc = (uintptr)runtime·goexit; newg->sched.pc = (uintptr)runtime·goexit;
newg->sched.g = newg; newg->sched.g = newg;
newg->fnstart = fn; runtime·gostartcallfn(&newg->sched, fn);
newg->gopc = (uintptr)callerpc; newg->gopc = (uintptr)callerpc;
newg->status = Grunnable; newg->status = Grunnable;
newg->goid = runtime·xadd64(&runtime·sched.goidgen, 1); newg->goid = runtime·xadd64(&runtime·sched.goidgen, 1);
...@@ -2421,3 +2429,12 @@ runtime·testSchedLocalQueueSteal(void) ...@@ -2421,3 +2429,12 @@ runtime·testSchedLocalQueueSteal(void)
} }
} }
bool
runtime·haszeroargs(uintptr pc)
{
return pc == (uintptr)runtime·goexit ||
pc == (uintptr)runtime·mcall ||
pc == (uintptr)runtime·mstart ||
pc == (uintptr)_rt0_go;
}
...@@ -209,10 +209,13 @@ struct Slice ...@@ -209,10 +209,13 @@ struct Slice
}; };
struct Gobuf struct Gobuf
{ {
// The offsets of these fields are known to (hard-coded in) libmach. // The offsets of sp, pc, and g are known to (hard-coded in) libmach.
uintptr sp; uintptr sp;
uintptr pc; uintptr pc;
G* g; G* g;
uintptr ret;
void* ctxt;
uintptr lr;
}; };
struct GCStats struct GCStats
{ {
...@@ -238,7 +241,6 @@ struct G ...@@ -238,7 +241,6 @@ struct G
uintptr gcguard; // if status==Gsyscall, gcguard = stackguard to use during gc uintptr gcguard; // if status==Gsyscall, gcguard = stackguard to use during gc
uintptr stackguard; // same as stackguard0, but not set to StackPreempt uintptr stackguard; // same as stackguard0, but not set to StackPreempt
uintptr stack0; uintptr stack0;
FuncVal* fnstart; // initial function
G* alllink; // on allg G* alllink; // on allg
void* param; // passed parameter on wakeup void* param; // passed parameter on wakeup
int16 status; int16 status;
...@@ -671,6 +673,7 @@ struct Stkframe ...@@ -671,6 +673,7 @@ struct Stkframe
int32 runtime·gentraceback(uintptr, uintptr, uintptr, G*, int32, uintptr*, int32, void(*)(Stkframe*, void*), void*); int32 runtime·gentraceback(uintptr, uintptr, uintptr, G*, int32, uintptr*, int32, void(*)(Stkframe*, void*), void*);
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);
/* /*
* external data * external data
...@@ -711,9 +714,9 @@ int32 runtime·charntorune(int32*, uint8*, int32); ...@@ -711,9 +714,9 @@ int32 runtime·charntorune(int32*, uint8*, int32);
*/ */
#define FLUSH(x) USED(x) #define FLUSH(x) USED(x)
void runtime·gogo(Gobuf*, uintptr); void runtime·gogo(Gobuf*);
void runtime·gogocall(Gobuf*, void(*)(void), uintptr); void runtime·gostartcall(Gobuf*, void(*)(void), void*);
void runtime·gogocallfn(Gobuf*, FuncVal*); void runtime·gostartcallfn(Gobuf*, FuncVal*);
void runtime·gosave(Gobuf*); void runtime·gosave(Gobuf*);
void runtime·lessstack(void); void runtime·lessstack(void);
void runtime·goargs(void); void runtime·goargs(void);
......
...@@ -130,7 +130,6 @@ runtime·oldstack(void) ...@@ -130,7 +130,6 @@ runtime·oldstack(void)
Stktop *top; Stktop *top;
Gobuf label; Gobuf label;
uint32 argsize; uint32 argsize;
uintptr cret;
byte *sp, *old; byte *sp, *old;
uintptr *src, *dst, *dstend; uintptr *src, *dst, *dstend;
G *gp; G *gp;
...@@ -161,9 +160,9 @@ runtime·oldstack(void) ...@@ -161,9 +160,9 @@ runtime·oldstack(void)
if(top->free != 0) if(top->free != 0)
runtime·stackfree(old, top->free); runtime·stackfree(old, top->free);
cret = m->cret; label.ret = m->cret;
m->cret = 0; // drop reference m->cret = 0; // drop reference
runtime·gogo(&label, cret); runtime·gogo(&label);
} }
// Called from reflect·call or from runtime·morestack when a new // Called from reflect·call or from runtime·morestack when a new
...@@ -270,13 +269,26 @@ runtime·newstack(void) ...@@ -270,13 +269,26 @@ runtime·newstack(void)
// Continue as if lessstack had just called m->morepc // Continue as if lessstack had just called m->morepc
// (the PC that decided to grow the stack). // (the PC that decided to grow the stack).
runtime·memclr((byte*)&label, sizeof label);
label.sp = sp; label.sp = sp;
label.pc = (uintptr)runtime·lessstack; label.pc = (uintptr)runtime·lessstack;
label.g = m->curg; label.g = m->curg;
if(reflectcall) if(reflectcall)
runtime·gogocallfn(&label, (FuncVal*)m->morepc); runtime·gostartcallfn(&label, (FuncVal*)m->morepc);
else else {
runtime·gogocall(&label, m->morepc, m->cret); // The stack growth code saves ctxt (not ret) in m->cret.
runtime·gostartcall(&label, m->morepc, (void*)m->cret);
m->cret = 0;
}
runtime·gogo(&label);
*(int32*)345 = 123; // never return *(int32*)345 = 123; // never return
} }
// adjust Gobuf as if it executed a call to fn
// and then did an immediate gosave.
void
runtime·gostartcallfn(Gobuf *gobuf, FuncVal *fv)
{
runtime·gostartcall(gobuf, fv->fn, fv);
}
...@@ -104,7 +104,7 @@ enum { ...@@ -104,7 +104,7 @@ enum {
// The assumed size of the top-of-stack data block. // The assumed size of the top-of-stack data block.
// The actual size can be smaller than this but cannot be larger. // The actual size can be smaller than this but cannot be larger.
// Checked in proc.c's runtime.malg. // Checked in proc.c's runtime.malg.
StackTop = 72, StackTop = 96,
// Goroutine preemption request. // Goroutine preemption request.
// Stored into g->stackguard0 to cause split stack check failure. // Stored into g->stackguard0 to cause split stack check failure.
......
// Copyright 2013 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"
// adjust Gobuf as if it executed a call to fn with context ctxt
// and then did an immediate Gosave.
void
runtime·gostartcall(Gobuf *gobuf, void (*fn)(void), void *ctxt)
{
if(gobuf->lr != 0)
runtime·throw("invalid use of gostartcall");
gobuf->lr = gobuf->pc;
gobuf->pc = (uintptr)fn;
gobuf->ctxt = ctxt;
}
// Copyright 2013 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.
// +build amd64 386
#include "runtime.h"
// adjust Gobuf as it if executed a call to fn with context ctxt
// and then did an immediate gosave.
void
runtime·gostartcall(Gobuf *gobuf, void (*fn)(void), void *ctxt)
{
uintptr *sp;
sp = (uintptr*)gobuf->sp;
*--sp = (uintptr)gobuf->pc;
gobuf->sp = (uintptr)sp;
gobuf->pc = (uintptr)fn;
gobuf->ctxt = ctxt;
}
...@@ -19,13 +19,15 @@ void _modu(void); ...@@ -19,13 +19,15 @@ void _modu(void);
int32 int32
runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max, void (*callback)(Stkframe*, void*), void *v) runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max, void (*callback)(Stkframe*, void*), void *v)
{ {
int32 i, n; int32 i, n, skip0;
uintptr x, tracepc; uintptr x, tracepc;
bool waspanic, printing; bool waspanic, printing;
Func *f, *f2; Func *f, *f2;
Stkframe frame; Stkframe frame;
Stktop *stk; Stktop *stk;
skip0 = skip;
runtime·memclr((byte*)&frame, sizeof frame); runtime·memclr((byte*)&frame, sizeof frame);
frame.pc = pc0; frame.pc = pc0;
frame.lr = lr0; frame.lr = lr0;
...@@ -33,12 +35,6 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, ...@@ -33,12 +35,6 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
waspanic = false; waspanic = false;
printing = pcbuf==nil && callback==nil; printing = pcbuf==nil && callback==nil;
// If the PC is goexit, the goroutine hasn't started yet.
if(frame.pc == (uintptr)runtime·goexit && gp->fnstart != nil) {
frame.pc = (uintptr)gp->fnstart->fn;
frame.lr = (uintptr)runtime·goexit;
}
// If the PC is zero, it's likely a nil function call. // If the PC is zero, it's likely a nil function call.
// Start in the caller's frame. // Start in the caller's frame.
if(frame.pc == 0) { if(frame.pc == 0) {
...@@ -69,8 +65,10 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, ...@@ -69,8 +65,10 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
} }
if(frame.pc <= 0x1000 || (frame.fn = f = runtime·findfunc(frame.pc)) == nil) { if(frame.pc <= 0x1000 || (frame.fn = f = runtime·findfunc(frame.pc)) == nil) {
if(callback != nil) if(callback != nil) {
runtime·throw("unknown pc"); runtime·printf("runtime: unknown pc %p at frame %d\n", frame.pc, skip0-skip+n);
runtime·throw("invalid stack");
}
break; break;
} }
...@@ -89,12 +87,12 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, ...@@ -89,12 +87,12 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
frame.arglen = 0; frame.arglen = 0;
if(f->args != ArgsSizeUnknown) if(f->args != ArgsSizeUnknown)
frame.arglen = f->args; frame.arglen = f->args;
else if(frame.pc == (uintptr)runtime·goexit || f->entry == (uintptr)runtime·mcall || f->entry == (uintptr)runtime·mstart || f->entry == (uintptr)_rt0_go) else if(runtime·haszeroargs(f->entry))
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(f->entry == (uintptr)runtime·deferproc || f->entry == (uintptr)runtime·newproc)
frame.arglen = 2*sizeof(uintptr) + ((uintptr*)frame.argp)[1]; frame.arglen = 3*sizeof(uintptr) + *(int32*)frame.argp;
else if((f2 = runtime·findfunc(frame.lr)) != nil && f2->frame >= sizeof(uintptr)) else if((f2 = runtime·findfunc(frame.lr)) != nil && f2->frame >= sizeof(uintptr))
frame.arglen = f2->frame; // conservative overestimate frame.arglen = f2->frame; // conservative overestimate
else { else {
......
...@@ -38,13 +38,6 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, ...@@ -38,13 +38,6 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
frame.sp = sp0; frame.sp = sp0;
waspanic = false; waspanic = false;
printing = pcbuf==nil && callback==nil; printing = pcbuf==nil && callback==nil;
// If the PC is goexit, the goroutine hasn't started yet.
if(frame.pc == gp->sched.pc && frame.sp == gp->sched.sp && frame.pc == (uintptr)runtime·goexit && gp->fnstart != nil) {
frame.fp = frame.sp;
frame.lr = frame.pc;
frame.pc = (uintptr)gp->fnstart->fn;
}
// If the PC is zero, it's likely a nil function call. // If the PC is zero, it's likely a nil function call.
// Start in the caller's frame. // Start in the caller's frame.
...@@ -98,12 +91,12 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, ...@@ -98,12 +91,12 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
frame.arglen = 0; frame.arglen = 0;
if(f->args != ArgsSizeUnknown) if(f->args != ArgsSizeUnknown)
frame.arglen = f->args; frame.arglen = f->args;
else if(frame.pc == (uintptr)runtime·goexit || f->entry == (uintptr)runtime·mcall || f->entry == (uintptr)runtime·mstart || f->entry == (uintptr)_rt0_go) else if(runtime·haszeroargs(f->entry))
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(f->entry == (uintptr)runtime·deferproc || f->entry == (uintptr)runtime·newproc)
frame.arglen = 2*sizeof(uintptr) + ((uintptr*)frame.argp)[1]; frame.arglen = 2*sizeof(uintptr) + *(int32*)frame.argp;
else if((f2 = runtime·findfunc(frame.lr)) != nil && f2->frame >= sizeof(uintptr)) else if((f2 = runtime·findfunc(frame.lr)) != nil && f2->frame >= sizeof(uintptr))
frame.arglen = f2->frame; // conservative overestimate frame.arglen = f2->frame; // conservative overestimate
else { else {
......
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