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)