aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/traceback.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime/traceback.go')
-rw-r--r--src/runtime/traceback.go113
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,