x/debug: support getting values of channel type.

program.Channel has a method Element, similar to that of array and
slice, that allows reading of the channel's buffered values.

Change-Id: Ic62d3319f231a685035c36a01f6c5f0b8fcfd87c
Reviewed-on: https://go-review.googlesource.com/13880
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/ogle/demo/ogler/ogler_test.go b/ogle/demo/ogler/ogler_test.go
index e5803dd..baba743 100644
--- a/ogle/demo/ogler/ogler_test.go
+++ b/ogle/demo/ogler/ogler_test.go
@@ -41,9 +41,10 @@
 	`main.Z_array_empty`:         `[0]int8{}`,
 	`main.Z_bool_false`:          `false`,
 	`main.Z_bool_true`:           `true`,
-	`main.Z_channel`:             `(chan int 0xX)`,
-	`main.Z_channel_buffered`:    `(chan int 0xX [0/10])`,
-	`main.Z_channel_nil`:         `(chan int <nil>)`,
+	`main.Z_channel`:             `(chan int16 0xX)`,
+	`main.Z_channel_2`:           `(chan int16 0xX)`,
+	`main.Z_channel_buffered`:    `(chan int16 0xX [6/10])`,
+	`main.Z_channel_nil`:         `(chan int16 <nil>)`,
 	`main.Z_array_of_empties`:    `[2]struct struct {}{struct struct {} {}, (struct struct {} 0xX)}`,
 	`main.Z_complex128`:          `(1.987654321-2.987654321i)`,
 	`main.Z_complex64`:           `(1.54321+2.54321i)`,
@@ -516,4 +517,87 @@
 		}
 		return nil
 	})
+
+	checkValue("main.Z_channel", func(val program.Value) error {
+		c, ok := val.(program.Channel)
+		if !ok {
+			return fmt.Errorf("got %T(%v) expected Channel", val, val)
+		}
+		if c.Buffer == 0 {
+			return fmt.Errorf("got buffer address %d expected nonzero", c.Buffer)
+		}
+		if c.Length != 0 {
+			return fmt.Errorf("got length %d expected 0", c.Length)
+		}
+		if c.Capacity != 0 {
+			return fmt.Errorf("got capacity %d expected 0", c.Capacity)
+		}
+		return nil
+	})
+
+	checkValue("main.Z_channel_2", func(val program.Value) error {
+		c, ok := val.(program.Channel)
+		if !ok {
+			return fmt.Errorf("got %T(%v) expected Channel", val, val)
+		}
+		if c.Buffer == 0 {
+			return fmt.Errorf("got buffer address %d expected nonzero", c.Buffer)
+		}
+		if c.Length != 0 {
+			return fmt.Errorf("got length %d expected 0", c.Length)
+		}
+		if c.Capacity != 0 {
+			return fmt.Errorf("got capacity %d expected 0", c.Capacity)
+		}
+		return nil
+	})
+
+	checkValue("main.Z_channel_nil", func(val program.Value) error {
+		c, ok := val.(program.Channel)
+		if !ok {
+			return fmt.Errorf("got %T(%v) expected Channel", val, val)
+		}
+		if c.Buffer != 0 {
+			return fmt.Errorf("got buffer address %d expected 0", c.Buffer)
+		}
+		if c.Length != 0 {
+			return fmt.Errorf("got length %d expected 0", c.Length)
+		}
+		if c.Capacity != 0 {
+			return fmt.Errorf("got capacity %d expected 0", c.Capacity)
+		}
+		return nil
+	})
+
+	checkValue("main.Z_channel_buffered", func(val program.Value) error {
+		c, ok := val.(program.Channel)
+		if !ok {
+			return fmt.Errorf("got %T(%v) expected Channel", val, val)
+		}
+		if c.Buffer == 0 {
+			return fmt.Errorf("got buffer address %d expected nonzero", c.Buffer)
+		}
+		if c.Length != 6 {
+			return fmt.Errorf("got length %d expected 6", c.Length)
+		}
+		if c.Capacity != 10 {
+			return fmt.Errorf("got capacity %d expected 10", c.Capacity)
+		}
+		if c.Stride != 2 {
+			return fmt.Errorf("got stride %d expected 2", c.Stride)
+		}
+		expected := []int16{8, 9, 10, 11, 12, 13}
+		for i := uint64(0); i < 6; i++ {
+			if v, err := prog.Value(c.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])
+			}
+		}
+		v := c.Element(6)
+		if v.Address != 0 {
+			return fmt.Errorf("invalid element returned Var with address %d, expected 0", v.Address)
+		}
+		return nil
+	})
 }
diff --git a/ogle/demo/tracee/main.go b/ogle/demo/tracee/main.go
index 7051b30..1ac8a8b 100644
--- a/ogle/demo/tracee/main.go
+++ b/ogle/demo/tracee/main.go
@@ -46,9 +46,10 @@
 	Z_array               [5]int8     = [5]int8{-121, 121, 3, 2, 1}
 	Z_array_empty         [0]int8     = [0]int8{}
 	Z_array_of_empties    [2]struct{} = [2]struct{}{struct{}{}, struct{}{}}
-	Z_channel             chan int    = make(chan int)
-	Z_channel_buffered    chan int    = make(chan int, 10)
-	Z_channel_nil         chan int
+	Z_channel             chan int16  = make(chan int16)
+	Z_channel_2           chan int16  = make(chan int16)
+	Z_channel_buffered    chan int16  = make(chan int16, 10)
+	Z_channel_nil         chan int16
 	Z_func_bar                         = (*FooStruct).Bar
 	Z_func_int8_r_int8                 = func(x int8) int8 { return x + 1 }
 	Z_func_int8_r_pint8                = func(x int8) *int8 { y := x + 1; return &y }
@@ -102,12 +103,30 @@
 	fmt.Print()
 }
 
+func populateChannels() {
+	go func() {
+		Z_channel_2 <- 8
+	}()
+	go func() {
+		for i := int16(0); i < 14; i++ {
+			Z_channel_buffered <- i
+		}
+	}()
+	go func() {
+		for i := 0; i < 8; i++ {
+			<-Z_channel_buffered
+		}
+	}()
+	time.Sleep(time.Second / 20)
+}
+
 func main() {
 	args := os.Args[1:]
 	expected := []string{"some", "arguments"}
 	if len(args) != 2 || args[0] != expected[0] || args[1] != expected[1] {
 		log.Fatalf("got command-line args %v, expected %v", args, expected)
 	}
+	populateChannels()
 	for ; ; time.Sleep(2 * time.Second) {
 		bar()
 	}
diff --git a/ogle/program/program.go b/ogle/program/program.go
index f80efe1..b48c055 100644
--- a/ogle/program/program.go
+++ b/ogle/program/program.go
@@ -160,6 +160,41 @@
 	Var  Var
 }
 
+// Channel is a Var representing a channel.
+type Channel struct {
+	ElementTypeID uint64
+	Buffer        uint64 // Location of the buffer; zero for nil and unbuffered channels.
+	Length        uint64 // Number of elements stored in the channel buffer.
+	Capacity      uint64 // Capacity of the buffer; zero for unbuffered channels.
+	Stride        uint64 // Number of bytes between buffer entries.
+	BufferStart   uint64 // Index in the buffer of the element at the head of the queue.
+}
+
+// Element returns a Var referring to the given element of the channel's queue.
+// If the channel is unbuffered, nil, or if the index is too large, returns a Var with Address == 0.
+func (m Channel) Element(index uint64) Var {
+	if index >= m.Length {
+		return Var{
+			TypeID:  m.ElementTypeID,
+			Address: 0,
+		}
+	}
+	if index < m.Capacity-m.BufferStart {
+		// The element is in the part of the queue that occurs later in the buffer
+		// than the head of the queue.
+		return Var{
+			TypeID:  m.ElementTypeID,
+			Address: m.Buffer + (m.BufferStart+index)*m.Stride,
+		}
+	}
+	// The element is in the part of the queue that has wrapped around to the
+	// start of the buffer.
+	return Var{
+		TypeID:  m.ElementTypeID,
+		Address: m.Buffer + (m.BufferStart+index-m.Capacity)*m.Stride,
+	}
+}
+
 // 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 1a76110..c3fac48 100644
--- a/ogle/program/proxyrpc/proxyrpc.go
+++ b/ogle/program/proxyrpc/proxyrpc.go
@@ -20,6 +20,7 @@
 	gob.Register(program.Slice{})
 	gob.Register(program.Map{})
 	gob.Register(program.String{})
+	gob.Register(program.Channel{})
 }
 
 // For regularity, each method has a unique Request and a Response type even
diff --git a/ogle/program/server/value.go b/ogle/program/server/value.go
index d53e5a8..3532620 100644
--- a/ogle/program/server/value.go
+++ b/ogle/program/server/value.go
@@ -201,6 +201,56 @@
 			return nil, fmt.Errorf("reading string contents: %s", err)
 		}
 		return program.String{Length: length, String: string(tmp)}, nil
+	case *dwarf.ChanType:
+		pt, ok := t.TypedefType.Type.(*dwarf.PtrType)
+		if !ok {
+			return nil, fmt.Errorf("reading channel: type is not a pointer")
+		}
+		st, ok := pt.Type.(*dwarf.StructType)
+		if !ok {
+			return nil, fmt.Errorf("reading channel: type is not a pointer to struct")
+		}
+
+		a, err := s.peekPtr(addr)
+		if err != nil {
+			return nil, fmt.Errorf("reading channel pointer: %s", err)
+		}
+		if a == 0 {
+			// This channel is nil.
+			return program.Channel{
+				ElementTypeID: uint64(t.ElemType.Common().Offset),
+				Buffer:        0,
+				Length:        0,
+				Capacity:      0,
+				Stride:        uint64(t.ElemType.Common().ByteSize),
+				BufferStart:   0,
+			}, nil
+		}
+
+		buf, err := s.peekPtrStructField(st, a, "buf")
+		if err != nil {
+			return nil, fmt.Errorf("reading channel buffer location: %s", err)
+		}
+		qcount, err := s.peekUintOrIntStructField(st, a, "qcount")
+		if err != nil {
+			return nil, fmt.Errorf("reading channel length: %s", err)
+		}
+		capacity, err := s.peekUintOrIntStructField(st, a, "dataqsiz")
+		if err != nil {
+			return nil, fmt.Errorf("reading channel capacity: %s", err)
+		}
+		recvx, err := s.peekUintOrIntStructField(st, a, "recvx")
+		if err != nil {
+			return nil, fmt.Errorf("reading channel buffer index: %s", err)
+		}
+		return program.Channel{
+			ElementTypeID: uint64(t.ElemType.Common().Offset),
+			Buffer:        buf,
+			Length:        qcount,
+			Capacity:      capacity,
+			Stride:        uint64(t.ElemType.Common().ByteSize),
+			BufferStart:   recvx,
+		}, nil
 		// TODO: more types
 	}
 	return nil, fmt.Errorf("Unsupported type %T", t)