summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Pike <r@golang.org>2009-12-29 14:03:33 +1100
committerRob Pike <r@golang.org>2009-12-29 14:03:33 +1100
commit4a1ccd48368bc73dd4ed53e91792ad1716a1adde (patch)
tree872c1e5b21e95e7821f4d2d79de4051e967ea784
parentfbc974f5c2061dc8467a87a0f22d4c7288485e3d (diff)
downloadgolang-4a1ccd48368bc73dd4ed53e91792ad1716a1adde.tar.gz
add a debugging printer to the gob package.
used only for debugging, debug.go is not normally part of the package source. also add a dump program to call it. R=rsc CC=golang-dev http://codereview.appspot.com/183075
-rw-r--r--src/pkg/gob/Makefile8
-rw-r--r--src/pkg/gob/debug.go216
-rw-r--r--src/pkg/gob/dump.go22
3 files changed, 246 insertions, 0 deletions
diff --git a/src/pkg/gob/Makefile b/src/pkg/gob/Makefile
index 5b175b347..1091adb01 100644
--- a/src/pkg/gob/Makefile
+++ b/src/pkg/gob/Makefile
@@ -13,3 +13,11 @@ GOFILES=\
type.go\
include ../../Make.pkg
+
+# Help for debugging. Requires adding debug.go to the gob package as well.
+
+dump: dump.$O
+ $(LD) -o dump $<
+
+dump.$O: dump.go
+ $(GC) $<
diff --git a/src/pkg/gob/debug.go b/src/pkg/gob/debug.go
new file mode 100644
index 000000000..02f2602ed
--- /dev/null
+++ b/src/pkg/gob/debug.go
@@ -0,0 +1,216 @@
+package gob
+
+// This file is not normally included in the gob package. Used only for debugging the package itself.
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+ "os"
+)
+
+// Debug prints a human-readable representation of the gob data read from r.
+func Debug(r io.Reader) { NewDecoder(r).debug() }
+
+// 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
+ }
+
+ // Allocate the buffer.
+ if nbytes > uint64(len(dec.buf)) {
+ dec.buf = make([]byte, nbytes+1000)
+ }
+ dec.state.b = bytes.NewBuffer(dec.buf[0:nbytes])
+
+ // 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
+ }
+
+ // 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 {
+ 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
+ break
+ }
+ fmt.Printf("\t%d bytes:\t% x\n", nbytes, dec.state.b.Bytes())
+ dec.printData(0, id)
+ break
+ }
+ if dec.state.err != nil {
+ log.Stderr("debug:", dec.state.err)
+ }
+}
+
+func (dec *Decoder) printType(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")
+ return
+ }
+
+ // Type:
+ wire := new(wireType)
+ dec.state.err = dec.decode(tWireType, wire)
+ if dec.state.err == nil {
+ printWireType(wire)
+ }
+ // Remember we've seen this type.
+ dec.wireType[id] = wire
+}
+
+func printWireType(wire *wireType) {
+ 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 {
+ fmt.Printf("\tfield %d:\t%s\tid=%d\n", i, field.name, field.id)
+ }
+ }
+}
+
+func printCommonType(kind string, common *commonType) {
+ fmt.Printf("\t%s %s\n\tid: %d\n", kind, common.name, common._id)
+}
+
+func (dec *Decoder) printData(indent int, id typeId) {
+ if dec.state.err != nil {
+ return
+ }
+ // is it a builtin type?
+ _, 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
+ }
+ 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)
+ }
+}
+
+func (dec *Decoder) printArray(indent int, wire *wireType) {
+ elemId := wire.array.Elem
+ n := int(decodeUint(dec.state))
+ for i := 0; i < n && dec.state.err == nil; i++ {
+ dec.printData(indent, elemId)
+ }
+ if n != wire.array.Len {
+ tab(indent)
+ fmt.Printf("(wrong length for array: %d should be %d)\n", n, wire.array.Len)
+ }
+}
+
+func (dec *Decoder) printSlice(indent int, wire *wireType) {
+ elemId := wire.slice.Elem
+ n := int(decodeUint(dec.state))
+ for i := 0; i < n && dec.state.err == nil; i++ {
+ dec.printData(indent, elemId)
+ }
+}
+
+func (dec *Decoder) printBuiltin(indent int, id typeId) {
+ tab(indent)
+ switch id {
+ case tBool:
+ if decodeInt(dec.state) == 0 {
+ fmt.Printf("false")
+ } else {
+ fmt.Printf("true")
+ }
+ case tInt:
+ fmt.Printf("%d", decodeInt(dec.state))
+ case tUint:
+ fmt.Printf("%d", decodeUint(dec.state))
+ case tFloat:
+ fmt.Printf("%g", floatFromBits(decodeUint(dec.state)))
+ case tBytes:
+ b := make([]byte, decodeUint(dec.state))
+ dec.state.b.Read(b)
+ fmt.Printf("% x", b)
+ case tString:
+ b := make([]byte, decodeUint(dec.state))
+ dec.state.b.Read(b)
+ fmt.Printf("%q", b)
+ default:
+ fmt.Print("unknown")
+ }
+ fmt.Print("\n")
+}
+
+func (dec *Decoder) printStruct(indent int, wire *wireType) {
+ strct := wire.strct
+ state := newDecodeState(dec.state.b)
+ state.fieldnum = -1
+ for state.err == nil {
+ delta := int(decodeUint(state))
+ if delta < 0 {
+ dec.state.err = os.ErrorString("gob decode: corrupted data: negative delta")
+ return
+ }
+ if state.err != nil || delta == 0 { // struct terminator is zero delta fieldnum
+ return
+ }
+ fieldnum := state.fieldnum + delta
+ if fieldnum < 0 || fieldnum >= len(strct.field) {
+ dec.state.err = os.ErrorString("field number out of range")
+ return
+ }
+ tab(indent)
+ fmt.Printf("field %d:\n", fieldnum)
+ dec.printData(indent+1, strct.field[fieldnum].id)
+ state.fieldnum = fieldnum
+ }
+}
+
+func tab(indent int) {
+ for i := 0; i < indent; i++ {
+ fmt.Print("\t")
+ }
+}
diff --git a/src/pkg/gob/dump.go b/src/pkg/gob/dump.go
new file mode 100644
index 000000000..a05510566
--- /dev/null
+++ b/src/pkg/gob/dump.go
@@ -0,0 +1,22 @@
+package main
+
+// Need to compile package gob with debug.go to build this program.
+
+import (
+ "fmt"
+ "gob"
+ "os"
+)
+
+func main() {
+ var err os.Error
+ file := os.Stdin
+ if len(os.Args) > 1 {
+ file, err = os.Open(os.Args[1], os.O_RDONLY, 0)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "dump: %s\n", err)
+ os.Exit(1)
+ }
+ }
+ gob.Debug(file)
+}