diff --git a/_fixtures/testvariables.go b/_fixtures/testvariables.go index 486f04b6..365eb6f6 100644 --- a/_fixtures/testvariables.go +++ b/_fixtures/testvariables.go @@ -13,6 +13,11 @@ type FooBar2 struct { Baz string } +type Nest struct { + Level int + Nest *Nest +} + func barfoo() { a1 := "bur" fmt.Println(a1) @@ -45,10 +50,12 @@ func foobar(baz string, bar FooBar) { f32 = float32(1.2) i32 = [2]int32{1, 2} f = barfoo + ms = Nest{0, &Nest{1, &Nest{2, &Nest{3, &Nest{4, nil}}}}} // Test recursion capping + ba = make([]int, 200, 200) // Test array size capping ) barfoo() - 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) + 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, ms, ba) } func main() { diff --git a/proctl/variables.go b/proctl/variables.go index 4cf477dc..d8266f9d 100644 --- a/proctl/variables.go +++ b/proctl/variables.go @@ -13,6 +13,11 @@ import ( "github.com/derekparker/delve/dwarf/reader" ) +const ( + maxVariableRecurse = 1 + maxArrayValues = 64 +) + type Variable struct { Name string Value string @@ -558,6 +563,10 @@ func (thread *ThreadContext) extractVariableDataAddress(entry *dwarf.Entry, read // We execute the stack program described in the DW_OP_* instruction stream, and // then grab the value from the other processes memory. func (thread *ThreadContext) extractValue(instructions []byte, addr int64, typ interface{}, printStructName bool) (string, error) { + return thread.extractValueInternal(instructions, addr, typ, printStructName, 0) +} + +func (thread *ThreadContext) extractValueInternal(instructions []byte, addr int64, typ interface{}, printStructName bool, recurseLevel int) (string, error) { var err error if addr == 0 { @@ -590,7 +599,8 @@ func (thread *ThreadContext) extractValue(instructions []byte, addr int64, typ i return fmt.Sprintf("%s nil", t.String()), nil } - val, err := thread.extractValue(nil, intaddr, t.Type, printStructName) + // Don't increase the recursion level when dereferencing pointers + val, err := thread.extractValueInternal(nil, intaddr, t.Type, printStructName, recurseLevel) if err != nil { return "", err } @@ -605,20 +615,26 @@ func (thread *ThreadContext) extractValue(instructions []byte, addr int64, typ i default: // Recursively call extractValue to grab // the value of all the members of the struct. - fields := make([]string, 0, len(t.Field)) - for _, field := range t.Field { - val, err := thread.extractValue(nil, field.ByteOffset+addr, field.Type, printStructName) - if err != nil { - return "", err - } + if recurseLevel <= maxVariableRecurse { + fields := make([]string, 0, len(t.Field)) + for _, field := range t.Field { + val, err := thread.extractValueInternal(nil, field.ByteOffset+addr, field.Type, printStructName, recurseLevel+1) + if err != nil { + return "", err + } - fields = append(fields, fmt.Sprintf("%s: %s", field.Name, val)) - } - if printStructName { - return fmt.Sprintf("%s {%s}", t.StructName, strings.Join(fields, ", ")), nil - } else { + fields = append(fields, fmt.Sprintf("%s: %s", field.Name, val)) + } + if printStructName { + return fmt.Sprintf("%s {%s}", t.StructName, strings.Join(fields, ", ")), nil + } return fmt.Sprintf("{%s}", strings.Join(fields, ", ")), nil } + // no fields + if printStructName { + return fmt.Sprintf("%s {...}", t.StructName), nil + } + return "{...}", nil } case *dwarf.ArrayType: return thread.readArray(ptraddress, t) @@ -736,6 +752,12 @@ func (thread *ThreadContext) readArrayValues(addr uintptr, count int64, stride i vals := make([]string, 0) for i := int64(0); i < count; i++ { + // Cap number of elements + if i >= maxArrayValues { + vals = append(vals, fmt.Sprintf("...+%d more", count-maxArrayValues)) + break + } + val, err := thread.extractValue(nil, int64(addr+uintptr(i*stride)), t, false) if err != nil { return nil, err diff --git a/proctl/variables_test.go b/proctl/variables_test.go index 76fd3cc7..1f158913 100644 --- a/proctl/variables_test.go +++ b/proctl/variables_test.go @@ -68,11 +68,13 @@ func TestVariableEvaluation(t *testing.T) { {"u8", "255", "uint8", nil}, {"up", "5", "uintptr", nil}, {"f", "main.barfoo", "func()", nil}, + {"ba", "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "struct []int", nil}, + {"ms", "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *main.Nest {...}}}", "main.Nest", nil}, {"NonExistent", "", "", errors.New("could not find symbol value for NonExistent")}, } withTestProcess(executablePath, t, func(p *DebuggedProcess) { - pc, _, _ := p.GoSymTable.LineToPC(fp, 50) + pc, _, _ := p.GoSymTable.LineToPC(fp, 57) _, err := p.Break(pc) assertNoError(err, t, "Break() returned an error") @@ -103,7 +105,7 @@ func TestVariableFunctionScoping(t *testing.T) { } withTestProcess(executablePath, t, func(p *DebuggedProcess) { - pc, _, _ := p.GoSymTable.LineToPC(fp, 50) + pc, _, _ := p.GoSymTable.LineToPC(fp, 57) _, err := p.Break(pc) assertNoError(err, t, "Break() returned an error") @@ -118,7 +120,7 @@ func TestVariableFunctionScoping(t *testing.T) { assertNoError(err, t, "Unable to find variable a1") // Move scopes, a1 exists here by a2 does not - pc, _, _ = p.GoSymTable.LineToPC(fp, 18) + pc, _, _ = p.GoSymTable.LineToPC(fp, 23) _, err = p.Break(pc) assertNoError(err, t, "Break() returned an error") @@ -182,10 +184,12 @@ func TestLocalVariables(t *testing.T) { {"a9", "*main.FooBar nil", "*main.FooBar", nil}, {"b1", "true", "bool", nil}, {"b2", "false", "bool", nil}, + {"ba", "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "struct []int", nil}, {"f", "main.barfoo", "func()", nil}, {"f32", "1.2", "float32", nil}, {"i32", "[2]int32 [1,2]", "[2]int32", nil}, {"i8", "1", "int8", nil}, + {"ms", "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *main.Nest {...}}}", "main.Nest", nil}, {"neg", "-1", "int", nil}, {"u16", "65535", "uint16", nil}, {"u32", "4294967295", "uint32", nil}, @@ -199,7 +203,7 @@ func TestLocalVariables(t *testing.T) { } withTestProcess(executablePath, t, func(p *DebuggedProcess) { - pc, _, _ := p.GoSymTable.LineToPC(fp, 50) + pc, _, _ := p.GoSymTable.LineToPC(fp, 57) _, err := p.Break(pc) assertNoError(err, t, "Break() returned an error")