Until now, the goroutine state has been scattered during the
execution of newstack and oldstack. It's all there, and those routines
know how to get back to a working goroutine, but other pieces of
the system, like stack traces, do not. If something does interrupt
the newstack or oldstack execution, the rest of the system can't
understand the goroutine. For example, if newstack decides there
is an overflow and calls throw, the stack tracer wouldn't dump the
goroutine correctly.
For newstack to save a useful state snapshot, it needs to be able
to rewind the PC in the function that triggered the split back to
the beginning of the function. (The PC is a few instructions in, just
after the call to morestack.) To make that possible, we change the
prologues to insert a jmp back to the beginning of the function
after the call to morestack. That is, the prologue used to be roughly:
TEXT myfunc
check for split
jmpcond nosplit
call morestack
nosplit:
sub $xxx, sp
Now an extra instruction is inserted after the call:
TEXT myfunc
start:
check for split
jmpcond nosplit
call morestack
jmp start
nosplit:
sub $xxx, sp
The jmp is not executed directly. It is decoded and simulated by
runtime.rewindmorestack to discover the beginning of the function,
and then the call to morestack returns directly to the start label
instead of to the jump instruction. So logically the jmp is still
executed, just not by the cpu.
The prologue thus repeats in the case of a function that needs a
stack split, but against the cost of the split itself, the extra few
instructions are noise. The repeated prologue has the nice effect of
making a stack split double-check that the new stack is big enough:
if morestack happens to return on a too-small stack, we'll now notice
before corruption happens.
The ability for newstack to rewind to the beginning of the function
should help preemption too. If newstack decides that it was called
for preemption instead of a stack split, it now has the goroutine state
correctly paused if rescheduling is needed, and when the goroutine
can run again, it can return to the start label on its original stack
and re-execute the split check.
Here is an example of a split stack overflow showing the full
trace, without any special cases in the stack printer.
(This one was triggered by making the split check incorrect.)
runtime: newstack framesize=0x0 argsize=0x18 sp=0x6aebd0 stack=[0x6b0000, 0x6b0fa0]
morebuf={pc:0x69f5b sp:0x6aebd8 lr:0x0}
sched={pc:0x68880 sp:0x6aebd0 lr:0x0 ctxt:0x34e700}
runtime: split stack overflow: 0x6aebd0 < 0x6b0000
fatal error: runtime: split stack overflow
goroutine 1 [stack split]:
runtime.mallocgc(0x290, 0x100000000, 0x1)
/Users/rsc/g/go/src/pkg/runtime/zmalloc_darwin_amd64.c:21 fp=0x6aebd8
runtime.new()
/Users/rsc/g/go/src/pkg/runtime/zmalloc_darwin_amd64.c:682 +0x5b fp=0x6aec08
go/build.(*Context).Import(0x5ae340, 0xc210030c71, 0xa, 0xc2100b4380, 0x1b, ...)
/Users/rsc/g/go/src/pkg/go/build/build.go:424 +0x3a fp=0x6b00a0
main.loadImport(0xc210030c71, 0xa, 0xc2100b4380, 0x1b, 0xc2100b42c0, ...)
/Users/rsc/g/go/src/cmd/go/pkg.go:249 +0x371 fp=0x6b01a8
main.(*Package).load(0xc21017c800, 0xc2100b42c0, 0xc2101828c0, 0x0, 0x0, ...)
/Users/rsc/g/go/src/cmd/go/pkg.go:431 +0x2801 fp=0x6b0c98
main.loadPackage(0x369040, 0x7, 0xc2100b42c0, 0x0)
/Users/rsc/g/go/src/cmd/go/pkg.go:709 +0x857 fp=0x6b0f80
----- stack segment boundary -----
main.(*builder).action(0xc2100902a0, 0x0, 0x0, 0xc2100e6c00, 0xc2100e5750, ...)
/Users/rsc/g/go/src/cmd/go/build.go:539 +0x437 fp=0x6b14a0
main.(*builder).action(0xc2100902a0, 0x0, 0x0, 0xc21015b400, 0x2, ...)
/Users/rsc/g/go/src/cmd/go/build.go:528 +0x1d2 fp=0x6b1658
main.(*builder).test(0xc2100902a0, 0xc210092000, 0x0, 0x0, 0xc21008ff60, ...)
/Users/rsc/g/go/src/cmd/go/test.go:622 +0x1b53 fp=0x6b1f68
----- stack segment boundary -----
main.runTest(0x5a6b20, 0xc21000a020, 0x2, 0x2)
/Users/rsc/g/go/src/cmd/go/test.go:366 +0xd09 fp=0x6a5cf0
main.main()
/Users/rsc/g/go/src/cmd/go/main.go:161 +0x4f9 fp=0x6a5f78
runtime.main()
/Users/rsc/g/go/src/pkg/runtime/proc.c:183 +0x92 fp=0x6a5fa0
runtime.goexit()
/Users/rsc/g/go/src/pkg/runtime/proc.c:1266 fp=0x6a5fa8
And here is a seg fault during oldstack:
SIGSEGV: segmentation violation
PC=0x1b2a6
runtime.oldstack()
/Users/rsc/g/go/src/pkg/runtime/stack.c:159 +0x76
runtime.lessstack()
/Users/rsc/g/go/src/pkg/runtime/asm_amd64.s:270 +0x22
goroutine 1 [stack unsplit]:
fmt.(*pp).printArg(0x2102e64e0, 0xe5c80, 0x2102c9220, 0x73, 0x0, ...)
/Users/rsc/g/go/src/pkg/fmt/print.go:818 +0x3d3 fp=0x221031e6f8
fmt.(*pp).doPrintf(0x2102e64e0, 0x12fb20, 0x2, 0x221031eb98, 0x1, ...)
/Users/rsc/g/go/src/pkg/fmt/print.go:1183 +0x15cb fp=0x221031eaf0
fmt.Sprintf(0x12fb20, 0x2, 0x221031eb98, 0x1, 0x1, ...)
/Users/rsc/g/go/src/pkg/fmt/print.go:234 +0x67 fp=0x221031eb40
flag.(*stringValue).String(0x2102c9210, 0x1, 0x0)
/Users/rsc/g/go/src/pkg/flag/flag.go:180 +0xb3 fp=0x221031ebb0
flag.(*FlagSet).Var(0x2102f6000, 0x293d38, 0x2102c9210, 0x143490, 0xa, ...)
/Users/rsc/g/go/src/pkg/flag/flag.go:633 +0x40 fp=0x221031eca0
flag.(*FlagSet).StringVar(0x2102f6000, 0x2102c9210, 0x143490, 0xa, 0x12fa60, ...)
/Users/rsc/g/go/src/pkg/flag/flag.go:550 +0x91 fp=0x221031ece8
flag.(*FlagSet).String(0x2102f6000, 0x143490, 0xa, 0x12fa60, 0x0, ...)
/Users/rsc/g/go/src/pkg/flag/flag.go:563 +0x87 fp=0x221031ed38
flag.String(0x143490, 0xa, 0x12fa60, 0x0, 0x161950, ...)
/Users/rsc/g/go/src/pkg/flag/flag.go:570 +0x6b fp=0x221031ed80
testing.init()
/Users/rsc/g/go/src/pkg/testing/testing.go:-531 +0xbb fp=0x221031edc0
strings_test.init()
/Users/rsc/g/go/src/pkg/strings/strings_test.go:1115 +0x62 fp=0x221031ef70
main.init()
strings/_test/_testmain.go:90 +0x3d fp=0x221031ef78
runtime.main()
/Users/rsc/g/go/src/pkg/runtime/proc.c:180 +0x8a fp=0x221031efa0
runtime.goexit()
/Users/rsc/g/go/src/pkg/runtime/proc.c:1269 fp=0x221031efa8
goroutine 2 [runnable]:
runtime.MHeap_Scavenger()
/Users/rsc/g/go/src/pkg/runtime/mheap.c:438
runtime.goexit()
/Users/rsc/g/go/src/pkg/runtime/proc.c:1269
created by runtime.main
/Users/rsc/g/go/src/pkg/runtime/proc.c:166
rax 0x23ccc0
rbx 0x23ccc0
rcx 0x0
rdx 0x38
rdi 0x2102c0170
rsi 0x221032cfe0
rbp 0x221032cfa0
rsp 0x7fff5fbff5b0
r8 0x2102c0120
r9 0x221032cfa0
r10 0x221032c000
r11 0x104ce8
r12 0xe5c80
r13 0x1be82baac718
r14 0x13091135f7d69200
r15 0x0
rip 0x1b2a6
rflags 0x10246
cs 0x2b
fs 0x0
gs 0x0
Fixes #5723.
R=r, dvyukov, go.peter.90, dave, iant
CC=golang-dev
https://golang.org/cl/
10360048
#include <libc.h>
#include "gg.h"
-// TODO(kaib): Can make this bigger if we move
+// TODO(rsc): Can make this bigger if we move
// the text segment up higher in 5l for all GOOS.
+// At the same time, can raise StackBig in ../../pkg/runtime/stack.h.
long unmappedzero = 4096;
void
#include "l.h"
#include "../ld/lib.h"
-
-// see ../../runtime/proc.c:/StackGuard
-enum
-{
- StackBig = 4096,
- StackSmall = 128,
-};
+#include "../../pkg/runtime/stack.h"
static Sym* sym_div;
static Sym* sym_divu;
break;
}
- if(p->reg & NOSPLIT) {
- q1 = prg();
- q1->as = AMOVW;
- q1->scond |= C_WBIT;
- q1->line = p->line;
- q1->from.type = D_REG;
- q1->from.reg = REGLINK;
- q1->to.type = D_OREG;
- q1->to.offset = -autosize;
- q1->to.reg = REGSP;
- q1->spadj = autosize;
- q1->link = p->link;
- p->link = q1;
- } else if (autosize < StackBig) {
- // split stack check for small functions
- // MOVW g_stackguard(g), R1
- // CMP R1, $-autosize(SP)
- // MOVW.LO $autosize, R1
- // MOVW.LO $args, R2
- // MOVW.LO R14, R3
- // BL.LO runtime.morestack(SB) // modifies LR
- // MOVW.W R14,$-autosize(SP)
-
- // TODO(kaib): add more trampolines
- // TODO(kaib): put stackguard in register
- // TODO(kaib): add support for -K and underflow detection
-
+ if(!(p->reg & NOSPLIT)) {
// MOVW g_stackguard(g), R1
p = appendp(p);
p->as = AMOVW;
p->to.type = D_REG;
p->to.reg = 1;
- if(autosize < StackSmall) {
- // CMP R1, SP
+ if(autosize <= StackSmall) {
+ // small stack: SP < stackguard
+ // CMP stackguard, SP
p = appendp(p);
p->as = ACMP;
p->from.type = D_REG;
p->from.reg = 1;
p->reg = REGSP;
- } else {
- // MOVW $-autosize(SP), R2
- // CMP R1, R2
+ } else if(autosize <= StackBig) {
+ // large stack: SP-framesize < stackguard-StackSmall
+ // MOVW $-autosize(SP), R2
+ // CMP stackguard, R2
p = appendp(p);
p->as = AMOVW;
p->from.type = D_CONST;
p->from.type = D_REG;
p->from.reg = 1;
p->reg = 2;
+ } else {
+ // such a large stack we need to protect against wraparound
+ // if SP is close to zero.
+ // SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
+ // The +StackGuard on both sides is required to keep the left side positive:
+ // SP is allowed to be slightly below stackguard. See stack.h.
+ // MOVW $StackGuard(SP), R2
+ // SUB R1, R2
+ // MOVW $(autosize+(StackGuard-StackSmall)), R3
+ // CMP R3, R2
+ p = appendp(p);
+ p->as = AMOVW;
+ p->from.type = D_CONST;
+ p->from.reg = REGSP;
+ p->from.offset = StackGuard;
+ p->to.type = D_REG;
+ p->to.reg = 2;
+
+ p = appendp(p);
+ p->as = ASUB;
+ p->from.type = D_REG;
+ p->from.reg = 1;
+ p->to.type = D_REG;
+ p->to.reg = 2;
+
+ p = appendp(p);
+ p->as = AMOVW;
+ p->from.type = D_CONST;
+ p->from.offset = autosize + (StackGuard - StackSmall);
+ p->to.type = D_REG;
+ p->to.reg = 3;
+
+ p = appendp(p);
+ p->as = ACMP;
+ p->from.type = D_REG;
+ p->from.reg = 3;
+ p->reg = 2;
}
-
- // MOVW.LO $autosize, R1
+
+ // MOVW.LS $autosize, R1
p = appendp(p);
p->as = AMOVW;
- p->scond = C_SCOND_LO;
+ p->scond = C_SCOND_LS;
p->from.type = D_CONST;
p->from.offset = autosize;
p->to.type = D_REG;
p->to.reg = 1;
- // MOVW.LO $args, R2
+ // MOVW.LS $args, R2
p = appendp(p);
p->as = AMOVW;
- p->scond = C_SCOND_LO;
+ p->scond = C_SCOND_LS;
p->from.type = D_CONST;
p->from.offset = (cursym->text->to.offset2 + 3) & ~3;
p->to.type = D_REG;
p->to.reg = 2;
- // MOVW.LO R14, R3
+ // MOVW.LS R14, R3
p = appendp(p);
p->as = AMOVW;
- p->scond = C_SCOND_LO;
+ p->scond = C_SCOND_LS;
p->from.type = D_REG;
p->from.reg = REGLINK;
p->to.type = D_REG;
p->to.reg = 3;
- // BL.LO runtime.morestack(SB) // modifies LR
+ // BL.LS runtime.morestack(SB) // modifies LR, returns with LO still asserted
p = appendp(p);
p->as = ABL;
- p->scond = C_SCOND_LO;
+ p->scond = C_SCOND_LS;
p->to.type = D_BRANCH;
p->to.sym = symmorestack;
p->cond = pmorestack;
-
- // MOVW.W R14,$-autosize(SP)
- p = appendp(p);
- p->as = AMOVW;
- p->scond |= C_WBIT;
- p->from.type = D_REG;
- p->from.reg = REGLINK;
- p->to.type = D_OREG;
- p->to.offset = -autosize;
- p->to.reg = REGSP;
- p->spadj = autosize;
- } else { // > StackBig
- // MOVW $autosize, R1
- // MOVW $args, R2
- // MOVW R14, R3
- // BL runtime.morestack(SB) // modifies LR
- // MOVW.W R14,$-autosize(SP)
-
- // MOVW $autosize, R1
- p = appendp(p);
- p->as = AMOVW;
- p->from.type = D_CONST;
- p->from.offset = autosize;
- p->to.type = D_REG;
- p->to.reg = 1;
-
- // MOVW $args, R2
- // also need to store the extra 4 bytes.
- p = appendp(p);
- p->as = AMOVW;
- p->from.type = D_CONST;
- p->from.offset = (cursym->text->to.offset2 + 3) & ~3;
- p->to.type = D_REG;
- p->to.reg = 2;
-
- // MOVW R14, R3
- p = appendp(p);
- p->as = AMOVW;
- p->from.type = D_REG;
- p->from.reg = REGLINK;
- p->to.type = D_REG;
- p->to.reg = 3;
-
- // BL runtime.morestack(SB) // modifies LR
+
+ // BLS start
p = appendp(p);
- p->as = ABL;
+ p->as = ABLS;
p->to.type = D_BRANCH;
- p->to.sym = symmorestack;
- p->cond = pmorestack;
-
- // MOVW.W R14,$-autosize(SP)
- p = appendp(p);
- p->as = AMOVW;
- p->scond |= C_WBIT;
- p->from.type = D_REG;
- p->from.reg = REGLINK;
- p->to.type = D_OREG;
- p->to.offset = -autosize;
- p->to.reg = REGSP;
- p->spadj = autosize;
+ p->cond = cursym->text->link;
}
+
+ // MOVW.W R14,$-autosize(SP)
+ p = appendp(p);
+ p->as = AMOVW;
+ p->scond |= C_WBIT;
+ p->from.type = D_REG;
+ p->from.reg = REGLINK;
+ p->to.type = D_OREG;
+ p->to.offset = -autosize;
+ p->to.reg = REGSP;
+ p->spadj = autosize;
break;
case ARET:
// TODO(rsc): Can make this bigger if we move
// the text segment up higher in 6l for all GOOS.
+// At the same time, can raise StackBig in ../../pkg/runtime/stack.h.
vlong unmappedzero = 4096;
void
q1->pcond = p;
}
- if(autoffset < StackBig) { // do we need to call morestack?
- if(autoffset <= StackSmall) {
- // small stack
- p = appendp(p);
- p->as = ACMPQ;
- p->from.type = D_SP;
- p->to.type = D_INDIR+D_CX;
- } else {
- // large stack
- p = appendp(p);
- p->as = ALEAQ;
- p->from.type = D_INDIR+D_SP;
- p->from.offset = -(autoffset-StackSmall);
- p->to.type = D_AX;
-
- p = appendp(p);
- p->as = ACMPQ;
- p->from.type = D_AX;
- p->to.type = D_INDIR+D_CX;
- }
+ if(autoffset <= StackSmall) {
+ // small stack: SP <= stackguard
+ // CMPQ SP, stackguard
+ p = appendp(p);
+ p->as = ACMPQ;
+ p->from.type = D_SP;
+ p->to.type = D_INDIR+D_CX;
+ } else if(autoffset <= StackBig) {
+ // large stack: SP-framesize <= stackguard-StackSmall
+ // LEAQ -xxx(SP), AX
+ // CMPQ AX, stackguard
+ p = appendp(p);
+ p->as = ALEAQ;
+ p->from.type = D_INDIR+D_SP;
+ p->from.offset = -(autoffset-StackSmall);
+ p->to.type = D_AX;
- // common
p = appendp(p);
- p->as = AJHI;
- p->to.type = D_BRANCH;
- p->to.offset = 4;
- q = p;
- }
+ p->as = ACMPQ;
+ p->from.type = D_AX;
+ p->to.type = D_INDIR+D_CX;
+ } else {
+ // such a large stack we need to protect against wraparound
+ // if SP is close to zero:
+ // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
+ // The +StackGuard on both sides is required to keep the left side positive:
+ // SP is allowed to be slightly below stackguard. See stack.h.
+ // LEAQ StackGuard(SP), AX
+ // SUBQ stackguard, AX
+ // CMPQ AX, $(autoffset+(StackGuard-StackSmall))
+ p = appendp(p);
+ p->as = ALEAQ;
+ p->from.type = D_INDIR+D_SP;
+ p->from.offset = StackGuard;
+ p->to.type = D_AX;
+
+ p = appendp(p);
+ p->as = ASUBQ;
+ p->from.type = D_INDIR+D_CX;
+ p->from.offset = 0;
+ p->to.type = D_AX;
+
+ p = appendp(p);
+ p->as = ACMPQ;
+ p->from.type = D_AX;
+ p->to.type = D_CONST;
+ p->to.offset = autoffset+(StackGuard-StackSmall);
+ }
+
+ // common
+ p = appendp(p);
+ p->as = AJHI;
+ p->to.type = D_BRANCH;
+ p->to.offset = 4;
+ q = p;
// If we ask for more stack, we'll get a minimum of StackMin bytes.
// We need a stack frame large enough to hold the top-of-stack data,
p->pcond = pmorestack[3];
p->to.sym = symmorestack[3];
}
+
+ p = appendp(p);
+ p->as = AJMP;
+ p->to.type = D_BRANCH;
+ p->pcond = cursym->text->link;
}
if(q != P)
// TODO(rsc): Can make this bigger if we move
// the text segment up higher in 8l for all GOOS.
+// At the same time, can raise StackBig in ../../pkg/runtime/stack.h.
uint32 unmappedzero = 4096;
#define CASE(a,b) (((a)<<16)|((b)<<0))
q1->pcond = p;
}
- if(autoffset < StackBig) { // do we need to call morestack
- if(autoffset <= StackSmall) {
- // small stack
- p = appendp(p);
- p->as = ACMPL;
- p->from.type = D_SP;
- p->to.type = D_INDIR+D_CX;
- } else {
- // large stack
- p = appendp(p);
- p->as = ALEAL;
- p->from.type = D_INDIR+D_SP;
- p->from.offset = -(autoffset-StackSmall);
- p->to.type = D_AX;
-
- p = appendp(p);
- p->as = ACMPL;
- p->from.type = D_AX;
- p->to.type = D_INDIR+D_CX;
- }
+ if(autoffset <= StackSmall) {
+ // small stack: SP <= stackguard
+ // CMPL SP, stackguard
+ p = appendp(p);
+ p->as = ACMPL;
+ p->from.type = D_SP;
+ p->to.type = D_INDIR+D_CX;
+ } else if(autoffset <= StackBig) {
+ // large stack: SP-framesize <= stackguard-StackSmall
+ // LEAL -(autoffset-StackSmall)(SP), AX
+ // CMPL AX, stackguard
+ p = appendp(p);
+ p->as = ALEAL;
+ p->from.type = D_INDIR+D_SP;
+ p->from.offset = -(autoffset-StackSmall);
+ p->to.type = D_AX;
- // common
p = appendp(p);
- p->as = AJHI;
- p->to.type = D_BRANCH;
- p->to.offset = 4;
- q = p;
- }
+ p->as = ACMPL;
+ p->from.type = D_AX;
+ p->to.type = D_INDIR+D_CX;
+ } else {
+ // such a large stack we need to protect against wraparound
+ // if SP is close to zero.
+ // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
+ // The +StackGuard on both sides is required to keep the left side positive:
+ // SP is allowed to be slightly below stackguard. See stack.h.
+ // LEAL StackGuard(SP), AX
+ // SUBL stackguard, AX
+ // CMPL AX, $(autoffset+(StackGuard-StackSmall))
+ p = appendp(p);
+ p->as = ALEAL;
+ p->from.type = D_INDIR+D_SP;
+ p->from.offset = StackGuard;
+ p->to.type = D_AX;
+
+ p = appendp(p);
+ p->as = ASUBL;
+ p->from.type = D_INDIR+D_CX;
+ p->from.offset = 0;
+ p->to.type = D_AX;
+
+ p = appendp(p);
+ p->as = ACMPL;
+ p->from.type = D_AX;
+ p->to.type = D_CONST;
+ p->to.offset = autoffset+(StackGuard-StackSmall);
+ }
+
+ // common
+ p = appendp(p);
+ p->as = AJHI;
+ p->to.type = D_BRANCH;
+ p->to.offset = 4;
+ q = p;
p = appendp(p); // save frame size in DI
p->as = AMOVL;
p->pcond = pmorestack;
p->to.sym = symmorestack;
+ p = appendp(p);
+ p->as = AJMP;
+ p->to.type = D_BRANCH;
+ p->pcond = cursym->text->link;
}
if(q != P)
break;
if(s.value == morestack) {
+ // This code is old and won't work anymore.
+ // But no one uses it anyway.
+ // Leave it obviously broken until someone needs it.
+ werrstr("morestack not implemented correctly");
+ return -1;
// In the middle of morestack.
// Caller is m->morepc.
// Caller's caller is in m->morearg.
CMPL g(CX), SI
JNE 2(PC)
INT $3
-
- MOVL DX, m_cret(BX)
// frame size in DI
// arg size in AX
MOVL g(CX), SI
MOVL SI, (m_morebuf+gobuf_g)(BX)
- // Set m->morepc to f's PC.
- MOVL 0(SP), AX
- MOVL AX, m_morepc(BX)
+ // Set g->sched to context in f.
+ MOVL 0(SP), AX // f's PC
+ MOVL AX, (g_sched+gobuf_pc)(SI)
+ MOVL SI, (g_sched+gobuf_g)(SI)
+ LEAL 4(SP), AX // f's SP
+ MOVL AX, (g_sched+gobuf_sp)(SI)
+ MOVL DX, (g_sched+gobuf_ctxt)(SI)
// Call newstack on m->g0's stack.
MOVL m_g0(BX), BP
MOVL 8(SP), DX // arg frame
MOVL 12(SP), CX // arg size
- MOVL AX, m_morepc(BX) // f's PC
+ MOVL AX, m_cret(BX) // f's PC
MOVL DX, m_moreargp(BX) // f's argument pointer
MOVL CX, m_moreargsize(BX) // f's argument size
MOVL $1, m_moreframesize(BX) // f's frame size
CMPQ g(CX), SI
JNE 2(PC)
INT $3
-
- MOVQ DX, m_cret(BX)
// Called from f.
// Set m->morebuf to f's caller.
MOVQ g(CX), SI
MOVQ SI, (m_morebuf+gobuf_g)(BX)
- // Set m->morepc to f's PC.
- MOVQ 0(SP), AX
- MOVQ AX, m_morepc(BX)
+ // Set g->sched to context in f.
+ MOVQ 0(SP), AX // f's PC
+ MOVQ AX, (g_sched+gobuf_pc)(SI)
+ MOVQ SI, (g_sched+gobuf_g)(SI)
+ LEAQ 8(SP), AX // f's SP
+ MOVQ AX, (g_sched+gobuf_sp)(SI)
+ MOVQ DX, (g_sched+gobuf_ctxt)(SI)
// Call newstack on m->g0's stack.
MOVQ m_g0(BX), BP
MOVQ 16(SP), DX // arg frame
MOVL 24(SP), CX // arg size
- MOVQ AX, m_morepc(BX) // f's PC
+ MOVQ AX, m_cret(BX) // f's PC
MOVQ DX, m_moreargp(BX) // argument frame pointer
MOVL CX, m_moreargsize(BX) // f's argument size
MOVL $1, m_moreframesize(BX) // f's frame size
MOVW R11, gobuf_ret(R1)
MOVW R11, gobuf_lr(R1)
MOVW R11, gobuf_ctxt(R1)
+ CMP R11, R11 // set condition codes for == test, needed by stack split
MOVW gobuf_pc(R1), PC
// void mcall(void (*fn)(G*))
// Save caller state in g->sched.
MOVW SP, (g_sched+gobuf_sp)(g)
MOVW LR, (g_sched+gobuf_pc)(g)
+ MOVW $0, R11
+ MOVW R11, (g_sched+gobuf_lr)(g)
MOVW g, (g_sched+gobuf_g)(g)
// Switch to m->g0 & its stack, call fn.
CMP g, R4
BL.EQ runtime·abort(SB)
- // Save in m.
- MOVW R7, m_cret(m) // function context
MOVW R1, m_moreframesize(m)
MOVW R2, m_moreargsize(m)
+ // Called from f.
+ // Set g->sched to context in f.
+ MOVW R7, (g_sched+gobuf_ctxt)(g)
+ MOVW SP, (g_sched+gobuf_sp)(g)
+ MOVW LR, (g_sched+gobuf_pc)(g)
+ MOVW R3, (g_sched+gobuf_lr)(g)
+
// Called from f.
// Set m->morebuf to f's caller.
MOVW R3, (m_morebuf+gobuf_pc)(m) // f's caller's PC
MOVW R3, m_moreargp(m)
MOVW g, (m_morebuf+gobuf_g)(m)
- // Set m->morepc to f's PC.
- MOVW LR, m_morepc(m)
-
// Call newstack on m->g0's stack.
MOVW m_g0(m), g
MOVW (g_sched+gobuf_sp)(g), SP
MOVW 8(SP), R1 // arg frame
MOVW 12(SP), R2 // arg size
- MOVW R0, m_morepc(m) // f's PC
+ MOVW R0, m_cret(m) // f's PC
MOVW R1, m_moreargp(m) // f's argument pointer
MOVW R2, m_moreargsize(m) // f's argument size
MOVW $1, R3
if(ScanStackByFrames) {
USED(stk);
USED(guard);
- runtime·gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, addframeroots, nil);
+ runtime·gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, addframeroots, nil, false);
} else {
USED(pc);
n = 0;
} else {
// switch to g0, call gc(&a), then switch back
g->param = &a;
+ g->status = Gwaiting;
+ g->waitreason = "garbage collection";
runtime·mcall(mgc);
}
// record a new start time in case we're going around again
{
gc(gp->param);
gp->param = nil;
+ gp->status = Grunning;
runtime·gogo(&gp->sched);
}
{
int32 n;
- n = runtime·gentraceback((uintptr)pc, (uintptr)sp, 0, gp, 0, r->stk, nelem(r->stk), nil, nil);
+ n = runtime·gentraceback((uintptr)pc, (uintptr)sp, 0, gp, 0, r->stk, nelem(r->stk), nil, nil, false);
if(n < nelem(r->stk))
r->stk[n] = 0;
}
return NDFLT;
Throw:
+ m->throwing = 1;
+ m->caughtsig = gp;
runtime·startpanic();
runtime·printf("%s\n", s);
return NDFLT;
Throw:
+ m->throwing = 1;
+ m->caughtsig = gp;
runtime·startpanic();
runtime·printf("%s\n", s);
else
gp->sched.sp = (uintptr)argp - 2*sizeof(uintptr);
gp->sched.pc = pc;
+ gp->sched.lr = 0;
gp->sched.ret = 1;
runtime·gogo(&gp->sched);
}
int32 traceback;
traceback = runtime·gotraceback(nil);
+
+ // Show the current goroutine first, if we haven't already.
+ if((gp = m->curg) != nil && gp != me) {
+ runtime·printf("\n");
+ runtime·goroutineheader(gp);
+ runtime·traceback(gp->sched.pc, gp->sched.sp, gp->sched.lr, gp);
+ }
+
for(gp = runtime·allg; gp != nil; gp = gp->alllink) {
- if(gp == me || gp->status == Gdead)
+ if(gp == me || gp == m->curg || gp->status == Gdead)
continue;
if(gp->issystem && traceback < 2)
continue;
runtime·printf("\n");
runtime·goroutineheader(gp);
- runtime·traceback(gp->sched.pc, gp->sched.sp, 0, gp);
+ if(gp->status == Grunning)
+ runtime·printf("\tgoroutine running on other thread; stack unavailable\n");
+ else
+ runtime·traceback(gp->sched.pc, gp->sched.sp, gp->sched.lr, gp);
}
}
gp = runtime·malg(4096);
gp->sched.pc = (uintptr)runtime·goexit;
gp->sched.sp = gp->stackbase;
+ gp->sched.lr = 0;
gp->sched.g = gp;
gp->status = Gsyscall;
mp->curg = gp;
runtime·unlock(&prof);
return;
}
- n = runtime·gentraceback((uintptr)pc, (uintptr)sp, (uintptr)lr, gp, 0, prof.pcbuf, nelem(prof.pcbuf), nil, nil);
+ n = runtime·gentraceback((uintptr)pc, (uintptr)sp, (uintptr)lr, gp, 0, prof.pcbuf, nelem(prof.pcbuf), nil, nil, false);
if(n > 0)
prof.fn(prof.pcbuf, n);
runtime·unlock(&prof);
}
}
+extern void runtime·morestack(void);
+
bool
runtime·haszeroargs(uintptr pc)
{
return pc == (uintptr)runtime·goexit ||
pc == (uintptr)runtime·mcall ||
pc == (uintptr)runtime·mstart ||
+ pc == (uintptr)runtime·lessstack ||
+ pc == (uintptr)runtime·morestack ||
pc == (uintptr)_rt0_go;
}
};
struct M
{
- // The offsets of these fields are known to (hard-coded in) libmach.
G* g0; // goroutine with scheduling stack
- void (*morepc)(void);
void* moreargp; // argument pointer for more stack
Gobuf morebuf; // gobuf arg to morestack
uintptr tls[4]; // thread-local storage (for x86 extern register)
void (*mstartfn)(void);
G* curg; // current running goroutine
+ G* caughtsig; // goroutine running during fatal signal
P* p; // attached P for executing Go code (nil if not executing Go code)
P* nextp;
int32 id;
uintptr varlen; // number of bytes at varp
};
-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*, bool);
void runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G* gp);
void runtime·tracebackothers(G*);
bool runtime·haszeroargs(uintptr pc);
int32 runtime·gcount(void);
void runtime·mcall(void(*)(G*));
uint32 runtime·fastrand1(void);
+void runtime·rewindmorestack(Gobuf*);
void runtime·setmg(M*, G*);
void runtime·newextram(void);
Hchan* runtime·makechan_c(ChanType*, int64);
void runtime·chansend(ChanType*, Hchan*, byte*, bool*, void*);
void runtime·chanrecv(ChanType*, Hchan*, byte*, bool*, bool*);
-bool runtime·showframe(Func*, bool);
+bool runtime·showframe(Func*, G*);
void runtime·ifaceE2I(InterfaceType*, Eface, Iface*);
return;
Throw:
+ m->throwing = 1;
+ m->caughtsig = gp;
runtime·startpanic();
if(sig < 0 || sig >= NSIG)
if(runtime·gotraceback(&crash)){
runtime·traceback(SIG_EIP(info, ctxt), SIG_ESP(info, ctxt), 0, gp);
runtime·tracebackothers(gp);
+ runtime·printf("\n");
runtime·dumpregs(info, ctxt);
}
return;
Throw:
+ m->throwing = 1;
+ m->caughtsig = gp;
runtime·startpanic();
if(sig < 0 || sig >= NSIG)
if(runtime·gotraceback(&crash)){
runtime·traceback(SIG_RIP(info, ctxt), SIG_RSP(info, ctxt), 0, gp);
runtime·tracebackothers(gp);
+ runtime·printf("\n");
runtime·dumpregs(info, ctxt);
}
return;
Throw:
+ m->throwing = 1;
+ m->caughtsig = gp;
if(runtime·panicking) // traceback already printed
runtime·exit(2);
runtime·panicking = 1;
#include "malloc.h"
#include "stack.h"
+enum
+{
+ StackDebug = 0,
+};
+
typedef struct StackCacheNode StackCacheNode;
struct StackCacheNode
{
runtime·oldstack(void)
{
Stktop *top;
- Gobuf label;
uint32 argsize;
byte *sp, *old;
uintptr *src, *dst, *dstend;
G *gp;
int64 goid;
-//printf("oldstack m->cret=%p\n", m->cret);
-
gp = m->curg;
top = (Stktop*)gp->stackbase;
old = (byte*)gp->stackguard - StackGuard;
sp = (byte*)top;
argsize = top->argsize;
+
+ if(StackDebug) {
+ runtime·printf("runtime: oldstack gobuf={pc:%p sp:%p lr:%p} cret=%p argsize=%p\n",
+ top->gobuf.pc, top->gobuf.sp, top->gobuf.lr, m->cret, (uintptr)argsize);
+ }
+
+ gp->sched = top->gobuf;
+ gp->sched.ret = m->cret;
+ m->cret = 0; // drop reference
+ gp->status = Gwaiting;
+ gp->waitreason = "stack unsplit";
+
if(argsize > 0) {
sp -= argsize;
dst = (uintptr*)top->argp;
goid = top->gobuf.g->goid; // fault if g is bad, before gogo
USED(goid);
- label = top->gobuf;
gp->stackbase = top->stackbase;
gp->stackguard = top->stackguard;
gp->stackguard0 = gp->stackguard;
+
if(top->free != 0)
runtime·stackfree(old, top->free);
- label.ret = m->cret;
- m->cret = 0; // drop reference
- runtime·gogo(&label);
+ gp->status = Grunning;
+ runtime·gogo(&gp->sched);
}
// Called from reflect·call or from runtime·morestack when a new
framesize = m->moreframesize;
argsize = m->moreargsize;
gp = m->curg;
+ gp->status = Gwaiting;
+ gp->waitreason = "stack split";
+ reflectcall = framesize==1;
+
+ if(!reflectcall)
+ runtime·rewindmorestack(&gp->sched);
- if(m->morebuf.sp < gp->stackguard - StackGuard) {
- runtime·printf("runtime: split stack overflow: %p < %p\n", m->morebuf.sp, gp->stackguard - StackGuard);
+ sp = m->morebuf.sp;
+ if(thechar == '6' || thechar == '8') {
+ // The call to morestack cost a word.
+ sp -= sizeof(uintptr);
+ }
+ if(StackDebug || sp < gp->stackguard - StackGuard) {
+ runtime·printf("runtime: newstack framesize=%p argsize=%p sp=%p stack=[%p, %p]\n"
+ "\tmorebuf={pc:%p sp:%p lr:%p}\n"
+ "\tsched={pc:%p sp:%p lr:%p ctxt:%p}\n",
+ (uintptr)framesize, (uintptr)argsize, sp, gp->stackguard - StackGuard, gp->stackbase,
+ m->morebuf.pc, m->morebuf.sp, m->morebuf.lr,
+ gp->sched.pc, gp->sched.sp, gp->sched.lr, gp->sched.ctxt);
+ }
+ if(sp < gp->stackguard - StackGuard) {
+ runtime·printf("runtime: split stack overflow: %p < %p\n", sp, gp->stackguard - StackGuard);
runtime·throw("runtime: split stack overflow");
}
+
if(argsize % sizeof(uintptr) != 0) {
runtime·printf("runtime: stack split with misaligned argsize %d\n", argsize);
runtime·throw("runtime: stack split argsize");
free = framesize;
}
- if(0) {
- runtime·printf("newstack framesize=%d argsize=%d morepc=%p moreargp=%p gobuf=%p, %p top=%p old=%p\n",
- framesize, argsize, m->morepc, m->moreargp, m->morebuf.pc, m->morebuf.sp, top, gp->stackbase);
+ if(StackDebug) {
+ runtime·printf("\t-> new stack [%p, %p]\n", stk, top);
}
top->stackbase = gp->stackbase;
top->free = free;
m->moreargp = nil;
m->morebuf.pc = (uintptr)nil;
+ m->morebuf.lr = (uintptr)nil;
m->morebuf.sp = (uintptr)nil;
// copy flag from panic
label.pc = (uintptr)runtime·lessstack;
label.g = m->curg;
if(reflectcall)
- runtime·gostartcallfn(&label, (FuncVal*)m->morepc);
+ runtime·gostartcallfn(&label, (FuncVal*)m->cret);
else {
- // The stack growth code saves ctxt (not ret) in m->cret.
- runtime·gostartcall(&label, m->morepc, (void*)m->cret);
- m->cret = 0;
+ runtime·gostartcall(&label, (void(*)(void))gp->sched.pc, gp->sched.ctxt);
+ gp->sched.ctxt = nil;
}
+ gp->status = Grunning;
runtime·gogo(&label);
*(int32*)345 = 123; // never return
StackMin = 4096,
FixedStack = StackMin + StackSystem,
- // Functions that need frames bigger than this call morestack
- // unconditionally. That is, on entry to a function it is assumed
- // that the amount of space available in the current stack segment
- // couldn't possibly be bigger than StackBig. If stack segments
- // do run with more space than StackBig, the space may not be
- // used efficiently. As a result, StackBig should not be significantly
- // smaller than StackMin or StackExtra.
+ // Functions that need frames bigger than this use an extra
+ // instruction to do the stack split check, to avoid overflow
+ // in case SP - framesize wraps below zero.
+ // This value can be no bigger than the size of the unmapped
+ // space at zero.
StackBig = 4096,
// The stack guard is a pointer this many bytes above the
}
bool
-runtime·showframe(Func *f, bool current)
+runtime·showframe(Func *f, G *gp)
{
static int32 traceback = -1;
- if(current && m->throwing > 0)
+ if(m->throwing && gp != nil && (gp == m->curg || gp == m->caughtsig))
return 1;
if(traceback < 0)
traceback = runtime·gotraceback(nil);
gobuf->pc = (uintptr)fn;
gobuf->ctxt = ctxt;
}
+
+// Called to rewind context saved during morestack back to beginning of function.
+// To help us, the linker emits a jmp back to the beginning right after the
+// call to morestack. We just have to decode and apply that jump.
+void
+runtime·rewindmorestack(Gobuf *gobuf)
+{
+ uint32 inst;
+
+ inst = *(uint32*)gobuf->pc;
+ if((gobuf->pc&3) == 0 && (inst>>24) == 0x9a) {
+ //runtime·printf("runtime: rewind pc=%p to pc=%p\n", gobuf->pc, gobuf->pc + ((int32)(inst<<8)>>6) + 8);
+ gobuf->pc += ((int32)(inst<<8)>>6) + 8;
+ return;
+ }
+ runtime·printf("runtime: pc=%p %x\n", gobuf->pc, inst);
+ runtime·throw("runtime: misuse of rewindmorestack");
+}
gobuf->pc = (uintptr)fn;
gobuf->ctxt = ctxt;
}
+
+// Called to rewind context saved during morestack back to beginning of function.
+// To help us, the linker emits a jmp back to the beginning right after the
+// call to morestack. We just have to decode and apply that jump.
+void
+runtime·rewindmorestack(Gobuf *gobuf)
+{
+ byte *pc;
+
+ pc = (byte*)gobuf->pc;
+ if(pc[0] == 0xe9) { // jmp 4-byte offset
+ gobuf->pc = gobuf->pc + 5 + *(int32*)(pc+1);
+ return;
+ }
+ if(pc[0] == 0xeb) { // jmp 1-byte offset
+ gobuf->pc = gobuf->pc + 2 + *(int8*)(pc+1);
+ return;
+ }
+ runtime·printf("runtime: pc=%p %x %x %x %x %x\n", pc, pc[0], pc[1], pc[2], pc[3], pc[4]);
+ runtime·throw("runtime: misuse of rewindmorestack");
+}
void runtime·deferproc(void);
void runtime·newproc(void);
-void runtime·newstack(void);
void runtime·morestack(void);
void runtime·sigpanic(void);
void _div(void);
void _modu(void);
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, bool printall)
{
- int32 i, n, skip0;
+ int32 i, n, nprint, skip0;
uintptr x, tracepc;
bool waspanic, printing;
Func *f, *f2;
skip0 = skip;
+ nprint = 0;
runtime·memclr((byte*)&frame, sizeof frame);
frame.pc = pc0;
frame.lr = lr0;
frame.sp = stk->gobuf.sp;
frame.lr = 0;
frame.fp = 0;
- if(printing && runtime·showframe(nil, gp == m->curg))
+ if(printing && runtime·showframe(nil, gp))
runtime·printf("----- stack segment boundary -----\n");
stk = (Stktop*)stk->stackbase;
continue;
frame.arglen = f2->frame; // conservative overestimate
else {
runtime·printf("runtime: unknown argument frame size for %S\n", f->name);
- runtime·throw("invalid stack");
+ if(!printing)
+ runtime·throw("invalid stack");
}
// Derive location and size of local variables.
if(callback != nil)
callback(&frame, v);
if(printing) {
- if(runtime·showframe(f, gp == m->curg)) {
+ if(printall || runtime·showframe(f, gp)) {
// Print during crash.
// main(0x1, 0x2, 0x3)
// /home/rsc/go/src/runtime/x.go:23 +0xf
tracepc = frame.pc; // back up to CALL instruction for funcline.
if(n > 0 && frame.pc > f->entry && !waspanic)
tracepc -= sizeof(uintptr);
- if(m->throwing && gp == m->curg)
- runtime·printf("[fp=%p] ", frame.fp);
runtime·printf("%S(", f->name);
for(i = 0; i < f->args/sizeof(uintptr); i++) {
if(i != 0)
runtime·printf("\t%S:%d", f->src, runtime·funcline(f, tracepc));
if(frame.pc > f->entry)
runtime·printf(" +%p", (uintptr)(frame.pc - f->entry));
+ if(m->throwing && gp == m->curg)
+ runtime·printf(" fp=%p", frame.fp);
runtime·printf("\n");
+ nprint++;
}
}
n++;
skipped:
waspanic = f->entry == (uintptr)runtime·sigpanic;
- if(printing && f->entry == (uintptr)runtime·newstack && gp == m->g0) {
- runtime·printf("----- newstack called from goroutine %D -----\n", m->curg->goid);
- frame.pc = (uintptr)m->morepc;
- frame.sp = (uintptr)m->moreargp - sizeof(void*);
- frame.lr = m->morebuf.pc;
- frame.fp = m->morebuf.sp;
- gp = m->curg;
- stk = (Stktop*)gp->stackbase;
- continue;
- }
-
- if(printing && f->entry == (uintptr)runtime·lessstack && gp == m->g0) {
- runtime·printf("----- lessstack called from goroutine %D -----\n", m->curg->goid);
- gp = m->curg;
- stk = (Stktop*)gp->stackbase;
- frame.sp = stk->gobuf.sp;
- frame.pc = stk->gobuf.pc;
- frame.fp = 0;
- frame.lr = 0;
- continue;
- }
-
// Do not unwind past the bottom of the stack.
if(frame.pc == (uintptr)runtime·goexit || f->entry == (uintptr)runtime·mstart || f->entry == (uintptr)_rt0_go)
break;
}
}
- if(printing && (frame.pc = gp->gopc) != 0 && (f = runtime·findfunc(frame.pc)) != nil
- && runtime·showframe(f, gp == m->curg) && gp->goid != 1) {
+ if(pcbuf == nil && callback == nil)
+ n = nprint;
+
+ return n;
+}
+
+static void
+printcreatedby(G *gp)
+{
+ uintptr pc, tracepc;
+ Func *f;
+
+ if((pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil
+ && runtime·showframe(f, gp) && gp->goid != 1) {
runtime·printf("created by %S\n", f->name);
- tracepc = frame.pc; // back up to CALL instruction for funcline.
- if(n > 0 && frame.pc > f->entry)
+ tracepc = pc; // back up to CALL instruction for funcline.
+ if(pc > f->entry)
tracepc -= sizeof(uintptr);
runtime·printf("\t%S:%d", f->src, runtime·funcline(f, tracepc));
- if(frame.pc > f->entry)
- runtime·printf(" +%p", (uintptr)(frame.pc - f->entry));
+ if(pc > f->entry)
+ runtime·printf(" +%p", (uintptr)(pc - f->entry));
runtime·printf("\n");
}
-
- return n;
}
void
sp = gp->sched.sp;
lr = 0;
}
- runtime·gentraceback(pc, sp, lr, gp, 0, nil, 100, nil, nil);
+
+ // Print traceback. By default, omits runtime frames.
+ // If that means we print nothing at all, repeat forcing all frames printed.
+ if(runtime·gentraceback(pc, sp, lr, gp, 0, nil, 100, nil, nil, false) == 0)
+ runtime·gentraceback(pc, sp, lr, gp, 0, nil, 100, nil, nil, true);
+ printcreatedby(gp);
}
// func caller(n int) (pc uintptr, file string, line int, ok bool)
sp = runtime·getcallersp(&skip);
pc = (uintptr)runtime·getcallerpc(&skip);
- return runtime·gentraceback(pc, sp, 0, g, skip, pcbuf, m, nil, nil);
+ return runtime·gentraceback(pc, sp, 0, g, skip, pcbuf, m, nil, nil, false);
}
void runtime·deferproc(void);
void runtime·newproc(void);
-void runtime·newstack(void);
void runtime·morestack(void);
void runtime·sigpanic(void);
// collector (callback != nil). A little clunky to merge these, but avoids
// duplicating the code and all its subtlety.
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, bool printall)
{
- int32 i, n, sawnewstack;
+ int32 i, n, nprint;
uintptr tracepc;
bool waspanic, printing;
Func *f, *f2;
USED(lr0);
+ nprint = 0;
runtime·memclr((byte*)&frame, sizeof frame);
frame.pc = pc0;
frame.sp = sp0;
}
n = 0;
- sawnewstack = 0;
stk = (Stktop*)gp->stackbase;
while(n < max) {
// Typically:
frame.sp = stk->gobuf.sp;
frame.lr = 0;
frame.fp = 0;
- if(printing && runtime·showframe(nil, gp == m->curg))
+ if(printing && runtime·showframe(nil, gp))
runtime·printf("----- stack segment boundary -----\n");
stk = (Stktop*)stk->stackbase;
continue;
frame.arglen = f2->frame; // conservative overestimate
else {
runtime·printf("runtime: unknown argument frame size for %S\n", f->name);
- runtime·throw("invalid stack");
+ if(!printing)
+ runtime·throw("invalid stack");
}
// Derive location and size of local variables.
if(callback != nil)
callback(&frame, v);
if(printing) {
- if(runtime·showframe(f, gp == m->curg)) {
+ if(printall || runtime·showframe(f, gp)) {
// Print during crash.
// main(0x1, 0x2, 0x3)
// /home/rsc/go/src/runtime/x.go:23 +0xf
tracepc = frame.pc; // back up to CALL instruction for funcline.
if(n > 0 && frame.pc > f->entry && !waspanic)
tracepc--;
- if(m->throwing && gp == m->curg)
- runtime·printf("[fp=%p] ", frame.fp);
runtime·printf("%S(", f->name);
for(i = 0; i < f->args/sizeof(uintptr); i++) {
if(i != 0)
runtime·printf("\t%S:%d", f->src, runtime·funcline(f, tracepc));
if(frame.pc > f->entry)
runtime·printf(" +%p", (uintptr)(frame.pc - f->entry));
+ if(m->throwing && gp == m->curg)
+ runtime·printf(" fp=%p", frame.fp);
runtime·printf("\n");
+ nprint++;
}
}
n++;
if(f->entry == (uintptr)runtime·deferproc || f->entry == (uintptr)runtime·newproc)
frame.fp += 2*sizeof(uintptr);
- if(f->entry == (uintptr)runtime·newstack)
- sawnewstack = 1;
-
- if(printing && f->entry == (uintptr)runtime·morestack && gp == m->g0 && sawnewstack) {
- // The fact that we saw newstack means that morestack
- // has managed to record its information in m, so we can
- // use it to keep unwinding the stack.
- runtime·printf("----- morestack called from goroutine %D -----\n", m->curg->goid);
- frame.pc = (uintptr)m->morepc;
- frame.sp = m->morebuf.sp - sizeof(void*);
- frame.lr = m->morebuf.pc;
- frame.fp = m->morebuf.sp;
- sawnewstack = 0;
- gp = m->curg;
- stk = (Stktop*)gp->stackbase;
- continue;
- }
-
- if(printing && f->entry == (uintptr)runtime·lessstack && gp == m->g0) {
- // Lessstack is running on scheduler stack. Switch to original goroutine.
- runtime·printf("----- lessstack called from goroutine %D -----\n", m->curg->goid);
- gp = m->curg;
- stk = (Stktop*)gp->stackbase;
- frame.sp = stk->gobuf.sp;
- frame.pc = stk->gobuf.pc;
- frame.fp = 0;
- frame.lr = 0;
- continue;
- }
-
// Do not unwind past the bottom of the stack.
if(frame.pc == (uintptr)runtime·goexit || f->entry == (uintptr)runtime·mstart || f->entry == (uintptr)_rt0_go)
break;
frame.fp = 0;
}
+ if(pcbuf == nil && callback == nil)
+ n = nprint;
+
+ return n;
+}
+
+static void
+printcreatedby(G *gp)
+{
+ uintptr pc, tracepc;
+ Func *f;
+
// Show what created goroutine, except main goroutine (goid 1).
- if(printing && (frame.pc = gp->gopc) != 0 && (f = runtime·findfunc(frame.pc)) != nil
- && runtime·showframe(f, gp == m->curg) && gp->goid != 1) {
+ if((pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil && gp->goid != 1) {
runtime·printf("created by %S\n", f->name);
- tracepc = frame.pc; // back up to CALL instruction for funcline.
- if(n > 0 && frame.pc > f->entry)
+ tracepc = pc; // back up to CALL instruction for funcline.
+ if(pc > f->entry)
tracepc--;
runtime·printf("\t%S:%d", f->src, runtime·funcline(f, tracepc));
- if(frame.pc > f->entry)
- runtime·printf(" +%p", (uintptr)(frame.pc - f->entry));
+ if(pc > f->entry)
+ runtime·printf(" +%p", (uintptr)(pc - f->entry));
runtime·printf("\n");
}
-
- return n;
}
void
pc = gp->sched.pc;
sp = gp->sched.sp;
}
- runtime·gentraceback(pc, sp, 0, gp, 0, nil, 100, nil, nil);
+
+ // Print traceback. By default, omits runtime frames.
+ // If that means we print nothing at all, repeat forcing all frames printed.
+ if(runtime·gentraceback(pc, sp, 0, gp, 0, nil, 100, nil, nil, false) == 0)
+ runtime·gentraceback(pc, sp, 0, gp, 0, nil, 100, nil, nil, true);
+ printcreatedby(gp);
}
int32
sp = runtime·getcallersp(&skip);
pc = (uintptr)runtime·getcallerpc(&skip);
- return runtime·gentraceback(pc, sp, 0, g, skip, pcbuf, m, nil, nil);
+ return runtime·gentraceback(pc, sp, 0, g, skip, pcbuf, m, nil, nil, false);
}