aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Pike <r@golang.org>2010-10-29 15:07:56 -0700
committerRob Pike <r@golang.org>2010-10-29 15:07:56 -0700
commit96b9efe8a96cb56be756861f337a9cfce822e25b (patch)
tree411d8c12e1b1361078faf1d7643757bbbe6cbbc4
parent907e998cba8a5313639d020040f24ac6b4bc1baa (diff)
downloadgo-96b9efe8a96cb56be756861f337a9cfce822e25b.tar.gz
go-96b9efe8a96cb56be756861f337a9cfce822e25b.zip
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
-rw-r--r--src/pkg/gob/codec_test.go47
-rw-r--r--src/pkg/gob/debug.go286
-rw-r--r--src/pkg/gob/decoder.go7
3 files changed, 239 insertions, 101 deletions
diff --git a/src/pkg/gob/codec_test.go b/src/pkg/gob/codec_test.go
index 9c1815af9b..1d9ac84388 100644
--- a/src/pkg/gob/codec_test.go
+++ b/src/pkg/gob/codec_test.go
@@ -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)
+}
diff --git a/src/pkg/gob/debug.go b/src/pkg/gob/debug.go
index 0e07bb588e..72645bdf07 100644
--- a/src/pkg/gob/debug.go
+++ b/src/pkg/gob/debug.go
@@ -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])
}
}
diff --git a/src/pkg/gob/decoder.go b/src/pkg/gob/decoder.go
index b86bdf3985..4237d01aca 100644
--- a/src/pkg/gob/decoder.go
+++ b/src/pkg/gob/decoder.go
@@ -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)