blob: c6cb7c79b60bae2fc777111ae8fcf1c618fabd3f [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
Joe Tsai01ab2962018-09-21 17:44:00 -070020 "github.com/golang/protobuf/v2/internal/encoding/pack"
21 "github.com/golang/protobuf/v2/internal/encoding/wire"
Joe Tsai990b9f52019-03-13 12:56:39 -070022 "github.com/golang/protobuf/v2/internal/prototype"
Joe Tsai009e0672018-11-27 18:45:07 -080023 "github.com/golang/protobuf/v2/internal/scalar"
Joe Tsai01ab2962018-09-21 17:44:00 -070024 "github.com/golang/protobuf/v2/reflect/protoreflect"
Joe Tsaie1f8d502018-11-26 18:55:29 -080025
26 descriptorpb "github.com/golang/protobuf/v2/types/descriptor"
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) {
205 ftyp, err := prototype.NewFile(&prototype.File{
206 Syntax: protoreflect.Proto2,
207 Messages: []prototype.Message{fs.messageDescriptor("M")},
208 })
209 if err != nil {
210 return nil, err
211 }
212 return ftyp.Messages().Get(0), nil
213}
214func (fs fields) messageDescriptor(name protoreflect.FullName) prototype.Message {
215 m := prototype.Message{Name: name.Name()}
216 for _, n := range fs.sortedNums() {
217 f := prototype.Field{
218 Name: protoreflect.Name(fmt.Sprintf("f%d", n)),
219 Number: n,
220 Cardinality: protoreflect.Optional,
221 Kind: fs[n].kind,
222 }
223 if !f.Kind.IsValid() {
224 f.Kind = protoreflect.MessageKind
225 }
226 switch f.Kind {
227 case protoreflect.BoolKind, protoreflect.EnumKind,
228 protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Uint32Kind,
229 protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Uint64Kind,
230 protoreflect.Sfixed32Kind, protoreflect.Fixed32Kind, protoreflect.FloatKind,
231 protoreflect.Sfixed64Kind, protoreflect.Fixed64Kind, protoreflect.DoubleKind:
232 f.Cardinality = protoreflect.Repeated
Joe Tsaie1f8d502018-11-26 18:55:29 -0800233 f.Options = &descriptorpb.FieldOptions{Packed: scalar.Bool(true)}
Joe Tsaib4e370e2018-08-15 14:59:51 -0700234 case protoreflect.MessageKind, protoreflect.GroupKind:
235 s := name.Append(protoreflect.Name(fmt.Sprintf("M%d", n)))
236 f.MessageType = prototype.PlaceholderMessage(s)
237 m.Messages = append(m.Messages, fs[n].sub.messageDescriptor(s))
238 }
239 m.Fields = append(m.Fields, f)
240 }
241 return m
242}
243
244func (fs fields) sortedNums() (ns []wire.Number) {
245 for n := range fs {
246 ns = append(ns, n)
247 }
248 sort.Slice(ns, func(i, j int) bool { return ns[i] < ns[j] })
249 return ns
250}
251
Joe Tsai769b9e72018-09-06 06:00:40 -0700252// fieldsFlag is an implementation of flag.Value that is keyed a specific kind.
253type fieldsFlag struct {
254 f *fields
255 k protoreflect.Kind
256}
Joe Tsaib4e370e2018-08-15 14:59:51 -0700257
Joe Tsai769b9e72018-09-06 06:00:40 -0700258func (fs fieldsFlag) String() string { return "FIELDS" }
259func (fs fieldsFlag) Set(s string) error { return fs.f.Set(s, fs.k) }