diff --git a/_fixtures/testvariables.go b/_fixtures/testvariables.go index 5863cf82..486f04b6 100644 --- a/_fixtures/testvariables.go +++ b/_fixtures/testvariables.go @@ -31,6 +31,8 @@ func foobar(baz string, bar FooBar) { a9 = (*FooBar)(nil) a10 = a1[2:5] a11 = [3]FooBar{{1, "a"}, {2, "b"}, {3, "c"}} + a12 = []FooBar{{4, "d"}, {5, "e"}} + a13 = []*FooBar{{6, "f"}, {7, "g"}, {8, "h"}} b1 = true b2 = false neg = -1 @@ -46,7 +48,7 @@ func foobar(baz string, bar FooBar) { ) barfoo() - fmt.Println(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, b1, b2, baz, neg, i8, u8, u16, u32, u64, up, f32, i32, bar, f) + fmt.Println(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, b1, b2, baz, neg, i8, u8, u16, u32, u64, up, f32, i32, bar, f) } func main() { diff --git a/proctl/variables.go b/proctl/variables.go index acac18b5..9b48f08e 100644 --- a/proctl/variables.go +++ b/proctl/variables.go @@ -592,11 +592,11 @@ func (thread *ThreadContext) extractValue(instructions []byte, addr int64, typ i return fmt.Sprintf("*%s", val), nil case *dwarf.StructType: - switch t.StructName { - case "string": + switch { + case t.StructName == "string": return thread.readString(ptraddress) - case "[]int": - return thread.readIntSlice(ptraddress, t) + case strings.HasPrefix(t.StructName, "[]"): + return thread.readSlice(ptraddress, t) default: // Recursively call extractValue to grab // the value of all the members of the struct. @@ -660,45 +660,79 @@ func (thread *ThreadContext) readString(addr uintptr) (string, error) { return *(*string)(unsafe.Pointer(&val)), nil } -func (thread *ThreadContext) readIntSlice(addr uintptr, t *dwarf.StructType) (string, error) { - val, err := thread.readMemory(addr, uintptr(24)) +func (thread *ThreadContext) readSlice(addr uintptr, t *dwarf.StructType) (string, error) { + var sliceLen, sliceCap int64 + var arrayAddr uintptr + var arrayType dwarf.Type + for _, f := range t.Field { + switch f.Name { + case "array": + val, err := thread.readMemory(addr+uintptr(f.ByteOffset), ptrsize) + if err != nil { + return "", err + } + arrayAddr = uintptr(binary.LittleEndian.Uint64(val)) + // Dereference array type to get value type + ptrType, ok := f.Type.(*dwarf.PtrType) + if !ok { + return "", fmt.Errorf("Invalid type %s in slice array", f.Type) + } + arrayType = ptrType.Type + case "len": + lstr, err := thread.extractValue(nil, int64(addr+uintptr(f.ByteOffset)), f.Type, true) + if err != nil { + return "", err + } + sliceLen, err = strconv.ParseInt(lstr, 10, 64) + if err != nil { + return "", err + } + case "cap": + cstr, err := thread.extractValue(nil, int64(addr+uintptr(f.ByteOffset)), f.Type, true) + if err != nil { + return "", err + } + sliceCap, err = strconv.ParseInt(cstr, 10, 64) + if err != nil { + return "", err + } + } + } + + stride := arrayType.Size() + if _, ok := arrayType.(*dwarf.PtrType); ok { + stride = int64(ptrsize) + } + vals, err := thread.readArrayValues(arrayAddr, sliceLen, stride, arrayType) if err != nil { return "", err } - a := binary.LittleEndian.Uint64(val[:8]) - l := binary.LittleEndian.Uint64(val[8:16]) - c := binary.LittleEndian.Uint64(val[16:24]) - - val, err = thread.readMemory(uintptr(a), uintptr(uint64(ptrsize)*l)) - if err != nil { - return "", err - } - - switch t.StructName { - case "[]int": - members := *(*[]int)(unsafe.Pointer(&val)) - setSliceLength(unsafe.Pointer(&members), int(l)) - return fmt.Sprintf("len: %d cap: %d %d", l, c, members), nil - } - return "", fmt.Errorf("Could not read slice") + return fmt.Sprintf("[]%s len: %d, cap: %d, [%s]", arrayType, sliceLen, sliceCap, strings.Join(vals, ",")), nil } func (thread *ThreadContext) readArray(addr uintptr, t *dwarf.ArrayType) (string, error) { - vals := make([]string, 0) - - stride := t.ByteSize / t.Count - for i := int64(0); i < t.Count; i++ { - val, err := thread.extractValue(nil, int64(addr+uintptr(i*stride)), t.Type, false) - if err != nil { - return "", err - } - vals = append(vals, val) + vals, err := thread.readArrayValues(addr, t.Count, t.ByteSize/t.Count, t.Type) + if err != nil { + return "", err } return fmt.Sprintf("%s [%s]", t, strings.Join(vals, ",")), nil } +func (thread *ThreadContext) readArrayValues(addr uintptr, count int64, stride int64, t dwarf.Type) ([]string, error) { + vals := make([]string, 0) + + for i := int64(0); i < count; i++ { + val, err := thread.extractValue(nil, int64(addr+uintptr(i*stride)), t, false) + if err != nil { + return nil, err + } + vals = append(vals, val) + } + return vals, nil +} + func (thread *ThreadContext) readInt(addr uintptr, size int64) (string, error) { var n int64 diff --git a/proctl/variables_test.go b/proctl/variables_test.go index e4be7297..76fd3cc7 100644 --- a/proctl/variables_test.go +++ b/proctl/variables_test.go @@ -40,10 +40,12 @@ func TestVariableEvaluation(t *testing.T) { {"a1", "foofoofoofoofoofoo", "struct string", nil}, {"a10", "ofo", "struct string", nil}, {"a11", "[3]main.FooBar [{Baz: 1, Bur: a},{Baz: 2, Bur: b},{Baz: 3, Bur: c}]", "[3]main.FooBar", nil}, + {"a12", "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: d},{Baz: 5, Bur: e}]", "struct []main.FooBar", nil}, + {"a13", "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: f},*{Baz: 7, Bur: g},*{Baz: 8, Bur: h}]", "struct []*main.FooBar", nil}, {"a2", "6", "int", nil}, {"a3", "7.23", "float64", nil}, {"a4", "[2]int [1,2]", "[2]int", nil}, - {"a5", "len: 5 cap: 5 [1 2 3 4 5]", "struct []int", nil}, + {"a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "struct []int", nil}, {"a6", "main.FooBar {Baz: 8, Bur: word}", "main.FooBar", nil}, {"a7", "*main.FooBar {Baz: 5, Bur: strum}", "*main.FooBar", nil}, {"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "main.FooBar2", nil}, @@ -70,7 +72,7 @@ func TestVariableEvaluation(t *testing.T) { } withTestProcess(executablePath, t, func(p *DebuggedProcess) { - pc, _, _ := p.GoSymTable.LineToPC(fp, 48) + pc, _, _ := p.GoSymTable.LineToPC(fp, 50) _, err := p.Break(pc) assertNoError(err, t, "Break() returned an error") @@ -101,7 +103,7 @@ func TestVariableFunctionScoping(t *testing.T) { } withTestProcess(executablePath, t, func(p *DebuggedProcess) { - pc, _, _ := p.GoSymTable.LineToPC(fp, 48) + pc, _, _ := p.GoSymTable.LineToPC(fp, 50) _, err := p.Break(pc) assertNoError(err, t, "Break() returned an error") @@ -168,10 +170,12 @@ func TestLocalVariables(t *testing.T) { {"a1", "foofoofoofoofoofoo", "struct string", nil}, {"a10", "ofo", "struct string", nil}, {"a11", "[3]main.FooBar [{Baz: 1, Bur: a},{Baz: 2, Bur: b},{Baz: 3, Bur: c}]", "[3]main.FooBar", nil}, + {"a12", "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: d},{Baz: 5, Bur: e}]", "struct []main.FooBar", nil}, + {"a13", "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: f},*{Baz: 7, Bur: g},*{Baz: 8, Bur: h}]", "struct []*main.FooBar", nil}, {"a2", "6", "int", nil}, {"a3", "7.23", "float64", nil}, {"a4", "[2]int [1,2]", "[2]int", nil}, - {"a5", "len: 5 cap: 5 [1 2 3 4 5]", "struct []int", nil}, + {"a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "struct []int", nil}, {"a6", "main.FooBar {Baz: 8, Bur: word}", "main.FooBar", nil}, {"a7", "*main.FooBar {Baz: 5, Bur: strum}", "*main.FooBar", nil}, {"a8", "main.FooBar2 {Bur: 10, Baz: feh}", "main.FooBar2", nil}, @@ -195,7 +199,7 @@ func TestLocalVariables(t *testing.T) { } withTestProcess(executablePath, t, func(p *DebuggedProcess) { - pc, _, _ := p.GoSymTable.LineToPC(fp, 48) + pc, _, _ := p.GoSymTable.LineToPC(fp, 50) _, err := p.Break(pc) assertNoError(err, t, "Break() returned an error")