debug: get values of more types: uintptr, bool, float, complex, pointer, array, and struct.
Also handles the DWARF types TypedefType, CharType, and UcharType. In
program.go, adds more implementations of Var: Pointer, Array, and
Struct. In proxyrpc.go, adds gob registration for those types in an
init() function.
Change-Id: Ie3a6c3a7ef670310f2be6314f27f5c0ddc92a661
Reviewed-on: https://go-review.googlesource.com/10562
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/ogle/demo/ogler/ogler_test.go b/ogle/demo/ogler/ogler_test.go
index 6231783..f3640b6 100644
--- a/ogle/demo/ogler/ogler_test.go
+++ b/ogle/demo/ogler/ogler_test.go
@@ -19,14 +19,20 @@
)
var expectedVarValues = map[string]interface{}{
- `main.Z_int16`: int16(-32321),
- `main.Z_int32`: int32(-1987654321),
- `main.Z_int64`: int64(-9012345678987654321),
- `main.Z_int8`: int8(-121),
- `main.Z_uint16`: uint16(54321),
- `main.Z_uint32`: uint32(3217654321),
- `main.Z_uint64`: uint64(12345678900987654321),
- `main.Z_uint8`: uint8(231),
+ `main.Z_bool_false`: false,
+ `main.Z_bool_true`: true,
+ `main.Z_complex128`: complex128(1.987654321 - 2.987654321i),
+ `main.Z_complex64`: complex64(1.54321 + 2.54321i),
+ `main.Z_float32`: float32(1.54321),
+ `main.Z_float64`: float64(1.987654321),
+ `main.Z_int16`: int16(-32321),
+ `main.Z_int32`: int32(-1987654321),
+ `main.Z_int64`: int64(-9012345678987654321),
+ `main.Z_int8`: int8(-121),
+ `main.Z_uint16`: uint16(54321),
+ `main.Z_uint32`: uint32(3217654321),
+ `main.Z_uint64`: uint64(12345678900987654321),
+ `main.Z_uint8`: uint8(231),
}
var expectedVars = map[string]string{
@@ -269,4 +275,75 @@
t.Error("Value of invalid location: expected error")
}
}
+
+ // checkValue tests that we can get a Var for a variable with the given name,
+ // that we can then get the value of that Var, and that calling fn for that
+ // value succeeds.
+ checkValue := func(name string, fn func(val program.Value) error) {
+ if v, err := prog.VarByName(name); err != nil {
+ t.Errorf("VarByName(%s): %s", name, err)
+ } else if val, err := prog.Value(v); err != nil {
+ t.Errorf("value for %s: %s", name, err)
+ } else if err := fn(val); err != nil {
+ t.Errorf("value for %s: %s", name, err)
+ }
+ }
+
+ checkValue("main.Z_uintptr", func(val program.Value) error {
+ if val != uint32(21) && val != uint64(21) {
+ // Z_uintptr should be an unsigned integer with size equal to the debugged
+ // program's address size.
+ return fmt.Errorf("got %T(%v) want 21", val, val)
+ }
+ return nil
+ })
+
+ checkValue("main.Z_int", func(val program.Value) error {
+ if val != int32(-21) && val != int64(-21) {
+ return fmt.Errorf("got %T(%v) want -21", val, val)
+ }
+ return nil
+ })
+
+ checkValue("main.Z_uint", func(val program.Value) error {
+ if val != uint32(21) && val != uint64(21) {
+ return fmt.Errorf("got %T(%v) want 21", val, val)
+ }
+ return nil
+ })
+
+ checkValue("main.Z_pointer", func(val program.Value) error {
+ if _, ok := val.(program.Pointer); !ok {
+ return fmt.Errorf("got %T(%v) expected Pointer", val, val)
+ }
+ return nil
+ })
+
+ checkValue("main.Z_pointer_nil", func(val program.Value) error {
+ if p, ok := val.(program.Pointer); !ok {
+ return fmt.Errorf("got %T(%v) expected Pointer", val, val)
+ } else if p.Address != 0 {
+ return fmt.Errorf("got %T(%v) expected nil pointer", val, val)
+ }
+ return nil
+ })
+
+ checkValue("main.Z_array", func(val program.Value) error {
+ a, ok := val.(program.Array)
+ if !ok {
+ return fmt.Errorf("got %T(%v) expected Array", val, val)
+ }
+ if a.Len() != 5 {
+ return fmt.Errorf("got array length %d expected 5", a.Len())
+ }
+ expected := [5]int8{-121, 121, 3, 2, 1}
+ for i := uint64(0); i < 5; i++ {
+ if v, err := prog.Value(a.Element(i)); err != nil {
+ return fmt.Errorf("reading element %d: %s", i, err)
+ } else if v != expected[i] {
+ return fmt.Errorf("element %d: got %T(%v) want %T(%d)", i, v, v, expected[i], expected[i])
+ }
+ }
+ return nil
+ })
}
diff --git a/ogle/program/program.go b/ogle/program/program.go
index 9b83dc0..fc86f46 100644
--- a/ogle/program/program.go
+++ b/ogle/program/program.go
@@ -105,6 +105,41 @@
// A value read from a remote program.
type Value interface{}
+// Pointer is a Var representing a pointer.
+type Pointer Var
+
+// Array is a Var representing an array.
+type Array struct {
+ ElementTypeID uint64
+ Address uint64
+ Length uint64 // Number of elements in the array
+ StrideBits uint64 // Number of bits between array entries
+}
+
+// Len returns the number of elements in the array.
+func (a Array) Len() uint64 {
+ return a.Length
+}
+
+// Element returns a Var referring to the given element of the array.
+func (a Array) Element(index uint64) Var {
+ return Var{
+ TypeID: a.ElementTypeID,
+ Address: a.Address + index*(a.StrideBits/8),
+ }
+}
+
+// Struct is a Var representing a struct.
+type Struct struct {
+ Fields []StructField
+}
+
+// StructField represents a field in a struct object.
+type StructField struct {
+ Name string
+ Var Var
+}
+
// The File interface provides access to file-like resources in the program.
// It implements only ReaderAt and WriterAt, not Reader and Writer, because
// random access is a far more common pattern for things like symbol tables,
diff --git a/ogle/program/proxyrpc/proxyrpc.go b/ogle/program/proxyrpc/proxyrpc.go
index e8e8276..18d551f 100644
--- a/ogle/program/proxyrpc/proxyrpc.go
+++ b/ogle/program/proxyrpc/proxyrpc.go
@@ -6,7 +6,18 @@
// used to the ogleproxy.
package proxyrpc // import "golang.org/x/debug/ogle/program/proxyrpc"
-import "golang.org/x/debug/ogle/program"
+import (
+ "encoding/gob"
+
+ "golang.org/x/debug/ogle/program"
+)
+
+func init() {
+ // Register implementations of program.Var with gob.
+ gob.Register(program.Pointer{})
+ gob.Register(program.Array{})
+ gob.Register(program.Struct{})
+}
// For regularity, each method has a unique Request and a Response type even
// when not strictly necessary.
diff --git a/ogle/program/server/value.go b/ogle/program/server/value.go
index c273b07..d457b92 100644
--- a/ogle/program/server/value.go
+++ b/ogle/program/server/value.go
@@ -12,10 +12,10 @@
// value peeks the program's memory at the given address, parsing it as a value of type t.
func (s *Server) value(t dwarf.Type, addr uint64) (program.Value, error) {
- // readInt reads the memory for an n-byte integer or unsigned integer.
- readInt := func(n int64) ([]byte, error) {
+ // readBasic reads the memory for a basic type of size n bytes.
+ readBasic := func(n int64) ([]byte, error) {
switch n {
- case 1, 2, 4, 8:
+ case 1, 2, 4, 8, 16:
default:
return nil, fmt.Errorf("invalid size: %d", n)
}
@@ -27,9 +27,9 @@
}
switch t := t.(type) {
- case *dwarf.IntType:
+ case *dwarf.CharType, *dwarf.IntType:
bs := t.Common().ByteSize
- buf, err := readInt(bs)
+ buf, err := readBasic(bs)
if err != nil {
return nil, fmt.Errorf("reading integer: %s", err)
}
@@ -43,10 +43,12 @@
return int32(x), nil
case 8:
return int64(x), nil
+ default:
+ return nil, fmt.Errorf("invalid integer size: %d", bs)
}
- case *dwarf.UintType:
+ case *dwarf.UcharType, *dwarf.UintType, *dwarf.AddrType:
bs := t.Common().ByteSize
- buf, err := readInt(bs)
+ buf, err := readBasic(bs)
if err != nil {
return nil, fmt.Errorf("reading unsigned integer: %s", err)
}
@@ -60,7 +62,88 @@
return uint32(x), nil
case 8:
return uint64(x), nil
+ default:
+ return nil, fmt.Errorf("invalid unsigned integer size: %d", bs)
}
+ case *dwarf.BoolType:
+ bs := t.Common().ByteSize
+ buf, err := readBasic(bs)
+ if err != nil {
+ return nil, fmt.Errorf("reading boolean: %s", err)
+ }
+ for _, b := range buf {
+ if b != 0 {
+ return true, nil
+ }
+ }
+ return false, nil
+ case *dwarf.FloatType:
+ bs := t.Common().ByteSize
+ buf, err := readBasic(bs)
+ if err != nil {
+ return nil, fmt.Errorf("reading float: %s", err)
+ }
+ switch bs {
+ case 4:
+ return s.arch.Float32(buf), nil
+ case 8:
+ return s.arch.Float64(buf), nil
+ default:
+ return nil, fmt.Errorf("invalid float size: %d", bs)
+ }
+ case *dwarf.ComplexType:
+ bs := t.Common().ByteSize
+ buf, err := readBasic(bs)
+ if err != nil {
+ return nil, fmt.Errorf("reading complex: %s", err)
+ }
+ switch bs {
+ case 8:
+ return s.arch.Complex64(buf), nil
+ case 16:
+ return s.arch.Complex128(buf), nil
+ default:
+ return nil, fmt.Errorf("invalid complex size: %d", bs)
+ }
+ case *dwarf.PtrType:
+ bs := t.Common().ByteSize
+ if bs != int64(s.arch.PointerSize) {
+ return nil, fmt.Errorf("invalid pointer size: %d", bs)
+ }
+ buf, err := readBasic(bs)
+ if err != nil {
+ return nil, fmt.Errorf("reading pointer: %s", err)
+ }
+ return program.Pointer{
+ TypeID: uint64(t.Type.Common().Offset),
+ Address: uint64(s.arch.Uintptr(buf)),
+ }, nil
+ case *dwarf.ArrayType:
+ length := t.Count
+ stride := t.StrideBitSize
+ if stride%8 != 0 {
+ return nil, fmt.Errorf("array is not byte-aligned")
+ }
+ return program.Array{
+ ElementTypeID: uint64(t.Type.Common().Offset),
+ Address: uint64(addr),
+ Length: uint64(length),
+ StrideBits: uint64(stride),
+ }, nil
+ case *dwarf.StructType:
+ fields := make([]program.StructField, len(t.Field))
+ for i, field := range t.Field {
+ fields[i] = program.StructField{
+ Name: field.Name,
+ Var: program.Var{
+ TypeID: uint64(field.Type.Common().Offset),
+ Address: uint64(addr) + uint64(field.ByteOffset),
+ },
+ }
+ }
+ return program.Struct{fields}, nil
+ case *dwarf.TypedefType:
+ return s.value(t.Type, addr)
// TODO: more types
}
return nil, fmt.Errorf("Unsupported type %T", t)