if pc != f.entry() {
pc--
}
- up := pcdatavalue(f, abi.PCDATA_UnsafePoint, pc)
+ up := pcdatavalue(f, abi.PCDATA_UnsafePoint, pc, nil)
if up != abi.UnsafePointSafe {
// Not at a safe point.
ret = debugCallUnsafePoint
pcdata := int32(-1) // Use the entry map at function entry
if pc != f.entry() {
pc--
- pcdata = pcdatavalue(f, abi.PCDATA_StackMapIndex, pc)
+ pcdata = pcdatavalue(f, abi.PCDATA_StackMapIndex, pc, nil)
}
if pcdata == -1 {
// We do not have a valid pcdata value but there might be a
}
}
if found {
- locals, _, _ := u.frame.getStackMap(false)
+ locals, _, _ := u.frame.getStackMap(nil, false)
if locals.n == 0 {
return
}
return
}
- locals, args, objs := frame.getStackMap(false)
+ locals, args, objs := frame.getStackMap(&state.cache, false)
// Scan local variables if stack frame has been allocated.
if locals.n > 0 {
// A stackScanState keeps track of the state used during the GC walk
// of a goroutine.
type stackScanState struct {
+ cache pcvalueCache
+
// stack limits
stack stack
// Not Go code.
return false, 0
}
- if (GOARCH == "mips" || GOARCH == "mipsle" || GOARCH == "mips64" || GOARCH == "mips64le") && lr == pc+8 && funcspdelta(f, pc) == 0 {
+ if (GOARCH == "mips" || GOARCH == "mipsle" || GOARCH == "mips64" || GOARCH == "mips64le") && lr == pc+8 && funcspdelta(f, pc, nil) == 0 {
// We probably stopped at a half-executed CALL instruction,
// where the LR is updated but the PC has not. If we preempt
// here we'll see a seemingly self-recursive call, which is in
return false, 0
}
// Check the inner-most name
- u, uf := newInlineUnwinder(f, pc)
+ u, uf := newInlineUnwinder(f, pc, nil)
name := u.srcFunc(uf).name()
if hasPrefix(name, "runtime.") ||
hasPrefix(name, "runtime/internal/") ||
pc := ctx.pc
fi := findfunc(pc)
if fi.valid() {
- u, uf := newInlineUnwinder(fi, pc)
+ u, uf := newInlineUnwinder(fi, pc, nil)
for ; uf.valid(); uf = u.next(uf) {
sf := u.srcFunc(uf)
if sf.funcID == abi.FuncIDWrapper && u.isInlined(uf) {
type adjustinfo struct {
old stack
delta uintptr // ptr distance from old to new stack (newbase - oldbase)
+ cache pcvalueCache
// sghi is the highest sudog.elem on the stack.
sghi uintptr
adjustpointer(adjinfo, unsafe.Pointer(frame.varp))
}
- locals, args, objs := frame.getStackMap(true)
+ locals, args, objs := frame.getStackMap(&adjinfo.cache, true)
// Adjust local variables if stack frame has been allocated.
if locals.n > 0 {
// getStackMap returns the locals and arguments live pointer maps, and
// stack object list for frame.
-func (frame *stkframe) getStackMap(debug bool) (locals, args bitvector, objs []stackObjectRecord) {
+func (frame *stkframe) getStackMap(cache *pcvalueCache, debug bool) (locals, args bitvector, objs []stackObjectRecord) {
targetpc := frame.continpc
if targetpc == 0 {
// Frame is dead. Return empty bitvectors.
// the first instruction of the function changes the
// stack map.
targetpc--
- pcdata = pcdatavalue(f, abi.PCDATA_StackMapIndex, targetpc)
+ pcdata = pcdatavalue(f, abi.PCDATA_StackMapIndex, targetpc, cache)
}
if pcdata == -1 {
// We do not have a valid pcdata value but there might be a
}
// It's important that interpret pc non-strictly as cgoTraceback may
// have added bogus PCs with a valid funcInfo but invalid PCDATA.
- u, uf := newInlineUnwinder(funcInfo, pc)
+ u, uf := newInlineUnwinder(funcInfo, pc, nil)
sf := u.srcFunc(uf)
if u.isInlined(uf) {
// Note: entry is not modified. It always refers to a real frame, not an inlined one.
if !f.funcInfo.valid() {
return f.Function
}
- u, uf := newInlineUnwinder(f.funcInfo, f.PC)
+ u, uf := newInlineUnwinder(f.funcInfo, f.PC, nil)
sf := u.srcFunc(uf)
return sf.name()
}
return stk
}
- u, uf := newInlineUnwinder(f, tracepc)
+ var cache pcvalueCache
+ u, uf := newInlineUnwinder(f, tracepc, &cache)
if !u.isInlined(uf) {
// Nothing inline at tracepc.
return stk
// We just report the preceding function in that situation. See issue 29735.
// TODO: Perhaps we should report no function at all in that case.
// The runtime currently doesn't have function end info, alas.
- u, uf := newInlineUnwinder(f, pc)
+ u, uf := newInlineUnwinder(f, pc, nil)
if !u.isInlined(uf) {
return f._Func()
}
}
// Returns the PCData value, and the PC where this value starts.
-func pcvalue(f funcInfo, off uint32, targetpc uintptr, strict bool) (int32, uintptr) {
+func pcvalue(f funcInfo, off uint32, targetpc uintptr, _ *pcvalueCache, strict bool) (int32, uintptr) {
if off == 0 {
return -1, 0
}
if !f.valid() {
return "?", 0
}
- fileno, _ := pcvalue(f, f.pcfile, targetpc, strict)
- line, _ = pcvalue(f, f.pcln, targetpc, strict)
+ fileno, _ := pcvalue(f, f.pcfile, targetpc, nil, strict)
+ line, _ = pcvalue(f, f.pcln, targetpc, nil, strict)
if fileno == -1 || line == -1 || int(fileno) >= len(datap.filetab) {
// print("looking for ", hex(targetpc), " in ", funcname(f), " got file=", fileno, " line=", lineno, "\n")
return "?", 0
return funcline1(f, targetpc, true)
}
-func funcspdelta(f funcInfo, targetpc uintptr) int32 {
- x, _ := pcvalue(f, f.pcsp, targetpc, true)
+func funcspdelta(f funcInfo, targetpc uintptr, cache *pcvalueCache) int32 {
+ x, _ := pcvalue(f, f.pcsp, targetpc, cache, true)
if debugPcln && x&(goarch.PtrSize-1) != 0 {
print("invalid spdelta ", funcname(f), " ", hex(f.entry()), " ", hex(targetpc), " ", hex(f.pcsp), " ", x, "\n")
throw("bad spdelta")
return *(*uint32)(add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(table)*4))
}
-func pcdatavalue(f funcInfo, table uint32, targetpc uintptr) int32 {
+func pcdatavalue(f funcInfo, table uint32, targetpc uintptr, cache *pcvalueCache) int32 {
if table >= f.npcdata {
return -1
}
- r, _ := pcvalue(f, pcdatastart(f, table), targetpc, true)
+ r, _ := pcvalue(f, pcdatastart(f, table), targetpc, cache, true)
return r
}
-func pcdatavalue1(f funcInfo, table uint32, targetpc uintptr, strict bool) int32 {
+func pcdatavalue1(f funcInfo, table uint32, targetpc uintptr, cache *pcvalueCache, strict bool) int32 {
if table >= f.npcdata {
return -1
}
- r, _ := pcvalue(f, pcdatastart(f, table), targetpc, strict)
+ r, _ := pcvalue(f, pcdatastart(f, table), targetpc, cache, strict)
return r
}
// Like pcdatavalue, but also return the start PC of this PCData value.
+// It doesn't take a cache.
func pcdatavalue2(f funcInfo, table uint32, targetpc uintptr) (int32, uintptr) {
if table >= f.npcdata {
return -1, 0
}
- return pcvalue(f, pcdatastart(f, table), targetpc, true)
+ return pcvalue(f, pcdatastart(f, table), targetpc, nil, true)
}
// funcdata returns a pointer to the ith funcdata for f.
// code.
type inlineUnwinder struct {
f funcInfo
+ cache *pcvalueCache
inlTree *[1 << 20]inlinedCall
}
// This unwinder uses non-strict handling of PC because it's assumed this is
// only ever used for symbolic debugging. If things go really wrong, it'll just
// fall back to the outermost frame.
-func newInlineUnwinder(f funcInfo, pc uintptr) (inlineUnwinder, inlineFrame) {
+func newInlineUnwinder(f funcInfo, pc uintptr, cache *pcvalueCache) (inlineUnwinder, inlineFrame) {
inldata := funcdata(f, abi.FUNCDATA_InlTree)
if inldata == nil {
return inlineUnwinder{f: f}, inlineFrame{pc: pc, index: -1}
}
inlTree := (*[1 << 20]inlinedCall)(inldata)
- u := inlineUnwinder{f: f, inlTree: inlTree}
+ u := inlineUnwinder{f: f, cache: cache, inlTree: inlTree}
return u, u.resolveInternal(pc)
}
pc: pc,
// Conveniently, this returns -1 if there's an error, which is the same
// value we use for the outermost frame.
- index: pcdatavalue1(u.f, abi.PCDATA_InlTreeIndex, pc, false),
+ index: pcdatavalue1(u.f, abi.PCDATA_InlTreeIndex, pc, u.cache, false),
}
}
// Iterate over the PCs in tiuTest and walk the inline stack for each.
prevStack := "x"
+ var cache pcvalueCache
for pc := pc1; pc < pc1+1024 && findfunc(pc) == f; pc += sys.PCQuantum {
stack := ""
- u, uf := newInlineUnwinder(f, pc)
+ u, uf := newInlineUnwinder(f, pc, &cache)
if file, _ := u.fileLine(uf); file == "?" {
// We're probably in the trailing function padding, where findfunc
// still returns f but there's no symbolic information. Just keep
}
var (
+ cache pcvalueCache
lastFuncID = abi.FuncIDNormal
newPCBuf = make([]uintptr, 0, traceStackSize)
skip = pcBuf[0]
continue
}
- u, uf := newInlineUnwinder(fi, callPC)
+ u, uf := newInlineUnwinder(fi, callPC, &cache)
for ; uf.valid(); uf = u.next(uf) {
sf := u.srcFunc(uf)
if sf.funcID == abi.FuncIDWrapper && elideWrapperCalling(lastFuncID) {
// flags are the flags to this unwind. Some of these are updated as we
// unwind (see the flags documentation).
flags unwindFlags
+
+ // cache is used to cache pcvalue lookups.
+ cache pcvalueCache
}
// init initializes u to start unwinding gp's stack and positions the
case abi.FuncID_systemstack:
// systemstack returns normally, so just follow the
// stack transition.
- if usesLR && funcspdelta(f, frame.pc) == 0 {
+ if usesLR && funcspdelta(f, frame.pc, &u.cache) == 0 {
// We're at the function prologue and the stack
// switch hasn't happened, or epilogue where we're
// about to return. Just unwind normally.
flag &^= abi.FuncFlagSPWrite
}
}
- frame.fp = frame.sp + uintptr(funcspdelta(f, frame.pc))
+ frame.fp = frame.sp + uintptr(funcspdelta(f, frame.pc, &u.cache))
if !usesLR {
// On x86, call instruction pushes return PC before entering new function.
frame.fp += goarch.PtrSize
frame.fn = f
if !f.valid() {
frame.pc = x
- } else if funcspdelta(f, frame.pc) == 0 {
+ } else if funcspdelta(f, frame.pc, &u.cache) == 0 {
frame.lr = x
}
}
cgoN := u.cgoCallers(cgoBuf[:])
// TODO: Why does &u.cache cause u to escape? (Same in traceback2)
- for iu, uf := newInlineUnwinder(f, u.symPC()); n < len(pcBuf) && uf.valid(); uf = iu.next(uf) {
+ for iu, uf := newInlineUnwinder(f, u.symPC(), noEscapePtr(&u.cache)); n < len(pcBuf) && uf.valid(); uf = iu.next(uf) {
sf := iu.srcFunc(uf)
if sf.funcID == abi.FuncIDWrapper && elideWrapperCalling(u.calleeFuncID) {
// ignore wrappers
}
liveInfo := funcdata(f, abi.FUNCDATA_ArgLiveInfo)
- liveIdx := pcdatavalue(f, abi.PCDATA_ArgLiveIndex, pc)
+ liveIdx := pcdatavalue(f, abi.PCDATA_ArgLiveIndex, pc, nil)
startOffset := uint8(0xff) // smallest offset that needs liveness info (slots with a lower offset is always live)
if liveInfo != nil {
startOffset = *(*uint8)(liveInfo)
for ; u.valid(); u.next() {
lastN = 0
f := u.frame.fn
- for iu, uf := newInlineUnwinder(f, u.symPC()); uf.valid(); uf = iu.next(uf) {
+ for iu, uf := newInlineUnwinder(f, u.symPC(), noEscapePtr(&u.cache)); uf.valid(); uf = iu.next(uf) {
sf := iu.srcFunc(uf)
callee := u.calleeFuncID
u.calleeFuncID = sf.funcID
// due to only have access to the pcs at the time of the caller
// goroutine being created.
func printAncestorTracebackFuncInfo(f funcInfo, pc uintptr) {
- u, uf := newInlineUnwinder(f, pc)
+ u, uf := newInlineUnwinder(f, pc, nil)
file, line := u.fileLine(uf)
printFuncName(u.srcFunc(uf).name())
print("(...)\n")