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