mirror of
https://github.com/golang/go
synced 2024-11-19 04:34:39 -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")
|
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"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"os"
|
"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.
|
// 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.
|
// debug is like Decode but just prints what it finds. It should be safe even for corrupted data.
|
||||||
func (dec *Decoder) debug() {
|
func (dec *Decoder) debug() {
|
||||||
dec.state.err = nil
|
// Make sure we're single-threaded through here.
|
||||||
for {
|
dec.mutex.Lock()
|
||||||
// Read a count.
|
defer dec.mutex.Unlock()
|
||||||
var nbytes uint64
|
|
||||||
nbytes, dec.state.err = decodeUintReader(dec.r, dec.countBuf[0:])
|
|
||||||
if dec.state.err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate the buffer.
|
dec.err = nil
|
||||||
if nbytes > uint64(len(dec.buf)) {
|
dec.debugRecv()
|
||||||
dec.buf = make([]byte, nbytes+1000)
|
if dec.err != nil {
|
||||||
}
|
return
|
||||||
dec.state.b = bytes.NewBuffer(dec.buf[0:nbytes])
|
}
|
||||||
|
dec.debugFromBuffer(0)
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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.
|
// Receive a type id.
|
||||||
id := typeId(decodeInt(dec.state))
|
id := typeId(decodeInt(dec.state))
|
||||||
if dec.state.err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is it a new type?
|
// Is it a new type?
|
||||||
if id < 0 { // 0 is the error state, handled above
|
if id < 0 { // 0 is the error state, handled above
|
||||||
// If the id is negative, we have a type.
|
// If the id is negative, we have a type.
|
||||||
fmt.Printf("new type id %d\n", -id)
|
dec.debugRecvType(-id)
|
||||||
dec.printType(-id)
|
if dec.err != nil {
|
||||||
if dec.state.err != nil {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("type id %d\n", id)
|
|
||||||
// No, it's a value.
|
// No, it's a value.
|
||||||
// Make sure the type has been defined already.
|
// Make sure the type has been defined already or is a builtin type (for
|
||||||
_, ok := dec.wireType[id]
|
// top-level singleton values).
|
||||||
if !ok {
|
if dec.wireType[id] == nil && builtinIdToType[id] == nil {
|
||||||
dec.state.err = errBadType
|
dec.err = errBadType
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
fmt.Printf("\t%d bytes:\t% x\n", nbytes, dec.state.b.Bytes())
|
dec.debugPrint(indent, id)
|
||||||
dec.printData(0, id)
|
|
||||||
break
|
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
|
// Have we already seen this type? That's an error
|
||||||
if _, alreadySeen := dec.wireType[id]; alreadySeen {
|
if _, alreadySeen := dec.wireType[id]; alreadySeen {
|
||||||
dec.state.err = os.ErrorString("gob: duplicate type received")
|
dec.err = os.ErrorString("gob: duplicate type received")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type:
|
// Type:
|
||||||
wire := new(wireType)
|
wire := new(wireType)
|
||||||
dec.state.err = dec.decode(tWireType, wire)
|
dec.err = dec.decode(tWireType, reflect.NewValue(wire))
|
||||||
if dec.state.err == nil {
|
if dec.err == nil {
|
||||||
printWireType(wire)
|
printWireType(wire)
|
||||||
}
|
}
|
||||||
// Remember we've seen this type.
|
// Remember we've seen this type.
|
||||||
dec.wireType[id] = wire
|
dec.wireType[id] = wire
|
||||||
|
|
||||||
|
// Load the next parcel.
|
||||||
|
dec.debugRecv()
|
||||||
}
|
}
|
||||||
|
|
||||||
func printWireType(wire *wireType) {
|
func printWireType(wire *wireType) {
|
||||||
|
fmt.Printf("type definition {\n")
|
||||||
switch {
|
switch {
|
||||||
case wire.array != nil:
|
case wire.arrayT != nil:
|
||||||
printCommonType("array", &wire.array.commonType)
|
printCommonType("array", &wire.arrayT.commonType)
|
||||||
fmt.Printf("\tlen %d\n\telemid %d\n", wire.array.Len, wire.array.Elem)
|
fmt.Printf("\tlen %d\n\telemid %d\n", wire.arrayT.Len, wire.arrayT.Elem)
|
||||||
case wire.slice != nil:
|
case wire.mapT != nil:
|
||||||
printCommonType("slice", &wire.slice.commonType)
|
printCommonType("map", &wire.mapT.commonType)
|
||||||
fmt.Printf("\telemid %d\n", wire.slice.Elem)
|
fmt.Printf("\tkeyid %d\n", wire.mapT.Key)
|
||||||
case wire.strct != nil:
|
fmt.Printf("\telemid %d\n", wire.mapT.Elem)
|
||||||
printCommonType("struct", &wire.strct.commonType)
|
case wire.sliceT != nil:
|
||||||
for i, field := range wire.strct.field {
|
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("\tfield %d:\t%s\tid=%d\n", i, field.name, field.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fmt.Printf("}\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func printCommonType(kind string, common *commonType) {
|
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) {
|
func (dec *Decoder) debugPrint(indent int, id typeId) {
|
||||||
if dec.state.err != nil {
|
wire, ok := dec.wireType[id]
|
||||||
return
|
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?
|
// is it a builtin type?
|
||||||
_, ok := builtinIdToType[id]
|
_, 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 {
|
if ok {
|
||||||
dec.printBuiltin(indent, id)
|
dec.printBuiltin(indent, id)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
wire, ok := dec.wireType[id]
|
wire, ok := dec.wireType[id]
|
||||||
if !ok {
|
if !ok {
|
||||||
fmt.Printf("type id %d not defined\n", id)
|
errorf("type id %d not defined\n", id)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case wire.array != nil:
|
case wire.arrayT != nil:
|
||||||
dec.printArray(indent+1, wire)
|
dec.printArray(indent, wire)
|
||||||
case wire.slice != nil:
|
case wire.mapT != nil:
|
||||||
dec.printSlice(indent+1, wire)
|
dec.printMap(indent, wire)
|
||||||
case wire.strct != nil:
|
case wire.sliceT != nil:
|
||||||
dec.printStruct(indent+1, wire)
|
dec.printSlice(indent, wire)
|
||||||
|
case wire.structT != nil:
|
||||||
|
dec.debugStruct(indent, id, wire)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dec *Decoder) printArray(indent int, wire *wireType) {
|
func (dec *Decoder) printArray(indent int, wire *wireType) {
|
||||||
elemId := wire.array.Elem
|
elemId := wire.arrayT.Elem
|
||||||
n := int(decodeUint(dec.state))
|
n := int(decodeUint(dec.state))
|
||||||
for i := 0; i < n && dec.state.err == nil; i++ {
|
for i := 0; i < n && dec.err == nil; i++ {
|
||||||
dec.printData(indent, elemId)
|
dec.printItem(indent, elemId)
|
||||||
}
|
}
|
||||||
if n != wire.array.Len {
|
if n != wire.arrayT.Len {
|
||||||
tab(indent)
|
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) {
|
func (dec *Decoder) printSlice(indent int, wire *wireType) {
|
||||||
elemId := wire.slice.Elem
|
elemId := wire.sliceT.Elem
|
||||||
n := int(decodeUint(dec.state))
|
n := int(decodeUint(dec.state))
|
||||||
for i := 0; i < n && dec.state.err == nil; i++ {
|
for i := 0; i < n && dec.err == nil; i++ {
|
||||||
dec.printData(indent, elemId)
|
dec.printItem(indent, elemId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,57 +235,72 @@ func (dec *Decoder) printBuiltin(indent int, id typeId) {
|
|||||||
switch id {
|
switch id {
|
||||||
case tBool:
|
case tBool:
|
||||||
if decodeInt(dec.state) == 0 {
|
if decodeInt(dec.state) == 0 {
|
||||||
fmt.Printf("false")
|
fmt.Printf("false\n")
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("true")
|
fmt.Printf("true\n")
|
||||||
}
|
}
|
||||||
case tInt:
|
case tInt:
|
||||||
fmt.Printf("%d", decodeInt(dec.state))
|
fmt.Printf("%d\n", decodeInt(dec.state))
|
||||||
case tUint:
|
case tUint:
|
||||||
fmt.Printf("%d", decodeUint(dec.state))
|
fmt.Printf("%d\n", decodeUint(dec.state))
|
||||||
case tFloat:
|
case tFloat:
|
||||||
fmt.Printf("%g", floatFromBits(decodeUint(dec.state)))
|
fmt.Printf("%g\n", floatFromBits(decodeUint(dec.state)))
|
||||||
case tBytes:
|
case tBytes:
|
||||||
b := make([]byte, decodeUint(dec.state))
|
b := make([]byte, decodeUint(dec.state))
|
||||||
dec.state.b.Read(b)
|
dec.state.b.Read(b)
|
||||||
fmt.Printf("% x", b)
|
fmt.Printf("% x\n", b)
|
||||||
case tString:
|
case tString:
|
||||||
b := make([]byte, decodeUint(dec.state))
|
b := make([]byte, decodeUint(dec.state))
|
||||||
dec.state.b.Read(b)
|
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:
|
default:
|
||||||
fmt.Print("unknown")
|
fmt.Print("unknown\n")
|
||||||
}
|
}
|
||||||
fmt.Print("\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dec *Decoder) printStruct(indent int, wire *wireType) {
|
func (dec *Decoder) debugStruct(indent int, id typeId, wire *wireType) {
|
||||||
strct := wire.strct
|
tab(indent)
|
||||||
|
fmt.Printf("%s struct {\n", id.Name())
|
||||||
|
strct := wire.structT
|
||||||
state := newDecodeState(dec.state.b)
|
state := newDecodeState(dec.state.b)
|
||||||
state.fieldnum = -1
|
state.fieldnum = -1
|
||||||
for state.err == nil {
|
for dec.err == nil {
|
||||||
delta := int(decodeUint(state))
|
delta := int(decodeUint(state))
|
||||||
if delta < 0 {
|
if delta < 0 {
|
||||||
dec.state.err = os.ErrorString("gob decode: corrupted data: negative delta")
|
errorf("gob decode: corrupted data: negative delta")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if state.err != nil || delta == 0 { // struct terminator is zero delta fieldnum
|
if delta == 0 { // struct terminator is zero delta fieldnum
|
||||||
return
|
break
|
||||||
}
|
}
|
||||||
fieldnum := state.fieldnum + delta
|
fieldNum := state.fieldnum + delta
|
||||||
if fieldnum < 0 || fieldnum >= len(strct.field) {
|
if fieldNum < 0 || fieldNum >= len(strct.field) {
|
||||||
dec.state.err = os.ErrorString("field number out of range")
|
errorf("field number out of range")
|
||||||
return
|
break
|
||||||
}
|
}
|
||||||
tab(indent)
|
tab(indent)
|
||||||
fmt.Printf("field %d:\n", fieldnum)
|
fmt.Printf("%s(%d):\n", wire.structT.field[fieldNum].name, fieldNum)
|
||||||
dec.printData(indent+1, strct.field[fieldnum].id)
|
dec.printItem(indent+1, strct.field[fieldNum].id)
|
||||||
state.fieldnum = fieldnum
|
state.fieldnum = fieldNum
|
||||||
}
|
}
|
||||||
|
tab(indent)
|
||||||
|
fmt.Printf(" } // end %s struct\n", id.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
func tab(indent int) {
|
func tab(indent int) {
|
||||||
for i := 0; i < indent; i++ {
|
for i, w := 0, 0; i < indent; i += w {
|
||||||
fmt.Print("\t")
|
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 {
|
for dec.state.b.Len() > 0 {
|
||||||
// Receive a type id.
|
// Receive a type id.
|
||||||
id := typeId(decodeInt(dec.state))
|
id := typeId(decodeInt(dec.state))
|
||||||
if dec.err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is it a new type?
|
// Is it a new type?
|
||||||
if id < 0 { // 0 is the error state, handled above
|
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)
|
dec.decodeValueFromBuffer(value, false)
|
||||||
return dec.err
|
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