Commit e7601e29 authored by Rob Pike's avatar Rob Pike

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
https://golang.org/cl/183075
parent 1f551156
...@@ -13,3 +13,11 @@ GOFILES=\ ...@@ -13,3 +13,11 @@ GOFILES=\
type.go\ type.go\
include ../../Make.pkg 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) $<
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")
}
}
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)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment