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)