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