| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| |
|
| | package gob |
| |
|
| | |
| | |
| | |
| | |
| | |
| |
|
| | import ( |
| | "bytes" |
| | "fmt" |
| | "io" |
| | "os" |
| | "strings" |
| | "sync" |
| | ) |
| |
|
| | var dumpBytes = false |
| |
|
| | |
| | |
| | func init() { |
| | debugFunc = Debug |
| | } |
| |
|
| | var ( |
| | blanks = bytes.Repeat([]byte{' '}, 3*10) |
| | empty = []byte(": <empty>\n") |
| | tabs = strings.Repeat("\t", 100) |
| | ) |
| |
|
| | |
| | type tab int |
| |
|
| | func (t tab) String() string { |
| | n := int(t) |
| | if n > len(tabs) { |
| | n = len(tabs) |
| | } |
| | return tabs[0:n] |
| | } |
| |
|
| | func (t tab) print() { |
| | fmt.Fprint(os.Stderr, t) |
| | } |
| |
|
| | |
| | |
| | type peekReader struct { |
| | r io.Reader |
| | data []byte |
| | } |
| |
|
| | |
| | func newPeekReader(r io.Reader) *peekReader { |
| | return &peekReader{r: r} |
| | } |
| |
|
| | |
| | func (p *peekReader) Read(b []byte) (n int, err error) { |
| | if len(p.data) == 0 { |
| | return p.r.Read(b) |
| | } |
| | |
| | n = copy(b, p.data) |
| | |
| | copy(p.data, p.data[n:]) |
| | p.data = p.data[:len(p.data)-n] |
| | return |
| | } |
| |
|
| | |
| | |
| | func (p *peekReader) peek(b []byte) (n int, err error) { |
| | if len(p.data) > 0 { |
| | n = copy(b, p.data) |
| | if n == len(b) { |
| | return |
| | } |
| | b = b[n:] |
| | } |
| | if len(b) == 0 { |
| | return |
| | } |
| | m, e := io.ReadFull(p.r, b) |
| | if m > 0 { |
| | p.data = append(p.data, b[:m]...) |
| | } |
| | n += m |
| | if e == io.ErrUnexpectedEOF { |
| | |
| | |
| | if n > 0 { |
| | e = nil |
| | } else { |
| | e = io.EOF |
| | } |
| | } |
| | return n, e |
| | } |
| |
|
| | type debugger struct { |
| | mutex sync.Mutex |
| | remain int |
| | remainingKnown bool |
| | r *peekReader |
| | wireType map[typeId]*wireType |
| | tmp []byte |
| | } |
| |
|
| | |
| | |
| | |
| | func (deb *debugger) dump(format string, args ...any) { |
| | if !dumpBytes { |
| | return |
| | } |
| | fmt.Fprintf(os.Stderr, format+" ", args...) |
| | if !deb.remainingKnown { |
| | return |
| | } |
| | if deb.remain < 0 { |
| | fmt.Fprintf(os.Stderr, "remaining byte count is negative! %d\n", deb.remain) |
| | return |
| | } |
| | data := make([]byte, deb.remain) |
| | n, _ := deb.r.peek(data) |
| | if n == 0 { |
| | os.Stderr.Write(empty) |
| | return |
| | } |
| | b := new(bytes.Buffer) |
| | fmt.Fprintf(b, "[%d]{\n", deb.remain) |
| | |
| | lineLength := 0 |
| | if n := len(data); n%10 != 0 { |
| | lineLength = 10 - n%10 |
| | fmt.Fprintf(b, "\t%s", blanks[:lineLength*3]) |
| | } |
| | |
| | for len(data) > 0 { |
| | if lineLength == 0 { |
| | fmt.Fprint(b, "\t") |
| | } |
| | m := 10 - lineLength |
| | lineLength = 0 |
| | if m > len(data) { |
| | m = len(data) |
| | } |
| | fmt.Fprintf(b, "% x\n", data[:m]) |
| | data = data[m:] |
| | } |
| | fmt.Fprint(b, "}\n") |
| | os.Stderr.Write(b.Bytes()) |
| | } |
| |
|
| | |
| | |
| | func Debug(r io.Reader) { |
| | err := debug(r) |
| | if err != nil { |
| | fmt.Fprintf(os.Stderr, "gob debug: %s\n", err) |
| | } |
| | } |
| |
|
| | |
| | |
| | func debug(r io.Reader) (err error) { |
| | defer catchError(&err) |
| | fmt.Fprintln(os.Stderr, "Start of debugging") |
| | deb := &debugger{ |
| | r: newPeekReader(r), |
| | wireType: make(map[typeId]*wireType), |
| | tmp: make([]byte, 16), |
| | } |
| | if b, ok := r.(*bytes.Buffer); ok { |
| | deb.remain = b.Len() |
| | deb.remainingKnown = true |
| | } |
| | deb.gobStream() |
| | return |
| | } |
| |
|
| | |
| | func (deb *debugger) consumed(n int) { |
| | if deb.remainingKnown { |
| | deb.remain -= n |
| | } |
| | } |
| |
|
| | |
| | |
| | func (deb *debugger) int64() int64 { |
| | return toInt(deb.uint64()) |
| | } |
| |
|
| | |
| | |
| | |
| | func (deb *debugger) uint64() uint64 { |
| | n, w, err := decodeUintReader(deb.r, deb.tmp) |
| | if err != nil { |
| | errorf("debug: read error: %s", err) |
| | } |
| | deb.consumed(w) |
| | return n |
| | } |
| |
|
| | |
| | |
| | |
| | func (deb *debugger) gobStream() { |
| | |
| | deb.mutex.Lock() |
| | defer deb.mutex.Unlock() |
| |
|
| | for deb.delimitedMessage(0) { |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | func (deb *debugger) delimitedMessage(indent tab) bool { |
| | for { |
| | n := deb.loadBlock(true) |
| | if n < 0 { |
| | return false |
| | } |
| | deb.dump("Delimited message of length %d", n) |
| | deb.message(indent) |
| | } |
| | return true |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | func (deb *debugger) loadBlock(eofOK bool) int { |
| | n64, w, err := decodeUintReader(deb.r, deb.tmp) |
| | if err != nil { |
| | if eofOK && err == io.EOF { |
| | return -1 |
| | } |
| | errorf("debug: unexpected error: %s", err) |
| | } |
| | deb.consumed(w) |
| | n := int(n64) |
| | if n < 0 { |
| | errorf("huge value for message length: %d", n64) |
| | } |
| | return int(n) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func (deb *debugger) message(indent tab) bool { |
| | for { |
| | |
| | uid := deb.int64() |
| | id := typeId(uid) |
| | deb.dump("type id=%d", id) |
| | if id < 0 { |
| | deb.typeDefinition(indent, -id) |
| | n := deb.loadBlock(false) |
| | deb.dump("Message of length %d", n) |
| | continue |
| | } else { |
| | deb.value(indent, id) |
| | break |
| | } |
| | } |
| | return true |
| | } |
| |
|
| | |
| |
|
| | |
| | func (deb *debugger) common() CommonType { |
| | fieldNum := -1 |
| | name := "" |
| | id := typeId(0) |
| | for { |
| | delta := deb.delta(-1) |
| | if delta == 0 { |
| | break |
| | } |
| | fieldNum += delta |
| | switch fieldNum { |
| | case 0: |
| | name = deb.string() |
| | case 1: |
| | |
| | id = deb.typeId() |
| | default: |
| | errorf("corrupted CommonType, delta is %d fieldNum is %d", delta, fieldNum) |
| | } |
| | } |
| | return CommonType{name, id} |
| | } |
| |
|
| | |
| | func (deb *debugger) uint() uint { |
| | return uint(deb.uint64()) |
| | } |
| |
|
| | |
| | func (deb *debugger) int() int { |
| | return int(deb.int64()) |
| | } |
| |
|
| | |
| | func (deb *debugger) typeId() typeId { |
| | return typeId(deb.int64()) |
| | } |
| |
|
| | |
| | func (deb *debugger) string() string { |
| | x := int(deb.uint64()) |
| | b := make([]byte, x) |
| | nb, _ := deb.r.Read(b) |
| | if nb != x { |
| | errorf("corrupted type") |
| | } |
| | deb.consumed(nb) |
| | return string(b) |
| | } |
| |
|
| | |
| | |
| | func (deb *debugger) delta(expect int) int { |
| | delta := int(deb.uint64()) |
| | if delta < 0 || (expect >= 0 && delta != expect) { |
| | errorf("decode: corrupted type: delta %d expected %d", delta, expect) |
| | } |
| | return delta |
| | } |
| |
|
| | |
| | |
| | |
| | func (deb *debugger) typeDefinition(indent tab, id typeId) { |
| | deb.dump("type definition for id %d", id) |
| | |
| | fieldNum := -1 |
| | wire := new(wireType) |
| | |
| | delta := deb.delta(-1) |
| | fieldNum += delta |
| | switch fieldNum { |
| | case 0: |
| | |
| | deb.delta(1) |
| | com := deb.common() |
| | |
| | deb.delta(1) |
| | id := deb.typeId() |
| | |
| | deb.delta(1) |
| | length := deb.int() |
| | wire.ArrayT = &arrayType{com, id, length} |
| |
|
| | case 1: |
| | |
| | deb.delta(1) |
| | com := deb.common() |
| | |
| | deb.delta(1) |
| | id := deb.typeId() |
| | wire.SliceT = &sliceType{com, id} |
| |
|
| | case 2: |
| | |
| | deb.delta(1) |
| | com := deb.common() |
| | |
| | deb.delta(1) |
| | numField := int(deb.uint()) |
| | field := make([]*fieldType, numField) |
| | for i := 0; i < numField; i++ { |
| | field[i] = new(fieldType) |
| | deb.delta(1) |
| | field[i].Name = deb.string() |
| | deb.delta(1) |
| | field[i].Id = deb.typeId() |
| | deb.delta(0) |
| | } |
| | wire.StructT = &structType{com, field} |
| |
|
| | case 3: |
| | |
| | deb.delta(1) |
| | com := deb.common() |
| | |
| | deb.delta(1) |
| | keyId := deb.typeId() |
| | |
| | deb.delta(1) |
| | elemId := deb.typeId() |
| | wire.MapT = &mapType{com, keyId, elemId} |
| | case 4: |
| | |
| | deb.delta(1) |
| | com := deb.common() |
| | wire.GobEncoderT = &gobEncoderType{com} |
| | case 5: |
| | |
| | deb.delta(1) |
| | com := deb.common() |
| | wire.BinaryMarshalerT = &gobEncoderType{com} |
| | case 6: |
| | |
| | deb.delta(1) |
| | com := deb.common() |
| | wire.TextMarshalerT = &gobEncoderType{com} |
| | default: |
| | errorf("bad field in type %d", fieldNum) |
| | } |
| | deb.printWireType(indent, wire) |
| | deb.delta(0) |
| | deb.delta(0) |
| | |
| | deb.wireType[id] = wire |
| | } |
| |
|
| | |
| | |
| | |
| | func (deb *debugger) value(indent tab, id typeId) { |
| | wire, ok := deb.wireType[id] |
| | if ok && wire.StructT != nil { |
| | deb.structValue(indent, id) |
| | } else { |
| | deb.singletonValue(indent, id) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | func (deb *debugger) singletonValue(indent tab, id typeId) { |
| | deb.dump("Singleton value") |
| | |
| | wire := deb.wireType[id] |
| | if builtinIdToType(id) == nil && wire == nil { |
| | errorf("type id %d not defined", id) |
| | } |
| | m := deb.uint64() |
| | if m != 0 { |
| | errorf("expected zero; got %d", m) |
| | } |
| | deb.fieldValue(indent, id) |
| | } |
| |
|
| | |
| | |
| | |
| | func (deb *debugger) interfaceValue(indent tab) { |
| | deb.dump("Start of interface value") |
| | if nameLen := deb.uint64(); nameLen == 0 { |
| | deb.nilInterfaceValue(indent) |
| | } else { |
| | deb.nonNilInterfaceValue(indent, int(nameLen)) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | func (deb *debugger) nilInterfaceValue(indent tab) int { |
| | fmt.Fprintf(os.Stderr, "%snil interface\n", indent) |
| | return 0 |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func (deb *debugger) nonNilInterfaceValue(indent tab, nameLen int) { |
| | |
| | b := make([]byte, nameLen) |
| | deb.r.Read(b) |
| | deb.consumed(nameLen) |
| | name := string(b) |
| |
|
| | for { |
| | id := deb.typeId() |
| | if id < 0 { |
| | deb.typeDefinition(indent, -id) |
| | n := deb.loadBlock(false) |
| | deb.dump("Nested message of length %d", n) |
| | } else { |
| | |
| | x := deb.uint64() |
| | fmt.Fprintf(os.Stderr, "%sinterface value, type %q id=%d; valueLength %d\n", indent, name, id, x) |
| | deb.value(indent, id) |
| | break |
| | } |
| | } |
| | } |
| |
|
| | |
| | func (deb *debugger) printCommonType(indent tab, kind string, common *CommonType) { |
| | indent.print() |
| | fmt.Fprintf(os.Stderr, "%s %q id=%d\n", kind, common.Name, common.Id) |
| | } |
| |
|
| | |
| | func (deb *debugger) printWireType(indent tab, wire *wireType) { |
| | fmt.Fprintf(os.Stderr, "%stype definition {\n", indent) |
| | indent++ |
| | switch { |
| | case wire.ArrayT != nil: |
| | deb.printCommonType(indent, "array", &wire.ArrayT.CommonType) |
| | fmt.Fprintf(os.Stderr, "%slen %d\n", indent+1, wire.ArrayT.Len) |
| | fmt.Fprintf(os.Stderr, "%selemid %d\n", indent+1, wire.ArrayT.Elem) |
| | case wire.MapT != nil: |
| | deb.printCommonType(indent, "map", &wire.MapT.CommonType) |
| | fmt.Fprintf(os.Stderr, "%skey id=%d\n", indent+1, wire.MapT.Key) |
| | fmt.Fprintf(os.Stderr, "%selem id=%d\n", indent+1, wire.MapT.Elem) |
| | case wire.SliceT != nil: |
| | deb.printCommonType(indent, "slice", &wire.SliceT.CommonType) |
| | fmt.Fprintf(os.Stderr, "%selem id=%d\n", indent+1, wire.SliceT.Elem) |
| | case wire.StructT != nil: |
| | deb.printCommonType(indent, "struct", &wire.StructT.CommonType) |
| | for i, field := range wire.StructT.Field { |
| | fmt.Fprintf(os.Stderr, "%sfield %d:\t%s\tid=%d\n", indent+1, i, field.Name, field.Id) |
| | } |
| | case wire.GobEncoderT != nil: |
| | deb.printCommonType(indent, "GobEncoder", &wire.GobEncoderT.CommonType) |
| | } |
| | indent-- |
| | fmt.Fprintf(os.Stderr, "%s}\n", indent) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func (deb *debugger) fieldValue(indent tab, id typeId) { |
| | if builtinIdToType(id) != nil { |
| | if id == tInterface { |
| | deb.interfaceValue(indent) |
| | } else { |
| | deb.printBuiltin(indent, id) |
| | } |
| | return |
| | } |
| | wire, ok := deb.wireType[id] |
| | if !ok { |
| | errorf("type id %d not defined", id) |
| | } |
| | switch { |
| | case wire.ArrayT != nil: |
| | deb.arrayValue(indent, wire) |
| | case wire.MapT != nil: |
| | deb.mapValue(indent, wire) |
| | case wire.SliceT != nil: |
| | deb.sliceValue(indent, wire) |
| | case wire.StructT != nil: |
| | deb.structValue(indent, id) |
| | case wire.GobEncoderT != nil: |
| | deb.gobEncoderValue(indent, id) |
| | default: |
| | panic("bad wire type for field") |
| | } |
| | } |
| |
|
| | |
| | |
| | func (deb *debugger) printBuiltin(indent tab, id typeId) { |
| | switch id { |
| | case tBool: |
| | x := deb.int64() |
| | if x == 0 { |
| | fmt.Fprintf(os.Stderr, "%sfalse\n", indent) |
| | } else { |
| | fmt.Fprintf(os.Stderr, "%strue\n", indent) |
| | } |
| | case tInt: |
| | x := deb.int64() |
| | fmt.Fprintf(os.Stderr, "%s%d\n", indent, x) |
| | case tUint: |
| | x := deb.uint64() |
| | fmt.Fprintf(os.Stderr, "%s%d\n", indent, x) |
| | case tFloat: |
| | x := deb.uint64() |
| | fmt.Fprintf(os.Stderr, "%s%g\n", indent, float64FromBits(x)) |
| | case tComplex: |
| | r := deb.uint64() |
| | i := deb.uint64() |
| | fmt.Fprintf(os.Stderr, "%s%g+%gi\n", indent, float64FromBits(r), float64FromBits(i)) |
| | case tBytes: |
| | x := int(deb.uint64()) |
| | b := make([]byte, x) |
| | deb.r.Read(b) |
| | deb.consumed(x) |
| | fmt.Fprintf(os.Stderr, "%s{% x}=%q\n", indent, b, b) |
| | case tString: |
| | x := int(deb.uint64()) |
| | b := make([]byte, x) |
| | deb.r.Read(b) |
| | deb.consumed(x) |
| | fmt.Fprintf(os.Stderr, "%s%q\n", indent, b) |
| | default: |
| | panic("unknown builtin") |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | func (deb *debugger) arrayValue(indent tab, wire *wireType) { |
| | elemId := wire.ArrayT.Elem |
| | u := deb.uint64() |
| | length := int(u) |
| | for i := 0; i < length; i++ { |
| | deb.fieldValue(indent, elemId) |
| | } |
| | if length != wire.ArrayT.Len { |
| | fmt.Fprintf(os.Stderr, "%s(wrong length for array: %d should be %d)\n", indent, length, wire.ArrayT.Len) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | func (deb *debugger) mapValue(indent tab, wire *wireType) { |
| | keyId := wire.MapT.Key |
| | elemId := wire.MapT.Elem |
| | u := deb.uint64() |
| | length := int(u) |
| | for i := 0; i < length; i++ { |
| | deb.fieldValue(indent+1, keyId) |
| | deb.fieldValue(indent+1, elemId) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | func (deb *debugger) sliceValue(indent tab, wire *wireType) { |
| | elemId := wire.SliceT.Elem |
| | u := deb.uint64() |
| | length := int(u) |
| | deb.dump("Start of slice of length %d", length) |
| |
|
| | for i := 0; i < length; i++ { |
| | deb.fieldValue(indent, elemId) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | func (deb *debugger) structValue(indent tab, id typeId) { |
| | deb.dump("Start of struct value of %q id=%d\n<<\n", id.name(), id) |
| | fmt.Fprintf(os.Stderr, "%s%s struct {\n", indent, id.name()) |
| | wire, ok := deb.wireType[id] |
| | if !ok { |
| | errorf("type id %d not defined", id) |
| | } |
| | strct := wire.StructT |
| | fieldNum := -1 |
| | indent++ |
| | for { |
| | delta := deb.uint64() |
| | if delta == 0 { |
| | break |
| | } |
| | fieldNum += int(delta) |
| | if fieldNum < 0 || fieldNum >= len(strct.Field) { |
| | deb.dump("field number out of range: prevField=%d delta=%d", fieldNum-int(delta), delta) |
| | break |
| | } |
| | fmt.Fprintf(os.Stderr, "%sfield %d:\t%s\n", indent, fieldNum, wire.StructT.Field[fieldNum].Name) |
| | deb.fieldValue(indent+1, strct.Field[fieldNum].Id) |
| | } |
| | indent-- |
| | fmt.Fprintf(os.Stderr, "%s} // end %s struct\n", indent, id.name()) |
| | deb.dump(">> End of struct value of type %d %q", id, id.name()) |
| | } |
| |
|
| | |
| | |
| | |
| | func (deb *debugger) gobEncoderValue(indent tab, id typeId) { |
| | len := deb.uint64() |
| | deb.dump("GobEncoder value of %q id=%d, length %d\n", id.name(), id, len) |
| | fmt.Fprintf(os.Stderr, "%s%s (implements GobEncoder)\n", indent, id.name()) |
| | data := make([]byte, len) |
| | _, err := deb.r.Read(data) |
| | if err != nil { |
| | errorf("gobEncoder data read: %s", err) |
| | } |
| | fmt.Fprintf(os.Stderr, "%s[% .2x]\n", indent+1, data) |
| | } |
| |
|