ogle/program/server: print maps
Add code to pretty-print maps. Depends on the internals of the map
implementation but there's no way around that.

LGTM=nigeltao
R=nigeltao
https://golang.org/cl/117810045
diff --git a/arch/arch.go b/arch/arch.go
index 7a2a65d..441feed 100644
--- a/arch/arch.go
+++ b/arch/arch.go
@@ -41,6 +41,30 @@
 	panic("no IntSize")
 }
 
+func (a *Architecture) Int16(buf []byte) int16 {
+	return int16(a.Uint16(buf))
+}
+
+func (a *Architecture) Int32(buf []byte) int32 {
+	return int32(a.Uint32(buf))
+}
+
+func (a *Architecture) Int64(buf []byte) int64 {
+	return int64(a.Uint64(buf))
+}
+
+func (a *Architecture) Uint16(buf []byte) uint16 {
+	return a.ByteOrder.Uint16(buf)
+}
+
+func (a *Architecture) Uint32(buf []byte) uint32 {
+	return a.ByteOrder.Uint32(buf)
+}
+
+func (a *Architecture) Uint64(buf []byte) uint64 {
+	return a.ByteOrder.Uint64(buf)
+}
+
 func (a *Architecture) IntN(buf []byte) int64 {
 	return int64(a.UintN(buf))
 }
diff --git a/program/server/print.go b/program/server/print.go
index ef458db..7a11262 100644
--- a/program/server/print.go
+++ b/program/server/print.go
@@ -282,7 +282,17 @@
 	p.printf("}")
 }
 
-// TODO: Unimplemented.
+// mapDesc collects the information necessary to print a map.
+type mapDesc struct {
+	typ        *dwarf.MapType
+	count      int
+	numBuckets int
+	keySize    uintptr
+	elemSize   uintptr
+	bucketSize uintptr
+	buf        []byte
+}
+
 func (p *Printer) printMapAt(typ *dwarf.MapType, addr uintptr) {
 	// Maps are pointers to a struct type.
 	structType := typ.Type.(*dwarf.PtrType).Type.(*dwarf.StructType)
@@ -295,11 +305,82 @@
 	if !p.peek(addr, structType.ByteSize) {
 		return
 	}
-	// TODO: unimplemented. Hard.
-	countField := structType.Field[0]
-	p.printf("%s with ", typ)
-	p.printValueAt(countField.Type, addr+uintptr(countField.ByteOffset))
-	p.printf(" elements")
+	// From runtime/hashmap.*; We need to walk the map data structure.
+	// Load the struct, then iterate over the buckets.
+	// uintgo count (occupancy).
+	offset := int(structType.Field[0].ByteOffset)
+	count := int(p.arch.Uint(p.tmp[offset : offset+p.arch.IntSize]))
+	// uint8 Log2 of number of buckets.
+	b := uint(p.tmp[structType.Field[3].ByteOffset])
+	// uint8 key size in bytes.
+	keySize := uintptr(p.tmp[structType.Field[4].ByteOffset])
+	// uint8 element size in bytes.
+	elemSize := uintptr(p.tmp[structType.Field[5].ByteOffset])
+	// uint16 bucket size in bytes.
+	bucketSize := uintptr(p.arch.Uint16(p.tmp[structType.Field[6].ByteOffset:]))
+	// pointer to buckets
+	offset = int(structType.Field[7].ByteOffset)
+	bucketPtr := uintptr(p.arch.Uintptr(p.tmp[offset : offset+p.arch.PointerSize]))
+	// pointer to old buckets.
+	offset = int(structType.Field[8].ByteOffset)
+	oldBucketPtr := uintptr(p.arch.Uintptr(p.tmp[offset : offset+p.arch.PointerSize]))
+	// Ready to print.
+	p.printf("%s{", typ)
+	desc := mapDesc{
+		typ:        typ,
+		count:      count,
+		numBuckets: 1 << b,
+		keySize:    keySize,
+		elemSize:   elemSize,
+		bucketSize: bucketSize,
+		buf:        make([]byte, bucketSize),
+	}
+	p.printMapBucketsAt(desc, bucketPtr)
+	p.printMapBucketsAt(desc, oldBucketPtr)
+	p.printf("}")
+}
+
+// Map bucket layout from runtime/hashmap.*
+const (
+	bucketCnt  = 8 // BUCKETSIZE (poor name) in C code.
+	minTopHash = 4
+)
+
+func (p *Printer) printMapBucketsAt(desc mapDesc, addr uintptr) {
+	if addr == 0 {
+		return
+	}
+	for i := 0; desc.count > 0 && i < desc.numBuckets; i++ {
+		// Load this bucket struct into memory and initialize "pointers" to the key and value slices.
+		// After the header, the bucket struct has an array of keys followed by an array of elements.
+		if !p.ok(p.peeker.peek(addr, desc.buf)) {
+			return
+		}
+		// TODO: We have loaded the data but printValueAt loads from remote addresses
+		// so we need to keep track of addresses and read memory twice. It would
+		// be nice to avoid this overhead.
+		keyAddr := addr + bucketCnt + uintptr(p.arch.PointerSize)
+		elemAddr := keyAddr + bucketCnt*desc.keySize
+		addr += desc.bucketSize // Advance to next bucket; buf, keyAddr and elemAddr are all we need now.
+		// tophash uint8 [bucketCnt] tells us which buckets are occupied.
+		tophash := desc.buf[:bucketCnt]
+		for j := 0; desc.count > 0 && j < bucketCnt; j++ {
+			if tophash[j] >= minTopHash {
+				p.printValueAt(desc.typ.KeyType, keyAddr)
+				p.printf(":")
+				p.printValueAt(desc.typ.ElemType, elemAddr)
+				desc.count--
+				if desc.count > 0 {
+					p.printf(", ")
+				}
+			}
+			keyAddr += desc.keySize
+			elemAddr += desc.elemSize
+		}
+		// pointer to overflow bucket, if any.
+		overflow := uintptr(p.arch.Uintptr(desc.buf[bucketCnt : bucketCnt+p.arch.PointerSize]))
+		p.printMapBucketsAt(desc, overflow)
+	}
 }
 
 func (p *Printer) printSliceAt(typ *dwarf.SliceType, addr uintptr) {