blob: 62e3a2742ada3ceda02bd430a986c49525c51d30 [file] [log] [blame]
Joe Tsaib4e370e2018-08-15 14:59:51 -07001// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// pbdump is a tool for decoding the wire format for protocol buffer messages.
6package main
7
8import (
9 "bytes"
10 "flag"
11 "fmt"
12 "io/ioutil"
13 "log"
14 "os"
15 "path/filepath"
16 "sort"
17 "strconv"
18 "strings"
19
Damien Neile89e6242019-05-13 23:55:40 -070020 "google.golang.org/protobuf/internal/encoding/pack"
21 "google.golang.org/protobuf/internal/encoding/wire"
Joe Tsai09217f02019-09-06 16:57:46 -070022 "google.golang.org/protobuf/internal/errors"
Damien Neila8a2cea2019-07-10 16:17:16 -070023 "google.golang.org/protobuf/proto"
Joe Tsaid8881392019-06-06 13:01:53 -070024 "google.golang.org/protobuf/reflect/protodesc"
Damien Neile89e6242019-05-13 23:55:40 -070025 "google.golang.org/protobuf/reflect/protoreflect"
Joe Tsaie1f8d502018-11-26 18:55:29 -080026
Joe Tsaia95b29f2019-05-16 12:47:20 -070027 "google.golang.org/protobuf/types/descriptorpb"
Joe Tsaib4e370e2018-08-15 14:59:51 -070028)
29
30func main() {
31 log.SetFlags(0)
32 log.SetOutput(os.Stderr)
33
34 var fs fields
Joe Tsai769b9e72018-09-06 06:00:40 -070035 var flagUsages []string
36 flagVar := func(value flag.Value, name, usage string) {
37 flagUsages = append(flagUsages, fmt.Sprintf(" -%-16v %v", name+" "+value.String(), usage))
38 flag.Var(value, name, usage)
39 }
40 flagBool := func(name, usage string) *bool {
41 flagUsages = append(flagUsages, fmt.Sprintf(" -%-16v %v", name, usage))
42 return flag.Bool(name, false, usage)
43 }
44 flagVar(fieldsFlag{&fs, protoreflect.BoolKind}, "bools", "List of bool fields")
45 flagVar(fieldsFlag{&fs, protoreflect.Int64Kind}, "ints", "List of int32 or int64 fields")
46 flagVar(fieldsFlag{&fs, protoreflect.Sint64Kind}, "sints", "List of sint32 or sint64 fields")
47 flagVar(fieldsFlag{&fs, protoreflect.Uint64Kind}, "uints", "List of enum, uint32, or uint64 fields")
48 flagVar(fieldsFlag{&fs, protoreflect.Fixed32Kind}, "uint32s", "List of fixed32 fields")
49 flagVar(fieldsFlag{&fs, protoreflect.Sfixed32Kind}, "int32s", "List of sfixed32 fields")
50 flagVar(fieldsFlag{&fs, protoreflect.FloatKind}, "float32s", "List of float fields")
51 flagVar(fieldsFlag{&fs, protoreflect.Fixed64Kind}, "uint64s", "List of fixed64 fields")
52 flagVar(fieldsFlag{&fs, protoreflect.Sfixed64Kind}, "int64s", "List of sfixed64 fields")
53 flagVar(fieldsFlag{&fs, protoreflect.DoubleKind}, "float64s", "List of double fields")
54 flagVar(fieldsFlag{&fs, protoreflect.StringKind}, "strings", "List of string fields")
55 flagVar(fieldsFlag{&fs, protoreflect.BytesKind}, "bytes", "List of bytes fields")
56 flagVar(fieldsFlag{&fs, protoreflect.MessageKind}, "messages", "List of message fields")
57 flagVar(fieldsFlag{&fs, protoreflect.GroupKind}, "groups", "List of group fields")
58 printDesc := flagBool("print_descriptor", "Print the message descriptor")
59 printSource := flagBool("print_source", "Print the output in valid Go syntax")
Joe Tsaib4e370e2018-08-15 14:59:51 -070060 flag.Usage = func() {
Joe Tsai769b9e72018-09-06 06:00:40 -070061 fmt.Printf("Usage: %s [OPTIONS]... [INPUTS]...\n\n%s\n", filepath.Base(os.Args[0]), strings.Join(append([]string{
Joe Tsaib4e370e2018-08-15 14:59:51 -070062 "Print structured representations of encoded protocol buffer messages.",
63 "Since the protobuf wire format is not fully self-describing, type information",
64 "about the proto message can be provided using flags (e.g., -messages).",
65 "Each field list is a comma-separated list of field identifiers,",
66 "where each field identifier is a dot-separated list of field numbers,",
67 "identifying each field relative to the root message.",
68 "",
69 "For example, \"-messages 1,3,3.1 -float32s 1.2 -bools 3.1.2\" represents:",
70 "",
Joe Tsai769b9e72018-09-06 06:00:40 -070071 " message M {",
72 " optional M1 f1 = 1; // -messages 1",
73 " message M1 {",
74 " repeated float f2 = 2; // -float32s 1.2",
75 " }",
76 " optional M3 f3 = 3; // -messages 3",
77 " message M3 {",
78 " optional M1 f1 = 1; // -messages 3.1",
79 " message M1 {",
80 " repeated bool f2 = 2; // -bools 3.1.2",
81 " }",
82 " }",
83 " }",
Joe Tsaib4e370e2018-08-15 14:59:51 -070084 "",
85 "Arbitrarily complex message schemas can be represented using these flags.",
86 "Scalar field types are marked as repeated so that pbdump can decode",
87 "the packed representations of such field types.",
88 "",
89 "If no inputs are specified, the wire data is read in from stdin, otherwise",
90 "the contents of each specified input file is concatenated and",
91 "treated as one large message.",
92 "",
93 "Options:",
Joe Tsai769b9e72018-09-06 06:00:40 -070094 }, flagUsages...), "\n"))
Joe Tsaib4e370e2018-08-15 14:59:51 -070095 }
96 flag.Parse()
97
98 // Create message types.
99 var desc protoreflect.MessageDescriptor
100 if len(fs) > 0 {
101 var err error
102 desc, err = fs.Descriptor()
103 if err != nil {
104 log.Fatalf("Descriptor error: %v", err)
105 }
106 if *printDesc {
Joe Tsai769b9e72018-09-06 06:00:40 -0700107 fmt.Printf("%#v\n", desc)
Joe Tsaib4e370e2018-08-15 14:59:51 -0700108 }
109 }
110
111 // Read message input.
112 var buf []byte
113 if flag.NArg() == 0 {
114 b, err := ioutil.ReadAll(os.Stdin)
115 if err != nil {
116 log.Fatalf("ReadAll error: %v", err)
117 }
118 buf = b
119 }
120 for _, f := range flag.Args() {
121 b, err := ioutil.ReadFile(f)
122 if err != nil {
123 log.Fatalf("ReadFile error: %v", err)
124 }
125 buf = append(buf, b...)
126 }
127
128 // Parse and print message structure.
129 defer log.Printf("fatal input: %q", buf) // debug printout if panic occurs
130 var m pack.Message
131 m.UnmarshalDescriptor(buf, desc)
132 if *printSource {
Joe Tsai769b9e72018-09-06 06:00:40 -0700133 fmt.Printf("%#v\n", m)
Joe Tsaib4e370e2018-08-15 14:59:51 -0700134 } else {
Joe Tsai769b9e72018-09-06 06:00:40 -0700135 fmt.Printf("%+v\n", m)
Joe Tsaib4e370e2018-08-15 14:59:51 -0700136 }
137 if !bytes.Equal(buf, m.Marshal()) || len(buf) != m.Size() {
138 log.Fatalf("roundtrip mismatch:\n\tgot: %d %x\n\twant: %d %x", m.Size(), m, len(buf), buf)
139 }
140 os.Exit(0) // exit cleanly, avoid debug printout
141}
142
143// fields is a tree of fields, keyed by a field number.
144// Fields representing messages or groups have sub-fields.
145type fields map[wire.Number]*field
146type field struct {
147 kind protoreflect.Kind
148 sub fields // only for MessageKind or GroupKind
149}
150
151// Set parses s as a comma-separated list (see the help above for the format)
152// and treats each field identifier as the specified kind.
153func (fs *fields) Set(s string, k protoreflect.Kind) error {
154 if *fs == nil {
155 *fs = make(fields)
156 }
157 for _, s := range strings.Split(s, ",") {
158 if err := fs.set("", strings.TrimSpace(s), k); err != nil {
159 return err
160 }
161 }
162 return nil
163}
164func (fs fields) set(prefix, s string, k protoreflect.Kind) error {
165 if s == "" {
166 return nil
167 }
168
169 // Parse next field number.
170 i := strings.IndexByte(s, '.')
171 if i < 0 {
172 i = len(s)
173 }
174 prefix = strings.TrimPrefix(prefix+"."+s[:i], ".")
175 n, _ := strconv.ParseInt(s[:i], 10, 32)
176 num := wire.Number(n)
177 if num < wire.MinValidNumber || wire.MaxValidNumber < num {
Joe Tsai09217f02019-09-06 16:57:46 -0700178 return errors.New("invalid field: %v", prefix)
Joe Tsaib4e370e2018-08-15 14:59:51 -0700179 }
180 s = strings.TrimPrefix(s[i:], ".")
181
182 // Handle the current field.
183 if fs[num] == nil {
184 fs[num] = &field{0, make(fields)}
185 }
186 if len(s) == 0 {
187 if fs[num].kind.IsValid() {
Joe Tsai09217f02019-09-06 16:57:46 -0700188 return errors.New("field %v already set as %v type", prefix, fs[num].kind)
Joe Tsaib4e370e2018-08-15 14:59:51 -0700189 }
190 fs[num].kind = k
191 }
192 if err := fs[num].sub.set(prefix, s, k); err != nil {
193 return err
194 }
195
196 // Verify that only messages or groups can have sub-fields.
197 k2 := fs[num].kind
198 if k2 > 0 && k2 != protoreflect.MessageKind && k2 != protoreflect.GroupKind && len(fs[num].sub) > 0 {
Joe Tsai09217f02019-09-06 16:57:46 -0700199 return errors.New("field %v of %v type cannot have sub-fields", prefix, k2)
Joe Tsaib4e370e2018-08-15 14:59:51 -0700200 }
201 return nil
202}
203
204// Descriptor returns the field tree as a message descriptor.
205func (fs fields) Descriptor() (protoreflect.MessageDescriptor, error) {
Joe Tsaid8881392019-06-06 13:01:53 -0700206 fd, err := protodesc.NewFile(&descriptorpb.FileDescriptorProto{
Damien Neila8a2cea2019-07-10 16:17:16 -0700207 Name: proto.String("dump.proto"),
208 Syntax: proto.String("proto2"),
Joe Tsai15076352019-07-02 15:19:08 -0700209 MessageType: []*descriptorpb.DescriptorProto{fs.messageDescriptor("X")},
Joe Tsaid8881392019-06-06 13:01:53 -0700210 }, nil)
Joe Tsaib4e370e2018-08-15 14:59:51 -0700211 if err != nil {
212 return nil, err
213 }
Joe Tsaid8881392019-06-06 13:01:53 -0700214 return fd.Messages().Get(0), nil
Joe Tsaib4e370e2018-08-15 14:59:51 -0700215}
Joe Tsaid8881392019-06-06 13:01:53 -0700216func (fs fields) messageDescriptor(name protoreflect.FullName) *descriptorpb.DescriptorProto {
Damien Neila8a2cea2019-07-10 16:17:16 -0700217 m := &descriptorpb.DescriptorProto{Name: proto.String(string(name.Name()))}
Joe Tsaib4e370e2018-08-15 14:59:51 -0700218 for _, n := range fs.sortedNums() {
Joe Tsaid8881392019-06-06 13:01:53 -0700219 k := fs[n].kind
220 if !k.IsValid() {
221 k = protoreflect.MessageKind
Joe Tsaib4e370e2018-08-15 14:59:51 -0700222 }
Joe Tsaid8881392019-06-06 13:01:53 -0700223 f := &descriptorpb.FieldDescriptorProto{
Damien Neila8a2cea2019-07-10 16:17:16 -0700224 Name: proto.String(fmt.Sprintf("x%d", n)),
225 Number: proto.Int32(int32(n)),
Joe Tsaid8881392019-06-06 13:01:53 -0700226 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
227 Type: descriptorpb.FieldDescriptorProto_Type(k).Enum(),
Joe Tsaib4e370e2018-08-15 14:59:51 -0700228 }
Joe Tsaid8881392019-06-06 13:01:53 -0700229 switch k {
Joe Tsaib4e370e2018-08-15 14:59:51 -0700230 case protoreflect.BoolKind, protoreflect.EnumKind,
231 protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Uint32Kind,
232 protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Uint64Kind,
233 protoreflect.Sfixed32Kind, protoreflect.Fixed32Kind, protoreflect.FloatKind,
234 protoreflect.Sfixed64Kind, protoreflect.Fixed64Kind, protoreflect.DoubleKind:
Joe Tsaid8881392019-06-06 13:01:53 -0700235 f.Label = descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum()
Damien Neila8a2cea2019-07-10 16:17:16 -0700236 f.Options = &descriptorpb.FieldOptions{Packed: proto.Bool(true)}
Joe Tsaib4e370e2018-08-15 14:59:51 -0700237 case protoreflect.MessageKind, protoreflect.GroupKind:
Joe Tsai15076352019-07-02 15:19:08 -0700238 s := name.Append(protoreflect.Name(fmt.Sprintf("X%d", n)))
Damien Neila8a2cea2019-07-10 16:17:16 -0700239 f.TypeName = proto.String(string("." + s))
Joe Tsaid8881392019-06-06 13:01:53 -0700240 m.NestedType = append(m.NestedType, fs[n].sub.messageDescriptor(s))
Joe Tsaib4e370e2018-08-15 14:59:51 -0700241 }
Joe Tsaid8881392019-06-06 13:01:53 -0700242 m.Field = append(m.Field, f)
Joe Tsaib4e370e2018-08-15 14:59:51 -0700243 }
244 return m
245}
246
247func (fs fields) sortedNums() (ns []wire.Number) {
248 for n := range fs {
249 ns = append(ns, n)
250 }
251 sort.Slice(ns, func(i, j int) bool { return ns[i] < ns[j] })
252 return ns
253}
254
Joe Tsai769b9e72018-09-06 06:00:40 -0700255// fieldsFlag is an implementation of flag.Value that is keyed a specific kind.
256type fieldsFlag struct {
257 f *fields
258 k protoreflect.Kind
259}
Joe Tsaib4e370e2018-08-15 14:59:51 -0700260
Joe Tsai769b9e72018-09-06 06:00:40 -0700261func (fs fieldsFlag) String() string { return "FIELDS" }
262func (fs fieldsFlag) Set(s string) error { return fs.f.Set(s, fs.k) }