From f4e2000fc8e24282455cd579d4a9b3de86fd4ff2 Mon Sep 17 00:00:00 2001 From: aarzilli Date: Thu, 5 Oct 2017 09:26:19 +0200 Subject: [PATCH] proc: refactor stack.go to use DWARF registers Instead of only tracking a few cherrypicked registers in stack.go track all DWARF registers. This is needed for cgo code and for the locationlists emitted by go in 1.10: * The debug_frame sections emitted by C compilers can not be used without tracking all registers * the loclists emitted by go1.10 need all registers of a frame to be interpreted. --- pkg/dwarf/frame/entries.go | 6 - pkg/dwarf/frame/table.go | 103 +++++++++--------- pkg/dwarf/op/op.go | 78 ++++++++----- pkg/dwarf/op/op_test.go | 2 +- pkg/dwarf/op/regs.go | 95 ++++++++++++++++ pkg/dwarf/reader/reader.go | 4 +- pkg/proc/arch.go | 197 +++++++++++++++++++++++++++++++++ pkg/proc/proc.go | 6 +- pkg/proc/proc_test.go | 7 +- pkg/proc/registers.go | 25 ++++- pkg/proc/stack.go | 206 ++++++++++++++++++++++++----------- pkg/proc/threads.go | 22 ++-- pkg/proc/variables.go | 8 +- service/debugger/debugger.go | 2 +- 14 files changed, 587 insertions(+), 174 deletions(-) create mode 100644 pkg/dwarf/op/regs.go diff --git a/pkg/dwarf/frame/entries.go b/pkg/dwarf/frame/entries.go index 92c9caf3..acd791cd 100644 --- a/pkg/dwarf/frame/entries.go +++ b/pkg/dwarf/frame/entries.go @@ -50,12 +50,6 @@ func (fde *FrameDescriptionEntry) EstablishFrame(pc uint64) *FrameContext { return executeDwarfProgramUntilPC(fde, pc) } -// Return the offset from the current SP that the return address is stored at. -func (fde *FrameDescriptionEntry) ReturnAddressOffset(pc uint64) (frameOffset, returnAddressOffset int64) { - frame := fde.EstablishFrame(pc) - return frame.cfa.offset, frame.regs[fde.CIE.ReturnAddressRegister].offset -} - type FrameDescriptionEntries []*FrameDescriptionEntry func NewFrameIndex() FrameDescriptionEntries { diff --git a/pkg/dwarf/frame/table.go b/pkg/dwarf/frame/table.go index 03788f78..012a7020 100644 --- a/pkg/dwarf/frame/table.go +++ b/pkg/dwarf/frame/table.go @@ -8,38 +8,28 @@ import ( "github.com/derekparker/delve/pkg/dwarf/util" ) -type CurrentFrameAddress struct { - register uint64 - offset int64 - expression []byte - rule byte -} - type DWRule struct { - rule byte - offset int64 - newreg uint64 - expression []byte + Rule Rule + Offset int64 + Reg uint64 + Expression []byte } type FrameContext struct { loc uint64 order binary.ByteOrder address uint64 - cfa CurrentFrameAddress - regs map[uint64]DWRule + CFA DWRule + Regs map[uint64]DWRule initialRegs map[uint64]DWRule prevRegs map[uint64]DWRule buf *bytes.Buffer cie *CommonInformationEntry + RetAddrReg uint64 codeAlignment uint64 dataAlignment int64 } -func (fctx *FrameContext) CFAOffset() int64 { - return fctx.cfa.offset -} - // Instructions used to recreate the table from the .debug_frame data. const ( DW_CFA_nop = 0x0 // No ops @@ -73,15 +63,19 @@ const ( ) // Rules defined for register values. +type Rule byte + const ( - rule_undefined = iota - rule_sameval - rule_offset - rule_valoffset - rule_register - rule_expression - rule_valexpression - rule_architectural + RuleUndefined Rule = iota + RuleSameVal + RuleOffset + RuleValOffset + RuleRegister + RuleExpression + RuleValExpression + RuleArchitectural + RuleCFA // Value is rule.Reg + rule.Offset + RuleRegOffset // Value is stored at address rule.Reg + rule.Offset ) const low_6_offset = 0x3f @@ -124,7 +118,8 @@ func executeCIEInstructions(cie *CommonInformationEntry) *FrameContext { copy(initialInstructions, cie.InitialInstructions) frame := &FrameContext{ cie: cie, - regs: make(map[uint64]DWRule), + Regs: make(map[uint64]DWRule), + RetAddrReg: cie.ReturnAddressRegister, initialRegs: make(map[uint64]DWRule), prevRegs: make(map[uint64]DWRule), codeAlignment: cie.CodeAlignmentFactor, @@ -142,9 +137,7 @@ func executeDwarfProgramUntilPC(fde *FrameDescriptionEntry, pc uint64) *FrameCon frame.order = fde.order frame.loc = fde.Begin() frame.address = pc - fdeInstructions := make([]byte, len(fde.Instructions)) - copy(fdeInstructions, fde.Instructions) - frame.ExecuteUntilPC(fdeInstructions) + frame.ExecuteUntilPC(fde.Instructions) return frame } @@ -262,7 +255,7 @@ func offset(frame *FrameContext) { offset, _ = util.DecodeULEB128(frame.buf) ) - frame.regs[uint64(reg)] = DWRule{offset: int64(offset) * frame.dataAlignment, rule: rule_offset} + frame.Regs[uint64(reg)] = DWRule{Offset: int64(offset) * frame.dataAlignment, Rule: RuleOffset} } func restore(frame *FrameContext) { @@ -274,9 +267,9 @@ func restore(frame *FrameContext) { reg := uint64(b & low_6_offset) oldrule, ok := frame.initialRegs[reg] if ok { - frame.regs[reg] = DWRule{offset: oldrule.offset, rule: rule_offset} + frame.Regs[reg] = DWRule{Offset: oldrule.Offset, Rule: RuleOffset} } else { - frame.regs[reg] = DWRule{rule: rule_undefined} + frame.Regs[reg] = DWRule{Rule: RuleUndefined} } } @@ -293,31 +286,31 @@ func offsetextended(frame *FrameContext) { offset, _ = util.DecodeULEB128(frame.buf) ) - frame.regs[reg] = DWRule{offset: int64(offset) * frame.dataAlignment, rule: rule_offset} + frame.Regs[reg] = DWRule{Offset: int64(offset) * frame.dataAlignment, Rule: RuleOffset} } func undefined(frame *FrameContext) { reg, _ := util.DecodeULEB128(frame.buf) - frame.regs[reg] = DWRule{rule: rule_undefined} + frame.Regs[reg] = DWRule{Rule: RuleUndefined} } func samevalue(frame *FrameContext) { reg, _ := util.DecodeULEB128(frame.buf) - frame.regs[reg] = DWRule{rule: rule_sameval} + frame.Regs[reg] = DWRule{Rule: RuleSameVal} } func register(frame *FrameContext) { reg1, _ := util.DecodeULEB128(frame.buf) reg2, _ := util.DecodeULEB128(frame.buf) - frame.regs[reg1] = DWRule{newreg: reg2, rule: rule_register} + frame.Regs[reg1] = DWRule{Reg: reg2, Rule: RuleRegister} } func rememberstate(frame *FrameContext) { - frame.prevRegs = frame.regs + frame.prevRegs = frame.Regs } func restorestate(frame *FrameContext) { - frame.regs = frame.prevRegs + frame.Regs = frame.prevRegs } func restoreextended(frame *FrameContext) { @@ -325,9 +318,9 @@ func restoreextended(frame *FrameContext) { oldrule, ok := frame.initialRegs[reg] if ok { - frame.regs[reg] = DWRule{offset: oldrule.offset, rule: rule_offset} + frame.Regs[reg] = DWRule{Offset: oldrule.Offset, Rule: RuleOffset} } else { - frame.regs[reg] = DWRule{rule: rule_undefined} + frame.Regs[reg] = DWRule{Rule: RuleUndefined} } } @@ -335,32 +328,34 @@ func defcfa(frame *FrameContext) { reg, _ := util.DecodeULEB128(frame.buf) offset, _ := util.DecodeULEB128(frame.buf) - frame.cfa.register = reg - frame.cfa.offset = int64(offset) + frame.CFA.Rule = RuleCFA + frame.CFA.Reg = reg + frame.CFA.Offset = int64(offset) } func defcfaregister(frame *FrameContext) { reg, _ := util.DecodeULEB128(frame.buf) - frame.cfa.register = reg + frame.CFA.Reg = reg } func defcfaoffset(frame *FrameContext) { offset, _ := util.DecodeULEB128(frame.buf) - frame.cfa.offset = int64(offset) + frame.CFA.Offset = int64(offset) } func defcfasf(frame *FrameContext) { reg, _ := util.DecodeULEB128(frame.buf) offset, _ := util.DecodeSLEB128(frame.buf) - frame.cfa.register = reg - frame.cfa.offset = offset * frame.dataAlignment + frame.CFA.Rule = RuleCFA + frame.CFA.Reg = reg + frame.CFA.Offset = offset * frame.dataAlignment } func defcfaoffsetsf(frame *FrameContext) { offset, _ := util.DecodeSLEB128(frame.buf) offset *= frame.dataAlignment - frame.cfa.offset = offset + frame.CFA.Offset = offset } func defcfaexpression(frame *FrameContext) { @@ -369,8 +364,8 @@ func defcfaexpression(frame *FrameContext) { expr = frame.buf.Next(int(l)) ) - frame.cfa.expression = expr - frame.cfa.rule = rule_expression + frame.CFA.Expression = expr + frame.CFA.Rule = RuleExpression } func expression(frame *FrameContext) { @@ -380,7 +375,7 @@ func expression(frame *FrameContext) { expr = frame.buf.Next(int(l)) ) - frame.regs[reg] = DWRule{rule: rule_expression, expression: expr} + frame.Regs[reg] = DWRule{Rule: RuleExpression, Expression: expr} } func offsetextendedsf(frame *FrameContext) { @@ -389,7 +384,7 @@ func offsetextendedsf(frame *FrameContext) { offset, _ = util.DecodeSLEB128(frame.buf) ) - frame.regs[reg] = DWRule{offset: offset * frame.dataAlignment, rule: rule_offset} + frame.Regs[reg] = DWRule{Offset: offset * frame.dataAlignment, Rule: RuleOffset} } func valoffset(frame *FrameContext) { @@ -398,7 +393,7 @@ func valoffset(frame *FrameContext) { offset, _ = util.DecodeULEB128(frame.buf) ) - frame.regs[reg] = DWRule{offset: int64(offset), rule: rule_valoffset} + frame.Regs[reg] = DWRule{Offset: int64(offset), Rule: RuleValOffset} } func valoffsetsf(frame *FrameContext) { @@ -407,7 +402,7 @@ func valoffsetsf(frame *FrameContext) { offset, _ = util.DecodeSLEB128(frame.buf) ) - frame.regs[reg] = DWRule{offset: offset * frame.dataAlignment, rule: rule_valoffset} + frame.Regs[reg] = DWRule{Offset: offset * frame.dataAlignment, Rule: RuleValOffset} } func valexpression(frame *FrameContext) { @@ -417,7 +412,7 @@ func valexpression(frame *FrameContext) { expr = frame.buf.Next(int(l)) ) - frame.regs[reg] = DWRule{rule: rule_valexpression, expression: expr} + frame.Regs[reg] = DWRule{Rule: RuleValExpression, Expression: expr} } func louser(frame *FrameContext) { diff --git a/pkg/dwarf/op/op.go b/pkg/dwarf/op/op.go index 158b0733..b8c465bc 100644 --- a/pkg/dwarf/op/op.go +++ b/pkg/dwarf/op/op.go @@ -17,7 +17,14 @@ const ( DW_OP_plus_uconsts = 0x23 ) -type stackfn func(*bytes.Buffer, []int64, int64) ([]int64, error) +type stackfn func(byte, *context) error + +type context struct { + buf *bytes.Buffer + stack []int64 + + DwarfRegisters +} var oplut = map[byte]stackfn{ DW_OP_call_frame_cfa: callframecfa, @@ -27,58 +34,75 @@ var oplut = map[byte]stackfn{ DW_OP_plus_uconsts: plusuconsts, } -func ExecuteStackProgram(cfa int64, instructions []byte) (int64, error) { - stack := make([]int64, 0, 3) - buf := bytes.NewBuffer(instructions) +func ExecuteStackProgram(regs DwarfRegisters, instructions []byte) (int64, error) { + ctxt := &context{ + buf: bytes.NewBuffer(instructions), + stack: make([]int64, 0, 3), + DwarfRegisters: regs, + } - for opcode, err := buf.ReadByte(); err == nil; opcode, err = buf.ReadByte() { + for { + opcode, err := ctxt.buf.ReadByte() + if err != nil { + break + } fn, ok := oplut[opcode] if !ok { return 0, fmt.Errorf("invalid instruction %#v", opcode) } - stack, err = fn(buf, stack, cfa) + err = fn(opcode, ctxt) if err != nil { return 0, err } } - if len(stack) == 0 { + if len(ctxt.stack) == 0 { return 0, errors.New("empty OP stack") } - return stack[len(stack)-1], nil + return ctxt.stack[len(ctxt.stack)-1], nil } -func callframecfa(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) { - if cfa == 0 { - return stack, fmt.Errorf("Could not retrieve CFA for current PC") +func callframecfa(opcode byte, ctxt *context) error { + if ctxt.CFA == 0 { + return fmt.Errorf("Could not retrieve call frame CFA for current PC") } - return append(stack, int64(cfa)), nil + ctxt.stack = append(ctxt.stack, int64(ctxt.CFA)) + return nil } -func addr(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) { - return append(stack, int64(binary.LittleEndian.Uint64(buf.Next(8)))), nil +func addr(opcode byte, ctxt *context) error { + ctxt.stack = append(ctxt.stack, int64(binary.LittleEndian.Uint64(ctxt.buf.Next(8)))) + return nil } -func plus(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) { +func plus(opcode byte, ctxt *context) error { var ( - slen = len(stack) - digits = stack[slen-2 : slen] - st = stack[:slen-2] + slen = len(ctxt.stack) + digits = ctxt.stack[slen-2 : slen] + st = ctxt.stack[:slen-2] ) - return append(st, digits[0]+digits[1]), nil + ctxt.stack = append(st, digits[0]+digits[1]) + return nil } -func plusuconsts(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) { - slen := len(stack) - num, _ := util.DecodeULEB128(buf) - stack[slen-1] = stack[slen-1] + int64(num) - return stack, nil +func plusuconsts(opcode byte, ctxt *context) error { + slen := len(ctxt.stack) + num, _ := util.DecodeULEB128(ctxt.buf) + ctxt.stack[slen-1] = ctxt.stack[slen-1] + int64(num) + return nil } -func consts(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) { - num, _ := util.DecodeSLEB128(buf) - return append(stack, num), nil +func consts(opcode byte, ctxt *context) error { + num, _ := util.DecodeSLEB128(ctxt.buf) + ctxt.stack = append(ctxt.stack, num) + return nil +} + +func framebase(opcode byte, ctxt *context) error { + num, _ := util.DecodeSLEB128(ctxt.buf) + ctxt.stack = append(ctxt.stack, ctxt.FrameBase+num) + return nil } diff --git a/pkg/dwarf/op/op_test.go b/pkg/dwarf/op/op_test.go index f67c1db0..9127a553 100644 --- a/pkg/dwarf/op/op_test.go +++ b/pkg/dwarf/op/op_test.go @@ -7,7 +7,7 @@ func TestExecuteStackProgram(t *testing.T) { instructions = []byte{DW_OP_consts, 0x1c, DW_OP_consts, 0x1c, DW_OP_plus} expected = int64(56) ) - actual, err := ExecuteStackProgram(0, instructions) + actual, err := ExecuteStackProgram(DwarfRegisters{}, instructions) if err != nil { t.Fatal(err) } diff --git a/pkg/dwarf/op/regs.go b/pkg/dwarf/op/regs.go new file mode 100644 index 00000000..130a4d71 --- /dev/null +++ b/pkg/dwarf/op/regs.go @@ -0,0 +1,95 @@ +package op + +import ( + "bytes" + "encoding/binary" +) + +type DwarfRegisters struct { + CFA int64 + FrameBase int64 + ObjBase int64 + Regs []*DwarfRegister + + ByteOrder binary.ByteOrder + PCRegNum uint64 + SPRegNum uint64 +} + +type DwarfRegister struct { + Uint64Val uint64 + Bytes []byte +} + +// Uint64Val returns the uint64 value of register idx. +func (regs *DwarfRegisters) Uint64Val(idx uint64) uint64 { + reg := regs.Reg(idx) + if reg == nil { + return 0 + } + return regs.Regs[idx].Uint64Val +} + +// Bytes returns the bytes value of register idx, nil if the register is not +// defined. +func (regs *DwarfRegisters) Bytes(idx uint64) []byte { + reg := regs.Reg(idx) + if reg == nil { + return nil + } + if reg.Bytes == nil { + var buf bytes.Buffer + binary.Write(&buf, regs.ByteOrder, reg.Uint64Val) + reg.Bytes = buf.Bytes() + } + return reg.Bytes +} + +// Reg returns register idx or nil if the register is not defined. +func (regs *DwarfRegisters) Reg(idx uint64) *DwarfRegister { + if idx >= uint64(len(regs.Regs)) { + return nil + } + return regs.Regs[idx] +} + +func (regs *DwarfRegisters) PC() uint64 { + return regs.Uint64Val(regs.PCRegNum) +} + +func (regs *DwarfRegisters) SP() uint64 { + return regs.Uint64Val(regs.SPRegNum) +} + +// AddReg adds register idx to regs. +func (regs *DwarfRegisters) AddReg(idx uint64, reg *DwarfRegister) { + if idx >= uint64(len(regs.Regs)) { + newRegs := make([]*DwarfRegister, idx+1) + copy(newRegs, regs.Regs) + regs.Regs = newRegs + } + regs.Regs[idx] = reg +} + +func DwarfRegisterFromUint64(v uint64) *DwarfRegister { + return &DwarfRegister{Uint64Val: v} +} + +func DwarfRegisterFromBytes(bytes []byte) *DwarfRegister { + var v uint64 + switch len(bytes) { + case 1: + v = uint64(bytes[0]) + case 2: + x := binary.LittleEndian.Uint16(bytes) + v = uint64(x) + case 4: + x := binary.LittleEndian.Uint32(bytes) + v = uint64(x) + default: + if len(bytes) >= 8 { + v = binary.LittleEndian.Uint64(bytes[:8]) + } + } + return &DwarfRegister{Uint64Val: v, Bytes: bytes} +} diff --git a/pkg/dwarf/reader/reader.go b/pkg/dwarf/reader/reader.go index cd56b2ad..d275f580 100755 --- a/pkg/dwarf/reader/reader.go +++ b/pkg/dwarf/reader/reader.go @@ -73,7 +73,7 @@ func (reader *Reader) AddrFor(name string) (uint64, error) { if !ok { return 0, fmt.Errorf("type assertion failed") } - addr, err := op.ExecuteStackProgram(0, instructions) + addr, err := op.ExecuteStackProgram(op.DwarfRegisters{}, instructions) if err != nil { return 0, err } @@ -99,7 +99,7 @@ func (reader *Reader) AddrForMember(member string, initialInstructions []byte) ( if !ok { continue } - addr, err := op.ExecuteStackProgram(0, append(initialInstructions, instructions...)) + addr, err := op.ExecuteStackProgram(op.DwarfRegisters{}, append(initialInstructions, instructions...)) return uint64(addr), err } } diff --git a/pkg/proc/arch.go b/pkg/proc/arch.go index 595ee887..e40c21c3 100644 --- a/pkg/proc/arch.go +++ b/pkg/proc/arch.go @@ -1,5 +1,13 @@ package proc +import ( + "encoding/binary" + + "github.com/derekparker/delve/pkg/dwarf/frame" + "github.com/derekparker/delve/pkg/dwarf/op" + "golang.org/x/arch/x86/x86asm" +) + // Arch defines an interface for representing a // CPU architecture. type Arch interface { @@ -7,6 +15,10 @@ type Arch interface { BreakpointInstruction() []byte BreakpointSize() int DerefTLS() bool + FixFrameUnwindContext(*frame.FrameContext) *frame.FrameContext + RegSize(uint64) int + RegistersToDwarfRegisters(Registers) op.DwarfRegisters + GoroutineToDwarfRegisters(*G) op.DwarfRegisters } // AMD64 represents the AMD64 CPU architecture. @@ -19,6 +31,12 @@ type AMD64 struct { goos string } +const ( + amd64DwarfIPRegNum uint64 = 16 + amd64DwarfSPRegNum uint64 = 7 + amd64DwarfBPRegNum uint64 = 6 +) + // AMD64Arch returns an initialized AMD64 // struct. func AMD64Arch(goos string) *AMD64 { @@ -56,3 +74,182 @@ func (a *AMD64) BreakpointSize() int { func (a *AMD64) DerefTLS() bool { return a.goos == "windows" } + +// FixFrameUnwindContext adds default architecture rules to fctxt or returns +// the default frame unwind context if fctxt is nil. +func (a *AMD64) FixFrameUnwindContext(fctxt *frame.FrameContext) *frame.FrameContext { + if fctxt == nil { + // When there's no frame descriptor entry use BP (the frame pointer) instead + // - return register is [bp + a.PtrSize()] (i.e. [cfa-a.PtrSize()]) + // - cfa is bp + a.PtrSize()*2 + // - bp is [bp] (i.e. [cfa-a.PtrSize()*2]) + // - sp is cfa + + return &frame.FrameContext{ + RetAddrReg: amd64DwarfIPRegNum, + Regs: map[uint64]frame.DWRule{ + amd64DwarfIPRegNum: frame.DWRule{ + Rule: frame.RuleOffset, + Offset: int64(-a.PtrSize()), + }, + amd64DwarfBPRegNum: frame.DWRule{ + Rule: frame.RuleOffset, + Offset: int64(-2 * a.PtrSize()), + }, + amd64DwarfSPRegNum: frame.DWRule{ + Rule: frame.RuleValOffset, + Offset: 0, + }, + }, + CFA: frame.DWRule{ + Rule: frame.RuleCFA, + Reg: amd64DwarfBPRegNum, + Offset: int64(2 * a.PtrSize()), + }, + } + } + + // We assume that RBP is the frame pointer and we want to keep it updated, + // so that we can use it to unwind the stack even when we encounter frames + // without descriptor entries. + // If there isn't a rule already we emit one. + if fctxt.Regs[amd64DwarfBPRegNum].Rule == frame.RuleUndefined { + fctxt.Regs[amd64DwarfBPRegNum] = frame.DWRule{ + Rule: frame.RuleRegOffset, + Reg: amd64DwarfBPRegNum, + Offset: 0, + } + } + + return fctxt +} + +// RegSize returns the size (in bytes) of register regnum. +// The mapping between hardware registers and DWARF registers is specified +// in the System V ABI AMD64 Architecture Processor Supplement page 57, +// figure 3.36 +// https://www.uclibc.org/docs/psABI-x86_64.pdf +func (a *AMD64) RegSize(regnum uint64) int { + // XMM registers + if regnum > amd64DwarfIPRegNum && regnum <= 32 { + return 16 + } + // x87 registers + if regnum >= 33 && regnum <= 40 { + return 10 + } + return 8 +} + +// The mapping between hardware registers and DWARF registers is specified +// in the System V ABI AMD64 Architecture Processor Supplement page 57, +// figure 3.36 +// https://www.uclibc.org/docs/psABI-x86_64.pdf + +var asm64DwarfToHardware = map[int]x86asm.Reg{ + 0: x86asm.RAX, + 1: x86asm.RDX, + 2: x86asm.RCX, + 3: x86asm.RBX, + 4: x86asm.RSI, + 5: x86asm.RDI, + 8: x86asm.R8, + 9: x86asm.R9, + 10: x86asm.R10, + 11: x86asm.R11, + 12: x86asm.R12, + 13: x86asm.R13, + 14: x86asm.R14, + 15: x86asm.R15, +} + +var amd64DwarfToName = map[int]string{ + 17: "XMM0", + 18: "XMM1", + 19: "XMM2", + 20: "XMM3", + 21: "XMM4", + 22: "XMM5", + 23: "XMM6", + 24: "XMM7", + 25: "XMM8", + 26: "XMM9", + 27: "XMM10", + 28: "XMM11", + 29: "XMM12", + 30: "XMM13", + 31: "XMM14", + 32: "XMM15", + 33: "ST(0)", + 34: "ST(1)", + 35: "ST(2)", + 36: "ST(3)", + 37: "ST(4)", + 38: "ST(5)", + 39: "ST(6)", + 40: "ST(7)", + 49: "Eflags", + 50: "Es", + 51: "Cs", + 52: "Ss", + 53: "Ds", + 54: "Fs", + 55: "Gs", + 58: "Fs_base", + 59: "Gs_base", + 64: "MXCSR", + 65: "CW", + 66: "SW", +} + +func maxAmd64DwarfRegister() int { + max := int(amd64DwarfIPRegNum) + for i := range asm64DwarfToHardware { + if i > max { + max = i + } + } + for i := range amd64DwarfToName { + if i > max { + max = i + } + } + return max +} + +// RegistersToDwarfRegisters converts hardware registers to the format used +// by the DWARF expression interpreter. +func (a *AMD64) RegistersToDwarfRegisters(regs Registers) op.DwarfRegisters { + dregs := make([]*op.DwarfRegister, maxAmd64DwarfRegister()+1) + + dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(regs.PC()) + dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(regs.SP()) + dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(regs.BP()) + + for dwarfReg, asmReg := range asm64DwarfToHardware { + v, err := regs.Get(int(asmReg)) + if err == nil { + dregs[dwarfReg] = op.DwarfRegisterFromUint64(v) + } + } + + for _, reg := range regs.Slice() { + for dwarfReg, regName := range amd64DwarfToName { + if regName == reg.Name { + dregs[dwarfReg] = op.DwarfRegisterFromBytes(reg.Bytes) + } + } + } + + return op.DwarfRegisters{Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum} +} + +// GoroutineToDwarfRegisters extract the saved DWARF registers from a parked +// goroutine in the format used by the DWARF expression interpreter. +func (a *AMD64) GoroutineToDwarfRegisters(g *G) op.DwarfRegisters { + dregs := make([]*op.DwarfRegister, amd64DwarfIPRegNum+1) + dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(g.PC) + dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(g.SP) + dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(g.BP) + return op.DwarfRegisters{Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum} +} diff --git a/pkg/proc/proc.go b/pkg/proc/proc.go index 02632a64..7d924635 100644 --- a/pkg/proc/proc.go +++ b/pkg/proc/proc.go @@ -290,7 +290,7 @@ func StepOut(dbp Process) error { } sameGCond := SameGoroutineCondition(selg) - retFrameCond := andFrameoffCondition(sameGCond, retframe.CFA-int64(retframe.StackHi)) + retFrameCond := andFrameoffCondition(sameGCond, retframe.Regs.CFA-int64(retframe.StackHi)) var deferpc uint64 = 0 if filepath.Ext(topframe.Current.File) == ".go" { @@ -485,12 +485,12 @@ func ConvertEvalScope(dbp Process, gid, frame int) (*EvalScope, error) { return nil, fmt.Errorf("Frame %d does not exist in goroutine %d", frame, gid) } - PC, CFA := locs[frame].Current.PC, locs[frame].CFA + PC, CFA := locs[frame].Current.PC, locs[frame].Regs.CFA return &EvalScope{PC, CFA, thread, g.variable, dbp.BinInfo(), g.stackhi}, nil } // FrameToScope returns a new EvalScope for this frame func FrameToScope(p Process, frame Stackframe) *EvalScope { - return &EvalScope{frame.Current.PC, frame.CFA, p.CurrentThread(), nil, p.BinInfo(), frame.StackHi} + return &EvalScope{frame.Current.PC, frame.Regs.CFA, p.CurrentThread(), nil, p.BinInfo(), frame.StackHi} } diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index 7dec4f55..8cb5d8d0 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -930,7 +930,7 @@ func TestStacktraceGoroutine(t *testing.T) { if locations[i].Call.Fn != nil { name = locations[i].Call.Fn.Name } - t.Logf("\t%s:%d %s\n", locations[i].Call.File, locations[i].Call.Line, name) + t.Logf("\t%s:%d %s (%#x)\n", locations[i].Call.File, locations[i].Call.Line, name, locations[i].Current.PC) } } } @@ -2763,7 +2763,7 @@ func TestStacktraceWithBarriers(t *testing.T) { if frame.Current.Fn != nil { name = frame.Current.Fn.Name } - t.Logf("\t%s [CFA: %x Ret: %x] at %s:%d", name, frame.CFA, frame.Ret, frame.Current.File, frame.Current.Line) + t.Logf("\t%s [CFA: %x Ret: %x] at %s:%d", name, frame.Regs.CFA, frame.Ret, frame.Current.File, frame.Current.Line) } if !found { @@ -2977,6 +2977,9 @@ func TestIssue893(t *testing.T) { if _, ok := err.(proc.ThreadBlockedError); ok { return } + if _, ok := err.(*proc.NoSourceForPCError); ok { + return + } assertNoError(err, t, "Next") }) } diff --git a/pkg/proc/registers.go b/pkg/proc/registers.go index f70e23c0..78da6903 100644 --- a/pkg/proc/registers.go +++ b/pkg/proc/registers.go @@ -29,26 +29,35 @@ type Registers interface { type Register struct { Name string + Bytes []byte Value string } // AppendWordReg appends a word (16 bit) register to regs. func AppendWordReg(regs []Register, name string, value uint16) []Register { - return append(regs, Register{name, fmt.Sprintf("%#04x", value)}) + var buf bytes.Buffer + binary.Write(&buf, binary.LittleEndian, value) + return append(regs, Register{name, buf.Bytes(), fmt.Sprintf("%#04x", value)}) } // AppendDwordReg appends a double word (32 bit) register to regs. func AppendDwordReg(regs []Register, name string, value uint32) []Register { - return append(regs, Register{name, fmt.Sprintf("%#08x", value)}) + var buf bytes.Buffer + binary.Write(&buf, binary.LittleEndian, value) + return append(regs, Register{name, buf.Bytes(), fmt.Sprintf("%#08x", value)}) } // AppendQwordReg appends a quad word (64 bit) register to regs. func AppendQwordReg(regs []Register, name string, value uint64) []Register { - return append(regs, Register{name, fmt.Sprintf("%#016x", value)}) + var buf bytes.Buffer + binary.Write(&buf, binary.LittleEndian, value) + return append(regs, Register{name, buf.Bytes(), fmt.Sprintf("%#016x", value)}) } func appendFlagReg(regs []Register, name string, value uint64, descr flagRegisterDescr, size int) []Register { - return append(regs, Register{name, descr.Describe(value, size)}) + var buf bytes.Buffer + binary.Write(&buf, binary.LittleEndian, value) + return append(regs, Register{name, buf.Bytes()[:size], descr.Describe(value, size)}) } // AppendEflagReg appends EFLAG register to regs. @@ -114,7 +123,11 @@ func AppendX87Reg(regs []Register, index int, exponent uint16, mantissa uint64) f = sign * math.Ldexp(significand, int(exponent-_EXP_BIAS)) } - return append(regs, Register{fmt.Sprintf("ST(%d)", index), fmt.Sprintf("%#04x%016x\t%g", exponent, mantissa, f)}) + var buf bytes.Buffer + binary.Write(&buf, binary.LittleEndian, exponent) + binary.Write(&buf, binary.LittleEndian, mantissa) + + return append(regs, Register{fmt.Sprintf("ST(%d)", index), buf.Bytes(), fmt.Sprintf("%#04x%016x\t%g", exponent, mantissa, f)}) } // AppendSSEReg appends a 256 bit SSE register to regs. @@ -151,7 +164,7 @@ func AppendSSEReg(regs []Register, name string, xmm []byte) []Register { } fmt.Fprintf(&out, "\tv4_float={ %g %g %g %g }", v4[0], v4[1], v4[2], v4[3]) - return append(regs, Register{name, out.String()}) + return append(regs, Register{name, xmm, out.String()}) } var UnknownRegisterError = errors.New("unknown register") diff --git a/pkg/proc/stack.go b/pkg/proc/stack.go index 544cb11d..e8039775 100644 --- a/pkg/proc/stack.go +++ b/pkg/proc/stack.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/derekparker/delve/pkg/dwarf/frame" + "github.com/derekparker/delve/pkg/dwarf/op" ) // This code is partly adaped from runtime.gentraceback in @@ -28,8 +29,8 @@ type Stackframe struct { Current Location // Address of the call instruction for the function above on the call stack. Call Location - // Start address of the stack frame. - CFA int64 + // Frame registers. + Regs op.DwarfRegisters // High address of the stack. StackHi uint64 // Return address for this stack frame (as read from the stack frame itself). @@ -43,11 +44,11 @@ type Stackframe struct { // ThreadStacktrace returns the stack trace for thread. // Note the locations in the array are return addresses not call addresses. func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) { - regs, err := thread.Registers(false) + regs, err := thread.Registers(true) if err != nil { return nil, err } - it := newStackIterator(thread.BinInfo(), thread, regs.PC(), regs.SP(), regs.BP(), 0, nil, -1) + it := newStackIterator(thread.BinInfo(), thread, thread.BinInfo().Arch.RegistersToDwarfRegisters(regs), 0, nil, -1) return it.stacktrace(depth) } @@ -57,13 +58,13 @@ func (g *G) stackIterator() (*stackIterator, error) { return nil, err } if g.Thread != nil { - regs, err := g.Thread.Registers(false) + regs, err := g.Thread.Registers(true) if err != nil { return nil, err } - return newStackIterator(g.variable.bi, g.Thread, regs.PC(), regs.SP(), regs.BP(), g.stackhi, stkbar, g.stkbarPos), nil + return newStackIterator(g.variable.bi, g.Thread, g.variable.bi.Arch.RegistersToDwarfRegisters(regs), g.stackhi, stkbar, g.stkbarPos), nil } - return newStackIterator(g.variable.bi, g.variable.mem, g.PC, g.SP, 0, g.stackhi, stkbar, g.stkbarPos), nil + return newStackIterator(g.variable.bi, g.variable.mem, g.variable.bi.Arch.GoroutineToDwarfRegisters(g), g.stackhi, stkbar, g.stkbarPos), nil } // Stacktrace returns the stack trace for a goroutine. @@ -87,17 +88,21 @@ func (n NullAddrError) Error() string { // required to iterate and walk the program // stack. type stackIterator struct { - pc, sp, bp uint64 - top bool - atend bool - frame Stackframe - bi *BinaryInfo - mem MemoryReadWriter - err error + pc uint64 + top bool + atend bool + frame Stackframe + bi *BinaryInfo + mem MemoryReadWriter + err error stackhi uint64 stackBarrierPC uint64 stkbar []savedLR + + // regs is the register set for the next frame, callFrameRegs is the + // register set for the call frame of the next frame. + regs, callFrameRegs op.DwarfRegisters } type savedLR struct { @@ -105,27 +110,27 @@ type savedLR struct { val uint64 } -func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, pc, sp, bp, stackhi uint64, stkbar []savedLR, stkbarPos int) *stackIterator { +func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, regs op.DwarfRegisters, stackhi uint64, stkbar []savedLR, stkbarPos int) *stackIterator { stackBarrierFunc := bi.LookupFunc[runtimeStackBarrier] // stack barriers were removed in Go 1.9 var stackBarrierPC uint64 if stackBarrierFunc != nil && stkbar != nil { stackBarrierPC = stackBarrierFunc.Entry - fn := bi.PCToFunc(pc) + fn := bi.PCToFunc(regs.PC()) if fn != nil && fn.Name == runtimeStackBarrier { // We caught the goroutine as it's executing the stack barrier, we must // determine whether or not g.stackPos has already been incremented or not. - if len(stkbar) > 0 && stkbar[stkbarPos].ptr < sp { + if len(stkbar) > 0 && stkbar[stkbarPos].ptr < regs.SP() { // runtime.stackBarrier has not incremented stkbarPos. - } else if stkbarPos > 0 && stkbar[stkbarPos-1].ptr < sp { + } else if stkbarPos > 0 && stkbar[stkbarPos-1].ptr < regs.SP() { // runtime.stackBarrier has incremented stkbarPos. stkbarPos-- } else { - return &stackIterator{err: fmt.Errorf("failed to unwind through stackBarrier at SP %x", sp)} + return &stackIterator{err: fmt.Errorf("failed to unwind through stackBarrier at SP %x", regs.SP())} } } stkbar = stkbar[stkbarPos:] } - return &stackIterator{pc: pc, sp: sp, bp: bp, top: true, bi: bi, mem: mem, err: nil, atend: false, stackhi: stackhi, stackBarrierPC: stackBarrierPC, stkbar: stkbar} + return &stackIterator{pc: regs.PC(), regs: regs, top: true, bi: bi, mem: mem, err: nil, atend: false, stackhi: stackhi, stackBarrierPC: stackBarrierPC, stkbar: stkbar} } // Next points the iterator to the next stack frame. @@ -133,16 +138,8 @@ func (it *stackIterator) Next() bool { if it.err != nil || it.atend { return false } - it.frame, it.err = it.frameInfo(it.pc, it.sp, it.bp, it.top) - if it.err != nil { - if _, nofde := it.err.(*frame.NoFDEForPCError); nofde && !it.top { - it.frame = Stackframe{Current: Location{PC: it.pc, File: "?", Line: -1}, Call: Location{PC: it.pc, File: "?", Line: -1}, CFA: 0, Ret: 0} - it.atend = true - it.err = nil - return true - } - return false - } + ret, retaddr := it.advanceRegs() + it.frame = it.newStackframe(ret, retaddr) if it.frame.Ret <= 0 { it.atend = true @@ -163,16 +160,12 @@ func (it *stackIterator) Next() bool { it.top = false it.pc = it.frame.Ret - it.sp = uint64(it.frame.CFA) - it.bp, _ = readUintRaw(it.mem, uintptr(it.bp), int64(it.bi.Arch.PtrSize())) + it.regs = it.callFrameRegs return true } // Frame returns the frame the iterator is pointing at. func (it *stackIterator) Frame() Stackframe { - if it.err != nil { - panic(it.err) - } return it.frame } @@ -181,42 +174,28 @@ func (it *stackIterator) Err() error { return it.err } -func (it *stackIterator) frameInfo(pc, sp, bp uint64, top bool) (Stackframe, error) { - fde, err := it.bi.frameEntries.FDEForPC(pc) - if _, nofde := err.(*frame.NoFDEForPCError); nofde { - if bp == 0 { - return Stackframe{}, err - } - // When no FDE is available attempt to use BP instead - retaddr := uintptr(int(bp) + it.bi.Arch.PtrSize()) - cfa := int64(retaddr) + int64(it.bi.Arch.PtrSize()) - return it.newStackframe(pc, cfa, retaddr, nil, top) - } - - spoffset, retoffset := fde.ReturnAddressOffset(pc) - cfa := int64(sp) + spoffset - - retaddr := uintptr(cfa + retoffset) - return it.newStackframe(pc, cfa, retaddr, fde, top) -} - -func (it *stackIterator) newStackframe(pc uint64, cfa int64, retaddr uintptr, fde *frame.FrameDescriptionEntry, top bool) (Stackframe, error) { +func (it *stackIterator) newStackframe(ret, retaddr uint64) Stackframe { if retaddr == 0 { - return Stackframe{}, NullAddrError{} + it.err = NullAddrError{} + return Stackframe{} } - f, l, fn := it.bi.PCToLine(pc) - ret, err := readUintRaw(it.mem, retaddr, int64(it.bi.Arch.PtrSize())) - if err != nil { - it.err = err + f, l, fn := it.bi.PCToLine(it.pc) + if fn == nil { + f = "?" + l = -1 } - r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, Ret: ret, addrret: uint64(retaddr), StackHi: it.stackhi} - if !top { - r.Call.File, r.Call.Line, r.Call.Fn = it.bi.PCToLine(pc - 1) + r := Stackframe{Current: Location{PC: it.pc, File: f, Line: l, Fn: fn}, Regs: it.regs, Ret: ret, addrret: retaddr, StackHi: it.stackhi} + if !it.top { + r.Call.File, r.Call.Line, r.Call.Fn = it.bi.PCToLine(it.pc - 1) + if r.Call.Fn == nil { + r.Call.File = "?" + r.Call.Line = -1 + } r.Call.PC = r.Current.PC } else { r.Call = r.Current } - return r, nil + return r } func (it *stackIterator) stacktrace(depth int) ([]Stackframe, error) { @@ -238,3 +217,102 @@ func (it *stackIterator) stacktrace(depth int) ([]Stackframe, error) { } return frames, nil } + +// advanceRegs calculates it.callFrameRegs using it.regs and the frame +// descriptor entry for the current stack frame. +// it.regs.CallFrameCFA is updated. +func (it *stackIterator) advanceRegs() (ret uint64, retaddr uint64) { + fde, err := it.bi.frameEntries.FDEForPC(it.pc) + var framectx *frame.FrameContext + if _, nofde := err.(*frame.NoFDEForPCError); nofde { + framectx = it.bi.Arch.FixFrameUnwindContext(nil) + } else { + framectx = it.bi.Arch.FixFrameUnwindContext(fde.EstablishFrame(it.pc)) + } + + cfareg, err := it.executeFrameRegRule(0, framectx.CFA, 0) + if cfareg == nil { + it.err = fmt.Errorf("CFA becomes undefined at PC %#x", it.pc) + return 0, 0 + } + it.regs.CFA = int64(cfareg.Uint64Val) + + it.callFrameRegs = op.DwarfRegisters{ByteOrder: it.regs.ByteOrder, PCRegNum: it.regs.PCRegNum, SPRegNum: it.regs.SPRegNum} + + // According to the standard the compiler should be responsible for emitting + // rules for the RSP register so that it can then be used to calculate CFA, + // however neither Go nor GCC do this. + // In the following line we copy GDB's behaviour by assuming this is + // implicit. + // See also the comment in dwarf2_frame_default_init in + // $GDB_SOURCE/dwarf2-frame.c + it.callFrameRegs.AddReg(uint64(amd64DwarfSPRegNum), cfareg) + + for i, regRule := range framectx.Regs { + reg, err := it.executeFrameRegRule(i, regRule, it.regs.CFA) + it.callFrameRegs.AddReg(i, reg) + if i == framectx.RetAddrReg { + if reg == nil { + if err == nil { + err = fmt.Errorf("Undefined return address at %#x", it.pc) + } + it.err = err + } else { + ret = reg.Uint64Val + } + retaddr = uint64(it.regs.CFA + regRule.Offset) + } + } + + return ret, retaddr +} + +func (it *stackIterator) executeFrameRegRule(regnum uint64, rule frame.DWRule, cfa int64) (*op.DwarfRegister, error) { + switch rule.Rule { + default: + fallthrough + case frame.RuleUndefined: + return nil, nil + case frame.RuleSameVal: + return it.regs.Reg(regnum), nil + case frame.RuleOffset: + return it.readRegisterAt(regnum, uint64(cfa+rule.Offset)) + case frame.RuleValOffset: + return op.DwarfRegisterFromUint64(uint64(cfa + rule.Offset)), nil + case frame.RuleRegister: + return it.regs.Reg(rule.Reg), nil + case frame.RuleExpression: + v, err := op.ExecuteStackProgram(it.regs, rule.Expression) + if err != nil { + return nil, err + } + return it.readRegisterAt(regnum, uint64(v)) + case frame.RuleValExpression: + v, err := op.ExecuteStackProgram(it.regs, rule.Expression) + if err != nil { + return nil, err + } + return op.DwarfRegisterFromUint64(uint64(v)), nil + case frame.RuleArchitectural: + return nil, errors.New("architectural frame rules are unsupported") + case frame.RuleCFA: + if it.regs.Reg(rule.Reg) == nil { + return nil, nil + } + return op.DwarfRegisterFromUint64(uint64(int64(it.regs.Uint64Val(rule.Reg)) + rule.Offset)), nil + case frame.RuleRegOffset: + if it.regs.Reg(rule.Reg) == nil { + return nil, nil + } + return it.readRegisterAt(regnum, uint64(int64(it.regs.Uint64Val(rule.Reg))+rule.Offset)) + } +} + +func (it *stackIterator) readRegisterAt(regnum uint64, addr uint64) (*op.DwarfRegister, error) { + buf := make([]byte, it.bi.Arch.RegSize(regnum)) + _, err := it.mem.ReadMemory(buf, uintptr(addr)) + if err != nil { + return nil, err + } + return op.DwarfRegisterFromBytes(buf), nil +} diff --git a/pkg/proc/threads.go b/pkg/proc/threads.go index 3e56d97a..c9f9ab3a 100644 --- a/pkg/proc/threads.go +++ b/pkg/proc/threads.go @@ -79,6 +79,14 @@ func topframe(g *G, thread Thread) (Stackframe, Stackframe, error) { } } +type NoSourceForPCError struct { + pc uint64 +} + +func (err *NoSourceForPCError) Error() string { + return fmt.Sprintf("no source for pc %#x", err.pc) +} + // Set breakpoints at every line, and the return address. Also look for // a deferred function and set a breakpoint there too. // If stepInto is true it will also set breakpoints inside all @@ -105,7 +113,7 @@ func next(dbp Process, stepInto bool) error { } if topframe.Current.Fn == nil { - return fmt.Errorf("no source for pc %#x", topframe.Current.PC) + return &NoSourceForPCError{topframe.Current.PC} } success := false @@ -133,8 +141,8 @@ func next(dbp Process, stepInto bool) error { } sameGCond := SameGoroutineCondition(selg) - retFrameCond := andFrameoffCondition(sameGCond, retframe.CFA-int64(retframe.StackHi)) - sameFrameCond := andFrameoffCondition(sameGCond, topframe.CFA-int64(topframe.StackHi)) + retFrameCond := andFrameoffCondition(sameGCond, retframe.Regs.CFA-int64(retframe.StackHi)) + sameFrameCond := andFrameoffCondition(sameGCond, topframe.Regs.CFA-int64(topframe.StackHi)) var sameOrRetFrameCond ast.Expr if sameGCond != nil { sameOrRetFrameCond = &ast.BinaryExpr{ @@ -142,8 +150,8 @@ func next(dbp Process, stepInto bool) error { X: sameGCond, Y: &ast.BinaryExpr{ Op: token.LOR, - X: frameoffCondition(topframe.CFA - int64(topframe.StackHi)), - Y: frameoffCondition(retframe.CFA - int64(retframe.StackHi)), + X: frameoffCondition(topframe.Regs.CFA - int64(topframe.StackHi)), + Y: frameoffCondition(retframe.Regs.CFA - int64(retframe.StackHi)), }, } } @@ -375,7 +383,7 @@ func ThreadScope(thread Thread) (*EvalScope, error) { if len(locations) < 1 { return nil, errors.New("could not decode first frame") } - return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, nil, thread.BinInfo(), 0}, nil + return &EvalScope{locations[0].Current.PC, locations[0].Regs.CFA, thread, nil, thread.BinInfo(), 0}, nil } // GoroutineScope returns an EvalScope for the goroutine running on this thread. @@ -391,7 +399,7 @@ func GoroutineScope(thread Thread) (*EvalScope, error) { if err != nil { return nil, err } - return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, g.variable, thread.BinInfo(), g.stackhi}, nil + return &EvalScope{locations[0].Current.PC, locations[0].Regs.CFA, thread, g.variable, thread.BinInfo(), g.stackhi}, nil } func onRuntimeBreakpoint(thread Thread) bool { diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go index ec479bc1..7d47c9f2 100644 --- a/pkg/proc/variables.go +++ b/pkg/proc/variables.go @@ -130,6 +130,7 @@ type G struct { ID int // Goroutine ID PC uint64 // PC of goroutine when it was parked. SP uint64 // SP of goroutine when it was parked. + BP uint64 // BP of goroutine when it was parked (go >= 1.7). GoPC uint64 // PC of 'go' statement that created this goroutine. WaitReason string // Reason for goroutine being parked. Status uint64 @@ -397,6 +398,10 @@ func (gvar *Variable) parseG() (*G, error) { schedVar := gvar.fieldVariable("sched") pc, _ := constant.Int64Val(schedVar.fieldVariable("pc").Value) sp, _ := constant.Int64Val(schedVar.fieldVariable("sp").Value) + var bp int64 + if bpvar := schedVar.fieldVariable("bp"); bpvar != nil && bpvar.Value != nil { + bp, _ = constant.Int64Val(bpvar.Value) + } id, _ := constant.Int64Val(gvar.fieldVariable("goid").Value) gopc, _ := constant.Int64Val(gvar.fieldVariable("gopc").Value) waitReason := "" @@ -424,6 +429,7 @@ func (gvar *Variable) parseG() (*G, error) { GoPC: uint64(gopc), PC: uint64(pc), SP: uint64(sp), + BP: uint64(bp), WaitReason: waitReason, Status: uint64(status), CurrentLoc: Location{PC: uint64(pc), File: f, Line: l, Fn: fn}, @@ -743,7 +749,7 @@ func (scope *EvalScope) extractVarInfoFromEntry(entry *dwarf.Entry) (*Variable, return nil, fmt.Errorf("type assertion failed") } - addr, err := op.ExecuteStackProgram(scope.CFA, instructions) + addr, err := op.ExecuteStackProgram(op.DwarfRegisters{CFA: scope.CFA}, instructions) if err != nil { return nil, err } diff --git a/service/debugger/debugger.go b/service/debugger/debugger.go index f788c0e3..b5c431b7 100644 --- a/service/debugger/debugger.go +++ b/service/debugger/debugger.go @@ -860,7 +860,7 @@ func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadCo for i := range rawlocs { frame := api.Stackframe{ Location: api.ConvertLocation(rawlocs[i].Call), - FrameOffset: rawlocs[i].CFA - int64(rawlocs[i].StackHi), + FrameOffset: rawlocs[i].Regs.CFA - int64(rawlocs[i].StackHi), } if rawlocs[i].Err != nil { frame.Err = rawlocs[i].Err.Error()