mirror of
https://github.com/go-delve/delve.git
synced 2025-10-28 12:47:22 +08:00
proc/tests: testing apparatus for complex location expressions
This commit is contained in:
220
pkg/proc/dwarf_expr_test.go
Normal file
220
pkg/proc/dwarf_expr_test.go
Normal file
@ -0,0 +1,220 @@
|
||||
// Tests for loading variables that have complex location expressions. They
|
||||
// are only produced for optimized code (for both Go and C) therefore we can
|
||||
// not get the compiler to produce them reliably enough for tests.
|
||||
|
||||
package proc_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/dwarf"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"testing"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/dwarfbuilder"
|
||||
"github.com/derekparker/delve/pkg/dwarf/godwarf"
|
||||
"github.com/derekparker/delve/pkg/dwarf/op"
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
const defaultCFA = 0xc420051d00
|
||||
|
||||
func fakeBinaryInfo(t *testing.T, dwb *dwarfbuilder.Builder) *proc.BinaryInfo {
|
||||
abbrev, aranges, frame, info, line, pubnames, ranges, str, err := dwb.Build()
|
||||
assertNoError(err, t, "dwarbuilder.Build")
|
||||
dwdata, err := dwarf.New(abbrev, aranges, frame, info, line, pubnames, ranges, str)
|
||||
assertNoError(err, t, "creating dwarf")
|
||||
|
||||
bi := proc.NewBinaryInfo("linux", "amd64")
|
||||
bi.LoadFromData(dwdata, frame, line)
|
||||
|
||||
return &bi
|
||||
}
|
||||
|
||||
// fakeMemory implements proc.MemoryReadWriter by reading from a byte slice.
|
||||
// Byte 0 of "data" is at address "base".
|
||||
type fakeMemory struct {
|
||||
base uint64
|
||||
data []byte
|
||||
}
|
||||
|
||||
func newFakeMemory(base uint64, contents ...interface{}) *fakeMemory {
|
||||
mem := &fakeMemory{base: base}
|
||||
var buf bytes.Buffer
|
||||
for _, x := range contents {
|
||||
binary.Write(&buf, binary.LittleEndian, x)
|
||||
}
|
||||
mem.data = buf.Bytes()
|
||||
return mem
|
||||
}
|
||||
|
||||
func (mem *fakeMemory) ReadMemory(data []byte, addr uintptr) (int, error) {
|
||||
if uint64(addr) < mem.base {
|
||||
return 0, fmt.Errorf("read out of bounds %d %#x", len(data), addr)
|
||||
}
|
||||
start := uint64(addr) - mem.base
|
||||
end := uint64(len(data)) + start
|
||||
if end > uint64(len(mem.data)) {
|
||||
panic(fmt.Errorf("read out of bounds %d %#x", len(data), addr))
|
||||
}
|
||||
copy(data, mem.data[start:end])
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
func (mem *fakeMemory) WriteMemory(uintptr, []byte) (int, error) {
|
||||
return 0, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
// fakeRegisters implements op.DwarfRegisters with arbitrary values for each
|
||||
// register.
|
||||
type fakeRegisters struct {
|
||||
regs [32]uint64
|
||||
}
|
||||
|
||||
func (regs *fakeRegisters) Set(i int, val uint64) {
|
||||
regs.regs[i] = val
|
||||
}
|
||||
|
||||
func (regs *fakeRegisters) Get(i int) []byte {
|
||||
var out bytes.Buffer
|
||||
binary.Write(&out, binary.LittleEndian, regs.regs[i])
|
||||
return out.Bytes()
|
||||
}
|
||||
|
||||
func uintExprCheck(t *testing.T, scope *proc.EvalScope, expr string, tgt uint64) {
|
||||
thevar, err := scope.EvalExpression(expr, normalLoadConfig)
|
||||
assertNoError(err, t, fmt.Sprintf("EvalExpression(%s)", expr))
|
||||
if thevar.Unreadable != nil {
|
||||
t.Errorf("variable %q unreadable: %v", expr, thevar.Unreadable)
|
||||
} else {
|
||||
if v, _ := constant.Uint64Val(thevar.Value); v != tgt {
|
||||
t.Errorf("expected value %x got %x for %q", tgt, v, expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dwarfExprCheck(t *testing.T, mem proc.MemoryReadWriter, regs op.DwarfRegisters, bi *proc.BinaryInfo, testCases map[string]uint16) *proc.EvalScope {
|
||||
scope := &proc.EvalScope{PC: 0x40100, CFA: defaultCFA, Mem: mem, Gvar: nil, BinInfo: bi}
|
||||
for name, value := range testCases {
|
||||
uintExprCheck(t, scope, name, uint64(value))
|
||||
}
|
||||
|
||||
return scope
|
||||
}
|
||||
|
||||
func TestDwarfExprRegisters(t *testing.T) {
|
||||
testCases := map[string]uint16{
|
||||
"a": 0x1234,
|
||||
"b": 0x4321,
|
||||
"c": 0x2143,
|
||||
}
|
||||
|
||||
dwb := dwarfbuilder.New()
|
||||
|
||||
uint16off := dwb.BaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
|
||||
|
||||
dwb.Subprogram("main.main", 0x40100, 0x41000)
|
||||
dwb.Attr(dwarf.AttrFrameBase, dwarfbuilder.LocationBlock(op.DW_OP_call_frame_cfa))
|
||||
dwb.Variable("a", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_reg0))
|
||||
dwb.Variable("b", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_fbreg, int(8)))
|
||||
dwb.Variable("c", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_regx, int(1)))
|
||||
dwb.TagClose()
|
||||
|
||||
bi := fakeBinaryInfo(t, dwb)
|
||||
|
||||
mem := newFakeMemory(defaultCFA, uint64(0), uint64(testCases["b"]), uint16(testCases["pair.v"]))
|
||||
var regs fakeRegisters
|
||||
regs.Set(0, uint64(testCases["a"]))
|
||||
regs.Set(1, uint64(testCases["c"]))
|
||||
|
||||
dwarfExprCheck(t, mem, ®s, bi, testCases)
|
||||
}
|
||||
|
||||
func TestDwarfExprComposite(t *testing.T) {
|
||||
testCases := map[string]uint16{
|
||||
"pair.k": 0x8765,
|
||||
"pair.v": 0x5678,
|
||||
}
|
||||
|
||||
const stringVal = "this is a string"
|
||||
|
||||
dwb := dwarfbuilder.New()
|
||||
|
||||
uint16off := dwb.BaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
|
||||
intoff := dwb.BaseType("int", dwarfbuilder.DW_ATE_signed, 8)
|
||||
|
||||
byteoff := dwb.BaseType("uint8", dwarfbuilder.DW_ATE_unsigned, 1)
|
||||
|
||||
byteptroff := dwb.TagOpen(dwarf.TagPointerType, "*uint8")
|
||||
dwb.Attr(godwarf.AttrGoKind, uint8(22))
|
||||
dwb.Attr(dwarf.AttrType, byteoff)
|
||||
dwb.TagClose()
|
||||
|
||||
pairoff := dwb.StructType("main.pair", 4)
|
||||
dwb.Attr(godwarf.AttrGoKind, uint8(25))
|
||||
dwb.Member("k", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(0)))
|
||||
dwb.Member("v", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(2)))
|
||||
dwb.TagClose()
|
||||
|
||||
stringoff := dwb.StructType("string", 16)
|
||||
dwb.Attr(godwarf.AttrGoKind, uint8(24))
|
||||
dwb.Member("str", byteptroff, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(0)))
|
||||
dwb.Member("len", intoff, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(8)))
|
||||
dwb.TagClose()
|
||||
|
||||
dwb.Subprogram("main.main", 0x40100, 0x41000)
|
||||
dwb.Variable("pair", pairoff, dwarfbuilder.LocationBlock(
|
||||
op.DW_OP_reg2, op.DW_OP_piece, uint(2),
|
||||
op.DW_OP_call_frame_cfa, op.DW_OP_consts, int(16), op.DW_OP_plus, op.DW_OP_piece, uint(2)))
|
||||
dwb.Variable("s", stringoff, dwarfbuilder.LocationBlock(
|
||||
op.DW_OP_reg1, op.DW_OP_piece, uint(8),
|
||||
op.DW_OP_reg0, op.DW_OP_piece, uint(8)))
|
||||
dwb.TagClose()
|
||||
|
||||
bi := fakeBinaryInfo(t, dwb)
|
||||
|
||||
mem := newFakeMemory(defaultCFA, uint64(0), uint64(0), uint16(testCases["pair.v"]), stringVal)
|
||||
var regs fakeRegisters
|
||||
regs.Set(0, uint64(len(stringVal)))
|
||||
regs.Set(1, defaultCFA+18)
|
||||
regs.Set(2, uint64(testCases["pair.k"]))
|
||||
|
||||
scope := dwarfExprCheck(t, mem, ®s, bi, testCases)
|
||||
|
||||
thevar, err := scope.EvalExpression("s", normalLoadConfig)
|
||||
assertNoError(err, t, fmt.Sprintf("EvalExpression(s)", "s"))
|
||||
if thevar.Unreadable != nil {
|
||||
t.Errorf("variable \"s\" unreadable: %v", thevar.Unreadable)
|
||||
} else {
|
||||
if v := constant.StringVal(thevar.Value); v != stringVal {
|
||||
t.Errorf("expected value %q got %q", stringVal, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDwarfExprLoclist(t *testing.T) {
|
||||
const before = 0x1234
|
||||
const after = 0x4321
|
||||
|
||||
dwb := dwarfbuilder.New()
|
||||
|
||||
uint16off := dwb.BaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
|
||||
|
||||
dwb.Subprogram("main.main", 0x40100, 0x41000)
|
||||
dwb.Variable("a", uint16off, []dwarfbuilder.LocEntry{
|
||||
{0x40100, 0x40700, dwarfbuilder.LocationBlock(op.DW_OP_call_frame_cfa)},
|
||||
{0x40700, 0x41000, dwarfbuilder.LocationBlock(op.DW_OP_call_frame_cfa, op.DW_OP_consts, int(2), op.DW_OP_plus)},
|
||||
})
|
||||
dwb.TagClose()
|
||||
|
||||
bi := fakeBinaryInfo(t, dwb)
|
||||
|
||||
mem := newFakeMemory(defaultCFA, uint16(before), uint16(after))
|
||||
|
||||
scope := &proc.EvalScope{PC: 0x40100, CFA: defaultCFA, Mem: mem, Gvar: nil, BinInfo: bi}
|
||||
|
||||
uintExprCheck(t, scope, "a", before)
|
||||
scope.PC = 0x40800
|
||||
uintExprCheck(t, scope, "a", after)
|
||||
}
|
||||
Reference in New Issue
Block a user