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)