gob: make the debugging (dumping) code work again. Mostly rewrite it, in fact.
It's still not compiled in by default.

R=rsc, r2
CC=golang-dev
https://golang.org/cl/2754043
diff --git a/src/pkg/gob/codec_test.go b/src/pkg/gob/codec_test.go
index 9c1815a..1d9ac84 100644
--- a/src/pkg/gob/codec_test.go
+++ b/src/pkg/gob/codec_test.go
@@ -1269,3 +1269,50 @@
 		t.Error("normal float did not decode correctly")
 	}
 }
+
+// A type that won't be defined in the gob until we send it in an interface value.
+type OnTheFly struct {
+	a int
+}
+
+type DT struct {
+	//	X OnTheFly
+	a     int
+	b     string
+	c     float
+	i     interface{}
+	j     interface{}
+	i_nil interface{}
+	m     map[string]int
+	r     [3]int
+	s     []string
+}
+
+func TestDebug(t *testing.T) {
+	if debugFunc == nil {
+		return
+	}
+	Register(OnTheFly{})
+	var dt DT
+	dt.a = 17
+	dt.b = "hello"
+	dt.c = 3.14159
+	dt.i = 271828
+	dt.j = OnTheFly{3}
+	dt.i_nil = nil
+	dt.m = map[string]int{"one": 1, "two": 2}
+	dt.r = [3]int{11, 22, 33}
+	dt.s = []string{"hi", "joe"}
+	b := new(bytes.Buffer)
+	err := NewEncoder(b).Encode(dt)
+	if err != nil {
+		t.Fatal("encode:", err)
+	}
+	debugBuffer := bytes.NewBuffer(b.Bytes())
+	dt2 := &DT{}
+	err = NewDecoder(b).Decode(&dt2)
+	if err != nil {
+		t.Error("decode:", err)
+	}
+	debugFunc(debugBuffer)
+}
diff --git a/src/pkg/gob/debug.go b/src/pkg/gob/debug.go
index 0e07bb5..72645bd 100644
--- a/src/pkg/gob/debug.go
+++ b/src/pkg/gob/debug.go
@@ -6,152 +6,227 @@
 	"bytes"
 	"fmt"
 	"io"
-	"log"
 	"os"
+	"reflect"
+	"runtime"
 )
 
+var dump = false // If true, print the remaining bytes in the input buffer at each item.
+
+// Init installs the debugging facility. If this file is not compiled in the
+// package, Debug will be a no-op.
+func init() {
+	debugFunc = Debug
+}
+
 // Debug prints a human-readable representation of the gob data read from r.
-func Debug(r io.Reader) { NewDecoder(r).debug() }
+func Debug(r io.Reader) {
+	defer func() {
+		if e := recover(); e != nil {
+			if _, ok := e.(runtime.Error); ok {
+				panic(e)
+			}
+			fmt.Printf("error during debugging: %v\n", e)
+		}
+	}()
+	NewDecoder(r).debug()
+}
+
+// debugRecv is like recv but prints what it sees.
+func (dec *Decoder) debugRecv() {
+	if dec.byteBuffer != nil && dec.byteBuffer.Len() != 0 {
+		fmt.Printf("error in recv: %d bytes left in input buffer\n", dec.byteBuffer.Len())
+		return
+	}
+	// Read a count.
+	var nbytes uint64
+	nbytes, dec.err = decodeUintReader(dec.r, dec.countBuf[0:])
+	if dec.err != nil {
+		fmt.Printf("receiver error on count: %s\n", dec.err)
+		return
+	}
+	// Allocate the buffer.
+	if nbytes > uint64(len(dec.buf)) {
+		dec.buf = make([]byte, nbytes+1000)
+	}
+	dec.byteBuffer = bytes.NewBuffer(dec.buf[0:nbytes])
+
+	// Read the data
+	_, dec.err = io.ReadFull(dec.r, dec.buf[0:nbytes])
+	if dec.err != nil {
+		fmt.Printf("receiver error on data: %s\n", dec.err)
+		if dec.err == os.EOF {
+			dec.err = io.ErrUnexpectedEOF
+		}
+		return
+	}
+	if dump {
+		fmt.Printf("received %d bytes:\n\t% x\n", nbytes, dec.byteBuffer.Bytes())
+	}
+}
+
 
 // debug is like Decode but just prints what it finds.  It should be safe even for corrupted data.
 func (dec *Decoder) debug() {
-	dec.state.err = nil
-	for {
-		// Read a count.
-		var nbytes uint64
-		nbytes, dec.state.err = decodeUintReader(dec.r, dec.countBuf[0:])
-		if dec.state.err != nil {
-			break
-		}
+	// Make sure we're single-threaded through here.
+	dec.mutex.Lock()
+	defer dec.mutex.Unlock()
 
-		// Allocate the buffer.
-		if nbytes > uint64(len(dec.buf)) {
-			dec.buf = make([]byte, nbytes+1000)
-		}
-		dec.state.b = bytes.NewBuffer(dec.buf[0:nbytes])
+	dec.err = nil
+	dec.debugRecv()
+	if dec.err != nil {
+		return
+	}
+	dec.debugFromBuffer(0)
+}
 
-		// Read the data
-		_, dec.state.err = io.ReadFull(dec.r, dec.buf[0:nbytes])
-		if dec.state.err != nil {
-			if dec.state.err == os.EOF {
-				dec.state.err = io.ErrUnexpectedEOF
-			}
-			break
-		}
-
+// printFromBuffer prints the next value.  The buffer contains data, but it may
+// be a type descriptor and we may need to load more data to see the value;
+// printType takes care of that.
+func (dec *Decoder) debugFromBuffer(indent int) {
+	for dec.state.b.Len() > 0 {
 		// Receive a type id.
 		id := typeId(decodeInt(dec.state))
-		if dec.state.err != nil {
-			break
-		}
 
 		// Is it a new type?
 		if id < 0 { // 0 is the error state, handled above
 			// If the id is negative, we have a type.
-			fmt.Printf("new type id %d\n", -id)
-			dec.printType(-id)
-			if dec.state.err != nil {
+			dec.debugRecvType(-id)
+			if dec.err != nil {
 				break
 			}
 			continue
 		}
 
-		fmt.Printf("type id %d\n", id)
 		// No, it's a value.
-		// Make sure the type has been defined already.
-		_, ok := dec.wireType[id]
-		if !ok {
-			dec.state.err = errBadType
+		// Make sure the type has been defined already or is a builtin type (for
+		// top-level singleton values).
+		if dec.wireType[id] == nil && builtinIdToType[id] == nil {
+			dec.err = errBadType
 			break
 		}
-		fmt.Printf("\t%d bytes:\t% x\n", nbytes, dec.state.b.Bytes())
-		dec.printData(0, id)
+		dec.debugPrint(indent, id)
 		break
 	}
-	if dec.state.err != nil {
-		log.Print("debug:", dec.state.err)
-	}
 }
 
-func (dec *Decoder) printType(id typeId) {
+func (dec *Decoder) debugRecvType(id typeId) {
 	// Have we already seen this type?  That's an error
 	if _, alreadySeen := dec.wireType[id]; alreadySeen {
-		dec.state.err = os.ErrorString("gob: duplicate type received")
+		dec.err = os.ErrorString("gob: duplicate type received")
 		return
 	}
 
 	// Type:
 	wire := new(wireType)
-	dec.state.err = dec.decode(tWireType, wire)
-	if dec.state.err == nil {
+	dec.err = dec.decode(tWireType, reflect.NewValue(wire))
+	if dec.err == nil {
 		printWireType(wire)
 	}
 	// Remember we've seen this type.
 	dec.wireType[id] = wire
+
+	// Load the next parcel.
+	dec.debugRecv()
 }
 
 func printWireType(wire *wireType) {
+	fmt.Printf("type definition {\n")
 	switch {
-	case wire.array != nil:
-		printCommonType("array", &wire.array.commonType)
-		fmt.Printf("\tlen %d\n\telemid %d\n", wire.array.Len, wire.array.Elem)
-	case wire.slice != nil:
-		printCommonType("slice", &wire.slice.commonType)
-		fmt.Printf("\telemid %d\n", wire.slice.Elem)
-	case wire.strct != nil:
-		printCommonType("struct", &wire.strct.commonType)
-		for i, field := range wire.strct.field {
+	case wire.arrayT != nil:
+		printCommonType("array", &wire.arrayT.commonType)
+		fmt.Printf("\tlen %d\n\telemid %d\n", wire.arrayT.Len, wire.arrayT.Elem)
+	case wire.mapT != nil:
+		printCommonType("map", &wire.mapT.commonType)
+		fmt.Printf("\tkeyid %d\n", wire.mapT.Key)
+		fmt.Printf("\telemid %d\n", wire.mapT.Elem)
+	case wire.sliceT != nil:
+		printCommonType("slice", &wire.sliceT.commonType)
+		fmt.Printf("\telemid %d\n", wire.sliceT.Elem)
+	case wire.structT != nil:
+		printCommonType("struct", &wire.structT.commonType)
+		for i, field := range wire.structT.field {
 			fmt.Printf("\tfield %d:\t%s\tid=%d\n", i, field.name, field.id)
 		}
 	}
+	fmt.Printf("}\n")
 }
 
 func printCommonType(kind string, common *commonType) {
-	fmt.Printf("\t%s %s\n\tid: %d\n", kind, common.name, common._id)
+	fmt.Printf("\t%s %q\n\tid: %d\n", kind, common.name, common._id)
 }
 
-func (dec *Decoder) printData(indent int, id typeId) {
-	if dec.state.err != nil {
-		return
+func (dec *Decoder) debugPrint(indent int, id typeId) {
+	wire, ok := dec.wireType[id]
+	if ok && wire.structT != nil {
+		dec.debugStruct(indent+1, id, wire)
+	} else {
+		dec.debugSingle(indent+1, id, wire)
 	}
+}
+
+func (dec *Decoder) debugSingle(indent int, id typeId, wire *wireType) {
 	// is it a builtin type?
 	_, ok := builtinIdToType[id]
+	if !ok && wire == nil {
+		errorf("type id %d not defined\n", id)
+	}
+	decodeUint(dec.state)
+	dec.printItem(indent, id)
+}
+
+func (dec *Decoder) printItem(indent int, id typeId) {
+	if dump {
+		fmt.Printf("print item %d bytes: % x\n", dec.state.b.Len(), dec.state.b.Bytes())
+	}
+	_, ok := builtinIdToType[id]
 	if ok {
 		dec.printBuiltin(indent, id)
 		return
 	}
 	wire, ok := dec.wireType[id]
 	if !ok {
-		fmt.Printf("type id %d not defined\n", id)
-		return
+		errorf("type id %d not defined\n", id)
 	}
 	switch {
-	case wire.array != nil:
-		dec.printArray(indent+1, wire)
-	case wire.slice != nil:
-		dec.printSlice(indent+1, wire)
-	case wire.strct != nil:
-		dec.printStruct(indent+1, wire)
+	case wire.arrayT != nil:
+		dec.printArray(indent, wire)
+	case wire.mapT != nil:
+		dec.printMap(indent, wire)
+	case wire.sliceT != nil:
+		dec.printSlice(indent, wire)
+	case wire.structT != nil:
+		dec.debugStruct(indent, id, wire)
 	}
 }
 
 func (dec *Decoder) printArray(indent int, wire *wireType) {
-	elemId := wire.array.Elem
+	elemId := wire.arrayT.Elem
 	n := int(decodeUint(dec.state))
-	for i := 0; i < n && dec.state.err == nil; i++ {
-		dec.printData(indent, elemId)
+	for i := 0; i < n && dec.err == nil; i++ {
+		dec.printItem(indent, elemId)
 	}
-	if n != wire.array.Len {
+	if n != wire.arrayT.Len {
 		tab(indent)
-		fmt.Printf("(wrong length for array: %d should be %d)\n", n, wire.array.Len)
+		fmt.Printf("(wrong length for array: %d should be %d)\n", n, wire.arrayT.Len)
+	}
+}
+
+func (dec *Decoder) printMap(indent int, wire *wireType) {
+	keyId := wire.mapT.Key
+	elemId := wire.mapT.Elem
+	n := int(decodeUint(dec.state))
+	for i := 0; i < n && dec.err == nil; i++ {
+		dec.printItem(indent, keyId)
+		dec.printItem(indent+1, elemId)
 	}
 }
 
 func (dec *Decoder) printSlice(indent int, wire *wireType) {
-	elemId := wire.slice.Elem
+	elemId := wire.sliceT.Elem
 	n := int(decodeUint(dec.state))
-	for i := 0; i < n && dec.state.err == nil; i++ {
-		dec.printData(indent, elemId)
+	for i := 0; i < n && dec.err == nil; i++ {
+		dec.printItem(indent, elemId)
 	}
 }
 
@@ -160,57 +235,72 @@
 	switch id {
 	case tBool:
 		if decodeInt(dec.state) == 0 {
-			fmt.Printf("false")
+			fmt.Printf("false\n")
 		} else {
-			fmt.Printf("true")
+			fmt.Printf("true\n")
 		}
 	case tInt:
-		fmt.Printf("%d", decodeInt(dec.state))
+		fmt.Printf("%d\n", decodeInt(dec.state))
 	case tUint:
-		fmt.Printf("%d", decodeUint(dec.state))
+		fmt.Printf("%d\n", decodeUint(dec.state))
 	case tFloat:
-		fmt.Printf("%g", floatFromBits(decodeUint(dec.state)))
+		fmt.Printf("%g\n", floatFromBits(decodeUint(dec.state)))
 	case tBytes:
 		b := make([]byte, decodeUint(dec.state))
 		dec.state.b.Read(b)
-		fmt.Printf("% x", b)
+		fmt.Printf("% x\n", b)
 	case tString:
 		b := make([]byte, decodeUint(dec.state))
 		dec.state.b.Read(b)
-		fmt.Printf("%q", b)
+		fmt.Printf("%q\n", b)
+	case tInterface:
+		b := make([]byte, decodeUint(dec.state))
+		dec.state.b.Read(b)
+		if len(b) == 0 {
+			fmt.Printf("nil interface")
+		} else {
+			fmt.Printf("interface value; type %q\n", b)
+			dec.debugFromBuffer(indent)
+		}
 	default:
-		fmt.Print("unknown")
+		fmt.Print("unknown\n")
 	}
-	fmt.Print("\n")
 }
 
-func (dec *Decoder) printStruct(indent int, wire *wireType) {
-	strct := wire.strct
+func (dec *Decoder) debugStruct(indent int, id typeId, wire *wireType) {
+	tab(indent)
+	fmt.Printf("%s struct {\n", id.Name())
+	strct := wire.structT
 	state := newDecodeState(dec.state.b)
 	state.fieldnum = -1
-	for state.err == nil {
+	for dec.err == nil {
 		delta := int(decodeUint(state))
 		if delta < 0 {
-			dec.state.err = os.ErrorString("gob decode: corrupted data: negative delta")
-			return
+			errorf("gob decode: corrupted data: negative delta")
 		}
-		if state.err != nil || delta == 0 { // struct terminator is zero delta fieldnum
-			return
+		if delta == 0 { // struct terminator is zero delta fieldnum
+			break
 		}
-		fieldnum := state.fieldnum + delta
-		if fieldnum < 0 || fieldnum >= len(strct.field) {
-			dec.state.err = os.ErrorString("field number out of range")
-			return
+		fieldNum := state.fieldnum + delta
+		if fieldNum < 0 || fieldNum >= len(strct.field) {
+			errorf("field number out of range")
+			break
 		}
 		tab(indent)
-		fmt.Printf("field %d:\n", fieldnum)
-		dec.printData(indent+1, strct.field[fieldnum].id)
-		state.fieldnum = fieldnum
+		fmt.Printf("%s(%d):\n", wire.structT.field[fieldNum].name, fieldNum)
+		dec.printItem(indent+1, strct.field[fieldNum].id)
+		state.fieldnum = fieldNum
 	}
+	tab(indent)
+	fmt.Printf(" } // end %s struct\n", id.Name())
 }
 
 func tab(indent int) {
-	for i := 0; i < indent; i++ {
-		fmt.Print("\t")
+	for i, w := 0, 0; i < indent; i += w {
+		w = 10
+		if i+w > indent {
+			w = indent - i
+		}
+		fmt.Print("\t\t\t\t\t\t\t\t\t\t"[:w])
 	}
 }
diff --git a/src/pkg/gob/decoder.go b/src/pkg/gob/decoder.go
index b86bdf3..4237d01 100644
--- a/src/pkg/gob/decoder.go
+++ b/src/pkg/gob/decoder.go
@@ -108,9 +108,6 @@
 	for dec.state.b.Len() > 0 {
 		// Receive a type id.
 		id := typeId(decodeInt(dec.state))
-		if dec.err != nil {
-			break
-		}
 
 		// Is it a new type?
 		if id < 0 { // 0 is the error state, handled above
@@ -155,3 +152,7 @@
 	dec.decodeValueFromBuffer(value, false)
 	return dec.err
 }
+
+// If enabled, Debug prints a human-readable representation of the gob data read from r.
+// If debug.go is compiled into the program it will override this link.
+var debugFunc func(io.Reader)