mirror of
https://github.com/golang/go
synced 2024-11-19 00:44:40 -07:00
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
This commit is contained in:
parent
907e998cba
commit
96b9efe8a9
@ -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)
|
||||
}
|
||||
|
@ -6,152 +6,227 @@ import (
|
||||
"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])
|
||||
|
||||
// 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
|
||||
}
|
||||
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])
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user