diff options
Diffstat (limited to 'src/runtime/traceback.go')
| -rw-r--r-- | src/runtime/traceback.go | 113 |
1 files changed, 59 insertions, 54 deletions
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index 2601cd697f..53eb689848 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -15,24 +15,9 @@ import ( // The most important fact about a given architecture is whether it uses a link register. // On systems with link registers, the prologue for a non-leaf function stores the // incoming value of LR at the bottom of the newly allocated stack frame. -// On systems without link registers, the architecture pushes a return PC during +// On systems without link registers (x86), the architecture pushes a return PC during // the call instruction, so the return PC ends up above the stack frame. // In this file, the return PC is always called LR, no matter how it was found. -// -// To date, the opposite of a link register architecture is an x86 architecture. -// This code may need to change if some other kind of non-link-register -// architecture comes along. -// -// The other important fact is the size of a pointer: on 32-bit systems the LR -// takes up only 4 bytes on the stack, while on 64-bit systems it takes up 8 bytes. -// Typically this is ptrSize. -// -// As an exception, amd64p32 had ptrSize == 4 but the CALL instruction still -// stored an 8-byte return PC onto the stack. To accommodate this, we used regSize -// as the size of the architecture-pushed return PC. -// -// usesLR is defined below in terms of minFrameSize, which is defined in -// arch_$GOARCH.go. ptrSize and regSize are defined in stubs.go. const usesLR = sys.MinFrameSize > 0 @@ -144,8 +129,8 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in frame.pc = *(*uintptr)(unsafe.Pointer(frame.sp)) frame.lr = 0 } else { - frame.pc = uintptr(*(*sys.Uintreg)(unsafe.Pointer(frame.sp))) - frame.sp += sys.RegSize + frame.pc = uintptr(*(*uintptr)(unsafe.Pointer(frame.sp))) + frame.sp += sys.PtrSize } } @@ -180,6 +165,22 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in break } + // Compute function info flags. + flag := f.flag + if f.funcID == funcID_cgocallback { + // cgocallback does write SP to switch from the g0 to the curg stack, + // but it carefully arranges that during the transition BOTH stacks + // have cgocallback frame valid for unwinding through. + // So we don't need to exclude it with the other SP-writing functions. + flag &^= funcFlag_SPWRITE + } + if frame.pc == pc0 && frame.sp == sp0 && pc0 == gp.syscallpc && sp0 == gp.syscallsp { + // Some Syscall functions write to SP, but they do so only after + // saving the entry PC/SP using entersyscall. + // Since we are using the entry PC/SP, the later SP write doesn't matter. + flag &^= funcFlag_SPWRITE + } + // Found an actual function. // Derive frame pointer and link register. if frame.fp == 0 { @@ -196,6 +197,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in frame.pc = gp.m.curg.sched.pc frame.fn = findfunc(frame.pc) f = frame.fn + flag = f.flag frame.sp = gp.m.curg.sched.sp cgoCtxt = gp.m.curg.cgoCtxt case funcID_systemstack: @@ -203,29 +205,37 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in // stack transition. frame.sp = gp.m.curg.sched.sp cgoCtxt = gp.m.curg.cgoCtxt + flag &^= funcFlag_SPWRITE } } frame.fp = frame.sp + uintptr(funcspdelta(f, frame.pc, &cache)) if !usesLR { // On x86, call instruction pushes return PC before entering new function. - frame.fp += sys.RegSize + frame.fp += sys.PtrSize } } var flr funcInfo - if topofstack(f, gp.m != nil && gp == gp.m.g0) { + if flag&funcFlag_TOPFRAME != 0 { + // This function marks the top of the stack. Stop the traceback. frame.lr = 0 flr = funcInfo{} - } else if usesLR && f.funcID == funcID_jmpdefer { - // jmpdefer modifies SP/LR/PC non-atomically. - // If a profiling interrupt arrives during jmpdefer, - // the stack unwind may see a mismatched register set - // and get confused. Stop if we see PC within jmpdefer - // to avoid that confusion. - // See golang.org/issue/8153. + } else if flag&funcFlag_SPWRITE != 0 { + // The function we are in does a write to SP that we don't know + // how to encode in the spdelta table. Examples include context + // switch routines like runtime.gogo but also any code that switches + // to the g0 stack to run host C code. Since we can't reliably unwind + // the SP (we might not even be on the stack we think we are), + // we stop the traceback here. if callback != nil { - throw("traceback_arm: found jmpdefer when tracing with callback") + // Finding an SPWRITE should only happen for a profiling signal, which can + // arrive at any time. For a GC stack traversal (callback != nil), + // we shouldn't see this case, and we must be sure to walk the + // entire stack or the GC is invalid. So crash. + println("traceback: unexpected SPWRITE function", funcname(f)) + throw("traceback") } frame.lr = 0 + flr = funcInfo{} } else { var lrPtr uintptr if usesLR { @@ -235,8 +245,8 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in } } else { if frame.lr == 0 { - lrPtr = frame.fp - sys.RegSize - frame.lr = uintptr(*(*sys.Uintreg)(unsafe.Pointer(lrPtr))) + lrPtr = frame.fp - sys.PtrSize + frame.lr = uintptr(*(*uintptr)(unsafe.Pointer(lrPtr))) } } flr = findfunc(frame.lr) @@ -266,13 +276,28 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in frame.varp = frame.fp if !usesLR { // On x86, call instruction pushes return PC before entering new function. - frame.varp -= sys.RegSize + frame.varp -= sys.PtrSize } // For architectures with frame pointers, if there's // a frame, then there's a saved frame pointer here. - if frame.varp > frame.sp && (GOARCH == "amd64" || GOARCH == "arm64") { - frame.varp -= sys.RegSize + // + // NOTE: This code is not as general as it looks. + // On x86, the ABI is to save the frame pointer word at the + // top of the stack frame, so we have to back down over it. + // On arm64, the frame pointer should be at the bottom of + // the stack (with R29 (aka FP) = RSP), in which case we would + // not want to do the subtraction here. But we started out without + // any frame pointer, and when we wanted to add it, we didn't + // want to break all the assembly doing direct writes to 8(RSP) + // to set the first parameter to a called function. + // So we decided to write the FP link *below* the stack pointer + // (with R29 = RSP - 8 in Go functions). + // This is technically ABI-compatible but not standard. + // And it happens to end up mimicking the x86 layout. + // Other architectures may make different decisions. + if frame.varp > frame.sp && framepointer_enabled { + frame.varp -= sys.PtrSize } // Derive size of arguments. @@ -490,11 +515,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in // before faking a call. if usesLR && injectedCall { x := *(*uintptr)(unsafe.Pointer(frame.sp)) - frame.sp += sys.MinFrameSize - if GOARCH == "arm64" { - // arm64 needs 16-byte aligned SP, always - frame.sp += sys.PtrSize - } + frame.sp += alignUp(sys.MinFrameSize, sys.StackAlign) f = findfunc(frame.pc) frame.fn = f if !f.valid() { @@ -1000,22 +1021,6 @@ func tracebackHexdump(stk stack, frame *stkframe, bad uintptr) { }) } -// Does f mark the top of a goroutine stack? -func topofstack(f funcInfo, g0 bool) bool { - return f.funcID == funcID_goexit || - f.funcID == funcID_mstart || - f.funcID == funcID_mcall || - f.funcID == funcID_morestack || - f.funcID == funcID_rt0_go || - f.funcID == funcID_externalthreadhandler || - // asmcgocall is TOS on the system stack because it - // switches to the system stack, but in this case we - // can come back to the regular stack and still want - // to be able to unwind through the call that appeared - // on the regular stack. - (g0 && f.funcID == funcID_asmcgocall) -} - // isSystemGoroutine reports whether the goroutine g must be omitted // in stack dumps and deadlock detector. This is any goroutine that // starts at a runtime.* entry point, except for runtime.main, |
