x/debug: generalize map-reading code and move it from print.go to peek.go so it can be used elsewhere.

Change the printer to output maps in a similar format to fmt.Print.

Change-Id: Ie53d83fd769a3d0c175c3d67d89efc12ffe17826
Reviewed-on: https://go-review.googlesource.com/12349
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/ogle/demo/ogler/ogler_test.go b/ogle/demo/ogler/ogler_test.go
index 855f3cf..3b9e35b 100644
--- a/ogle/demo/ogler/ogler_test.go
+++ b/ogle/demo/ogler/ogler_test.go
@@ -60,10 +60,10 @@
 	`main.Z_interface`:           `("*main.FooStruct", 0xX)`,
 	`main.Z_interface_nil`:       `(<nil>, <nil>)`,
 	`main.Z_interface_typed_nil`: `("*main.FooStruct", <nil>)`,
-	`main.Z_map`:                 `{-21:3.54321}`,
-	`main.Z_map_2`:               `{1024:1}`,
-	`main.Z_map_empty`:           `{}`,
-	`main.Z_map_nil`:             `<nil>`,
+	`main.Z_map`:                 `map[-21:3.54321]`,
+	`main.Z_map_2`:               `map[1024:1]`,
+	`main.Z_map_empty`:           `map[]`,
+	`main.Z_map_nil`:             `map[]`,
 	`main.Z_pointer`:             `0xX`,
 	`main.Z_pointer_nil`:         `0x0`,
 	`main.Z_slice`:               `[]uint8{115, 108, 105, 99, 101}`,
diff --git a/ogle/program/server/peek.go b/ogle/program/server/peek.go
index a2f03ce..d3c16b2 100644
--- a/ogle/program/server/peek.go
+++ b/ogle/program/server/peek.go
@@ -7,6 +7,7 @@
 package server
 
 import (
+	"errors"
 	"fmt"
 
 	"golang.org/x/debug/dwarf"
@@ -120,3 +121,129 @@
 	}
 	return s.peekInt(addr+uint64(f.ByteOffset), it.ByteSize)
 }
+
+// peekMapValues reads a map at the given address and calls fn with the addresses for each (key, value) pair.
+// If fn returns false, peekMapValues stops.
+func (s *Server) peekMapValues(t *dwarf.MapType, a uint64, fn func(keyAddr, valAddr uint64, keyType, valType dwarf.Type) bool) error {
+	pt, ok := t.Type.(*dwarf.PtrType)
+	if !ok {
+		return errors.New("bad map type: not a pointer")
+	}
+	st, ok := pt.Type.(*dwarf.StructType)
+	if !ok {
+		return errors.New("bad map type: not a pointer to a struct")
+	}
+	// a is the address of a pointer to a struct.  Get the pointer's value.
+	a, err := s.peekPtr(a)
+	if err != nil {
+		return fmt.Errorf("reading map pointer: %s", err)
+	}
+	if a == 0 {
+		// The pointer was nil, so the map is empty.
+		return nil
+	}
+	// Gather information about the struct type and the map bucket type.
+	b, err := s.peekUintStructField(st, a, "B")
+	if err != nil {
+		return fmt.Errorf("reading map: %s", err)
+	}
+	buckets, err := s.peekPtrStructField(st, a, "buckets")
+	if err != nil {
+		return fmt.Errorf("reading map: %s", err)
+	}
+	oldbuckets, err := s.peekPtrStructField(st, a, "oldbuckets")
+	if err != nil {
+		return fmt.Errorf("reading map: %s", err)
+	}
+	bf, err := getField(st, "buckets")
+	if err != nil {
+		return fmt.Errorf("reading map: %s", err)
+	}
+	bucketPtrType, ok := bf.Type.(*dwarf.PtrType)
+	if !ok {
+		return errors.New("bad map bucket type: not a pointer")
+	}
+	bt, ok := bucketPtrType.Type.(*dwarf.StructType)
+	if !ok {
+		return errors.New("bad map bucket type: not a pointer to a struct")
+	}
+	bucketSize := uint64(bucketPtrType.Type.Size())
+	tophashField, err := getField(bt, "tophash")
+	if err != nil {
+		return fmt.Errorf("reading map: %s", err)
+	}
+	bucketCnt := uint64(tophashField.Type.Size())
+	tophashFieldOffset := uint64(tophashField.ByteOffset)
+	keysField, err := getField(bt, "keys")
+	if err != nil {
+		return fmt.Errorf("reading map: %s", err)
+	}
+	keysType, ok := keysField.Type.(*dwarf.ArrayType)
+	if !ok {
+		return errors.New(`bad map bucket type: "keys" is not an array`)
+	}
+	keyType := keysType.Type
+	keysStride := uint64(keysType.StrideBitSize / 8)
+	keysFieldOffset := uint64(keysField.ByteOffset)
+	valuesField, err := getField(bt, "values")
+	if err != nil {
+		return fmt.Errorf("reading map: %s", err)
+	}
+	valuesType, ok := valuesField.Type.(*dwarf.ArrayType)
+	if !ok {
+		return errors.New(`bad map bucket type: "values" is not an array`)
+	}
+	valueType := valuesType.Type
+	valuesStride := uint64(valuesType.StrideBitSize / 8)
+	valuesFieldOffset := uint64(valuesField.ByteOffset)
+	overflowField, err := getField(bt, "overflow")
+	if err != nil {
+		return fmt.Errorf("reading map: %s", err)
+	}
+	overflowFieldOffset := uint64(overflowField.ByteOffset)
+
+	// Iterate through the two arrays of buckets.
+	bucketArrays := [2]struct {
+		addr uint64
+		size uint64
+	}{
+		{buckets, 1 << b},
+		{oldbuckets, 1 << (b - 1)},
+	}
+	for _, bucketArray := range bucketArrays {
+		if bucketArray.addr == 0 {
+			continue
+		}
+		for i := uint64(0); i < bucketArray.size; i++ {
+			bucketAddr := bucketArray.addr + i*bucketSize
+			// Iterate through the linked list of buckets.
+			// TODO: check for repeated bucket pointers.
+			for bucketAddr != 0 {
+				// Iterate through each entry in the bucket.
+				for j := uint64(0); j < bucketCnt; j++ {
+					tophash, err := s.peekUint8(bucketAddr + tophashFieldOffset + j)
+					if err != nil {
+						return errors.New("reading map: " + err.Error())
+					}
+					// From runtime/hashmap.go
+					const minTopHash = 4
+					if tophash < minTopHash {
+						continue
+					}
+					keyAddr := bucketAddr + keysFieldOffset + j*keysStride
+					valAddr := bucketAddr + valuesFieldOffset + j*valuesStride
+					if !fn(keyAddr, valAddr, keyType, valueType) {
+						return nil
+					}
+				}
+				var err error
+				bucketAddr, err = s.peekPtr(bucketAddr + overflowFieldOffset)
+				if err != nil {
+					return errors.New("reading map: " + err.Error())
+				}
+			}
+		}
+	}
+
+	return nil
+}
diff --git a/ogle/program/server/print.go b/ogle/program/server/print.go
index b133dd3..3d671ae 100644
--- a/ogle/program/server/print.go
+++ b/ogle/program/server/print.go
@@ -391,168 +391,28 @@
 const maxMapValuesToPrint = 8
 
 func (p *Printer) printMapAt(typ *dwarf.MapType, a uint64) {
-	// Maps are pointers to structs.
-	pt, ok := typ.Type.(*dwarf.PtrType)
-	if !ok {
-		p.errorf("bad map type: not a pointer")
-		return
-	}
-	st, ok := pt.Type.(*dwarf.StructType)
-	if !ok {
-		p.errorf("bad map type: not a pointer to a struct")
-		return
-	}
-	a, err := p.server.peekPtr(a)
-	if err != nil {
-		p.errorf("reading map pointer: %s", err)
-		return
-	}
-	if a == 0 {
-		p.printf("<nil>")
-		return
-	}
-	b, err := p.server.peekUintStructField(st, a, "B")
-	if err != nil {
-		p.errorf("reading map: %s", err)
-		return
-	}
-	buckets, err := p.server.peekPtrStructField(st, a, "buckets")
-	if err != nil {
-		p.errorf("reading map: %s", err)
-		return
-	}
-	oldbuckets, err := p.server.peekPtrStructField(st, a, "oldbuckets")
-	if err != nil {
-		p.errorf("reading map: %s", err)
-		return
-	}
-
-	p.printf("{")
-	// Limit how many values are printed per map.
-	numValues := uint64(0)
-	{
-		bf, err := getField(st, "buckets")
-		if err != nil {
-			p.errorf("%s", err)
-		} else {
-			p.printMapBucketsAt(bf.Type, buckets, 1<<b, &numValues)
+	count := 0
+	fn := func(keyAddr, valAddr uint64, keyType, valType dwarf.Type) (stop bool) {
+		count++
+		if count > maxMapValuesToPrint {
+			return false
 		}
-	}
-	if b > 0 {
-		bf, err := getField(st, "oldbuckets")
-		if err != nil {
-			p.errorf("%s", err)
-		} else {
-			p.printMapBucketsAt(bf.Type, oldbuckets, 1<<(b-1), &numValues)
+		if count > 1 {
+			p.printf(" ")
 		}
+		p.printValueAt(keyType, keyAddr)
+		p.printf(":")
+		p.printValueAt(valType, valAddr)
+		return true
 	}
-	p.printf("}")
-}
-
-func (p *Printer) printMapBucketsAt(t dwarf.Type, a, numBuckets uint64, numValues *uint64) {
-	if *numValues > maxMapValuesToPrint {
-		return
+	p.printf("map[")
+	if err := p.server.peekMapValues(typ, a, fn); err != nil {
+		p.errorf("reading map values: %s", err)
 	}
-	if a == 0 {
-		return
+	if count > maxMapValuesToPrint {
+		p.printf(" ...")
 	}
-	// From runtime/hashmap.go
-	const minTopHash = 4
-	// t is a pointer to a struct.
-	bucketPtrType, ok := t.(*dwarf.PtrType)
-	if !ok {
-		p.errorf("bad map bucket type: not a pointer")
-		return
-	}
-	bt, ok := bucketPtrType.Type.(*dwarf.StructType)
-	if !ok {
-		p.errorf("bad map bucket type: not a pointer to a struct")
-		return
-	}
-	bucketSize, ok := p.sizeof(bucketPtrType.Type)
-	if !ok {
-		p.errorf("can't get bucket size")
-		return
-	}
-	tophashField, err := getField(bt, "tophash")
-	if err != nil {
-		p.errorf("%s", err)
-		return
-	}
-	bucketCnt, ok := p.sizeof(tophashField.Type)
-	if !ok {
-		p.errorf("can't get tophash size")
-		return
-	}
-	keysField, err := getField(bt, "keys")
-	if err != nil {
-		p.errorf("%s", err)
-		return
-	}
-	keysType, ok := keysField.Type.(*dwarf.ArrayType)
-	if !ok {
-		p.errorf(`bad map bucket type: "keys" is not an array`)
-		return
-	}
-	keysStride, ok := p.arrayStride(keysType)
-	if !ok {
-		p.errorf("unknown key size")
-		keysStride = 1
-	}
-	valuesField, err := getField(bt, "values")
-	if err != nil {
-		p.errorf("%s", err)
-		return
-	}
-	valuesType, ok := valuesField.Type.(*dwarf.ArrayType)
-	if !ok {
-		p.errorf(`bad map bucket type: "values" is not an array`)
-		return
-	}
-	valuesStride, ok := p.arrayStride(valuesType)
-	if !ok {
-		p.errorf("unknown value size")
-		valuesStride = 1
-	}
-
-	for i := uint64(0); i < numBuckets; i++ {
-		bucketAddr := a + i*bucketSize
-		// TODO: check for repeated bucket pointers.
-		for bucketAddr != 0 {
-			for j := uint64(0); j < bucketCnt; j++ {
-				tophash, err := p.server.peekUint8(bucketAddr + uint64(tophashField.ByteOffset) + j)
-				if err != nil {
-					p.errorf("reading map: ", err)
-					return
-				}
-				if tophash < minTopHash {
-					continue
-				}
-
-				// Limit how many values are printed per map.
-				*numValues++
-				if *numValues > maxMapValuesToPrint {
-					p.printf(", ...")
-					return
-				}
-				if *numValues > 1 {
-					p.printf(", ")
-				}
-
-				p.printValueAt(keysType.Type,
-					bucketAddr+uint64(keysField.ByteOffset)+j*keysStride)
-				p.printf(":")
-				p.printValueAt(valuesType.Type,
-					bucketAddr+uint64(valuesField.ByteOffset)+j*valuesStride)
-			}
-
-			bucketAddr, err = p.server.peekPtrStructField(bt, bucketAddr, "overflow")
-			if err != nil {
-				p.errorf("reading map: ", err)
-				return
-			}
-		}
-	}
+	p.printf("]")
 }
 
 func (p *Printer) printChannelAt(ct *dwarf.ChanType, a uint64) {