x/debug: read goroutines from slice runtime.allgs
Tries to read runtime.{allg,allglen} if we can't read runtime.allgs.
runtime.allg is not present in Go 1.6 executables.
Change-Id: I68e08fa4cfb1859e083e91beb8b25db417c2a057
Reviewed-on: https://go-review.googlesource.com/19940
Reviewed-by: Dave Day <djd@golang.org>
diff --git a/server/peek.go b/server/peek.go
index 936b3ab..a26677d 100644
--- a/server/peek.go
+++ b/server/peek.go
@@ -10,6 +10,7 @@
"errors"
"fmt"
+ "golang.org/x/debug"
"golang.org/x/debug/dwarf"
)
@@ -54,6 +55,35 @@
return s.arch.UintN(buf), nil
}
+// peekSlice reads the header of a slice with the given type and address.
+func (s *Server) peekSlice(t *dwarf.SliceType, addr uint64) (debug.Slice, error) {
+ ptr, err := s.peekPtrStructField(&t.StructType, addr, "array")
+ if err != nil {
+ return debug.Slice{}, fmt.Errorf("reading slice location: %s", err)
+ }
+ length, err := s.peekUintOrIntStructField(&t.StructType, addr, "len")
+ if err != nil {
+ return debug.Slice{}, fmt.Errorf("reading slice length: %s", err)
+ }
+ capacity, err := s.peekUintOrIntStructField(&t.StructType, addr, "cap")
+ if err != nil {
+ return debug.Slice{}, fmt.Errorf("reading slice capacity: %s", err)
+ }
+ if capacity < length {
+ return debug.Slice{}, fmt.Errorf("slice's capacity %d is less than its length %d", capacity, length)
+ }
+
+ return debug.Slice{
+ debug.Array{
+ ElementTypeID: uint64(t.ElemType.Common().Offset),
+ Address: uint64(ptr),
+ Length: length,
+ StrideBits: uint64(t.ElemType.Common().ByteSize) * 8,
+ },
+ capacity,
+ }, nil
+}
+
// peekString reads a string of the given type at the given address.
// At most byteLimit bytes will be read. If the string is longer, "..." is appended.
func (s *Server) peekString(typ *dwarf.StringType, a uint64, byteLimit uint64) (string, error) {
diff --git a/server/server.go b/server/server.go
index 584ed5b..640895e 100644
--- a/server/server.go
+++ b/server/server.go
@@ -909,66 +909,101 @@
return errors.New("runtime.g is not a struct")
}
- // Read runtime.allg.
- allgEntry, err := s.dwarfData.LookupEntry("runtime.allg")
- if err != nil {
- return err
- }
- allgAddr, err := s.dwarfData.EntryLocation(allgEntry)
- if err != nil {
- return err
- }
- allg, err := s.peekPtr(allgAddr)
- if err != nil {
- return fmt.Errorf("reading allg: %v", err)
- }
-
- // Read runtime.allglen.
- allglenEntry, err := s.dwarfData.LookupEntry("runtime.allglen")
- if err != nil {
- return err
- }
- off, err := s.dwarfData.EntryTypeOffset(allglenEntry)
- if err != nil {
- return err
- }
- allglenType, err := s.dwarfData.Type(off)
- if err != nil {
- return err
- }
- allglenAddr, err := s.dwarfData.EntryLocation(allglenEntry)
- if err != nil {
- return err
- }
- var allglen uint64
- switch followTypedefs(allglenType).(type) {
- case *dwarf.UintType, *dwarf.IntType:
- allglen, err = s.peekUint(allglenAddr, allglenType.Common().ByteSize)
+ var (
+ allgPtr, allgLen uint64
+ allgPtrOk bool
+ )
+ for {
+ // Try to read the slice runtime.allgs.
+ allgsEntry, err := s.dwarfData.LookupEntry("runtime.allgs")
if err != nil {
- return fmt.Errorf("reading allglen: %v", err)
- }
- default:
- // Some runtimes don't specify the type for allglen. Assume it's uint32.
- allglen, err = s.peekUint(allglenAddr, 4)
- if err != nil {
- return fmt.Errorf("reading allglen: %v", err)
- }
- if allglen != 0 {
break
}
- // Zero? Let's try uint64.
- allglen, err = s.peekUint(allglenAddr, 8)
+ allgsAddr, err := s.dwarfData.EntryLocation(allgsEntry)
if err != nil {
- return fmt.Errorf("reading allglen: %v", err)
+ break
+ }
+ off, err := s.dwarfData.EntryTypeOffset(allgsEntry)
+ if err != nil {
+ break
+ }
+ t, err := s.dwarfData.Type(off)
+ if err != nil {
+ break
+ }
+ allgsType, ok := followTypedefs(t).(*dwarf.SliceType)
+ if !ok {
+ break
+ }
+ allgs, err := s.peekSlice(allgsType, allgsAddr)
+ if err != nil {
+ break
+ }
+
+ allgPtr, allgLen, allgPtrOk = allgs.Address, allgs.Length, true
+ break
+ }
+ if !allgPtrOk {
+ // Read runtime.allg.
+ allgEntry, err := s.dwarfData.LookupEntry("runtime.allg")
+ if err != nil {
+ return err
+ }
+ allgAddr, err := s.dwarfData.EntryLocation(allgEntry)
+ if err != nil {
+ return err
+ }
+ allgPtr, err = s.peekPtr(allgAddr)
+ if err != nil {
+ return fmt.Errorf("reading allg: %v", err)
+ }
+
+ // Read runtime.allglen.
+ allglenEntry, err := s.dwarfData.LookupEntry("runtime.allglen")
+ if err != nil {
+ return err
+ }
+ off, err := s.dwarfData.EntryTypeOffset(allglenEntry)
+ if err != nil {
+ return err
+ }
+ allglenType, err := s.dwarfData.Type(off)
+ if err != nil {
+ return err
+ }
+ allglenAddr, err := s.dwarfData.EntryLocation(allglenEntry)
+ if err != nil {
+ return err
+ }
+ switch followTypedefs(allglenType).(type) {
+ case *dwarf.UintType, *dwarf.IntType:
+ allgLen, err = s.peekUint(allglenAddr, allglenType.Common().ByteSize)
+ if err != nil {
+ return fmt.Errorf("reading allglen: %v", err)
+ }
+ default:
+ // Some runtimes don't specify the type for allglen. Assume it's uint32.
+ allgLen, err = s.peekUint(allglenAddr, 4)
+ if err != nil {
+ return fmt.Errorf("reading allglen: %v", err)
+ }
+ if allgLen != 0 {
+ break
+ }
+ // Zero? Let's try uint64.
+ allgLen, err = s.peekUint(allglenAddr, 8)
+ if err != nil {
+ return fmt.Errorf("reading allglen: %v", err)
+ }
}
}
// Initialize s.goroutineStack.
s.goroutineStackOnce.Do(func() { s.goroutineStackInit(gType) })
- for i := uint64(0); i < allglen; i++ {
+ for i := uint64(0); i < allgLen; i++ {
// allg is an array of pointers to g structs. Read allg[i].
- g, err := s.peekPtr(allg + i*uint64(s.arch.PointerSize))
+ g, err := s.peekPtr(allgPtr + i*uint64(s.arch.PointerSize))
if err != nil {
return err
}
diff --git a/server/value.go b/server/value.go
index 0fef7fe..e5bb0d2 100644
--- a/server/value.go
+++ b/server/value.go
@@ -120,31 +120,11 @@
Address: uint64(s.arch.Uintptr(buf)),
}, nil
case *dwarf.SliceType:
- ptr, err := s.peekPtrStructField(&t.StructType, addr, "array")
- if err != nil {
- return nil, fmt.Errorf("reading slice location: %s", err)
+ if s, err := s.peekSlice(t, addr); err != nil {
+ return nil, err
+ } else {
+ return s, nil
}
- length, err := s.peekUintOrIntStructField(&t.StructType, addr, "len")
- if err != nil {
- return nil, fmt.Errorf("reading slice length: %s", err)
- }
- capacity, err := s.peekUintOrIntStructField(&t.StructType, addr, "cap")
- if err != nil {
- return nil, fmt.Errorf("reading slice capacity: %s", err)
- }
- if capacity < length {
- return nil, fmt.Errorf("slice's capacity %d is less than its length %d", capacity, length)
- }
-
- return debug.Slice{
- debug.Array{
- ElementTypeID: uint64(t.ElemType.Common().Offset),
- Address: uint64(ptr),
- Length: length,
- StrideBits: uint64(t.ElemType.Common().ByteSize) * 8,
- },
- capacity,
- }, nil
case *dwarf.ArrayType:
length := t.Count
stride := t.StrideBitSize