]> Cypherpunks repositories - gostls13.git/commitdiff
gob: make the debugging (dumping) code work again. Mostly rewrite it, in fact.
authorRob Pike <r@golang.org>
Fri, 29 Oct 2010 22:07:56 +0000 (15:07 -0700)
committerRob Pike <r@golang.org>
Fri, 29 Oct 2010 22:07:56 +0000 (15:07 -0700)
It's still not compiled in by default.

R=rsc, r2
CC=golang-dev
https://golang.org/cl/2754043

src/pkg/gob/codec_test.go
src/pkg/gob/debug.go
src/pkg/gob/decoder.go

index 9c1815af9b75743619ce36a39aa3aea3745b984a..1d9ac843889cc11488bd1d2fa4523cfb02175467 100644 (file)
@@ -1269,3 +1269,50 @@ func TestIgnoreInterface(t *testing.T) {
                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)
+}
index 0e07bb588e7181387bec483e38591db4b85f8134..72645bdf0752e044c1cc2d468d513a8705169b0b 100644 (file)
@@ -6,152 +6,227 @@ import (
        "bytes"
        "fmt"
        "io"
-       "log"
        "os"
+       "reflect"
+       "runtime"
 )
 
-// Debug prints a human-readable representation of the gob data read from r.
-func Debug(r io.Reader) { NewDecoder(r).debug() }
+var dump = false // If true, print the remaining bytes in the input buffer at each item.
 
-// 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
-               }
+// Init installs the debugging facility. If this file is not compiled in the
+// package, Debug will be a no-op.
+func init() {
+       debugFunc = Debug
+}
 
-               // Allocate the buffer.
-               if nbytes > uint64(len(dec.buf)) {
-                       dec.buf = make([]byte, nbytes+1000)
+// Debug prints a human-readable representation of the gob data read from r.
+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)
                }
-               dec.state.b = bytes.NewBuffer(dec.buf[0:nbytes])
+       }()
+       NewDecoder(r).debug()
+}
 
-               // 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
+// 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() {
+       // Make sure we're single-threaded through here.
+       dec.mutex.Lock()
+       defer dec.mutex.Unlock()
+
+       dec.err = nil
+       dec.debugRecv()
+       if dec.err != nil {
+               return
+       }
+       dec.debugFromBuffer(0)
+}
 
+// 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 @@ func (dec *Decoder) printBuiltin(indent int, id typeId) {
        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])
        }
 }
index b86bdf3985e3631a50c540ec1a13aa109ac864f4..4237d01aca39bda8ec7aeb558224c859a535ab03 100644 (file)
@@ -108,9 +108,6 @@ func (dec *Decoder) decodeValueFromBuffer(value reflect.Value, ignore bool) {
        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 @@ func (dec *Decoder) DecodeValue(value reflect.Value) os.Error {
        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)