1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/trace_processor/util/protozero_to_json.h"
18
19 #include <optional>
20 #include <unordered_set>
21 #include <utility>
22 #include <vector>
23
24 #include "perfetto/ext/base/string_utils.h"
25 #include "perfetto/ext/base/string_view.h"
26 #include "perfetto/protozero/field.h"
27 #include "perfetto/protozero/proto_decoder.h"
28 #include "perfetto/protozero/proto_utils.h"
29 #include "protos/perfetto/common/descriptor.pbzero.h"
30 #include "src/trace_processor/util/descriptors.h"
31
32 namespace perfetto {
33 namespace trace_processor {
34 namespace protozero_to_json {
35
36 namespace {
37
38 using protos::pbzero::FieldDescriptorProto;
39 using protozero::PackedRepeatedFieldIterator;
40 using protozero::proto_utils::ProtoWireType;
41
42 class JsonBuilder {
43 public:
JsonBuilder(int flags)44 explicit JsonBuilder(int flags) : flags_(flags) {}
45
OpenObject()46 void OpenObject() {
47 if (is_array_scope()) {
48 if (!is_empty_scope()) {
49 Append(",");
50 }
51 MaybeAppendNewline();
52 MaybeAppendIndent();
53 }
54 Append("{");
55 stack_.push_back(Scope{ScopeContext::kObject});
56 }
57
CloseObject()58 void CloseObject() {
59 bool needs_newline = !is_empty_scope();
60 stack_.pop_back();
61 if (needs_newline) {
62 MaybeAppendNewline();
63 MaybeAppendIndent();
64 }
65
66 MarkScopeAsNonEmpty();
67 Append("}");
68 }
69
OpenArray()70 void OpenArray() {
71 Append("[");
72 stack_.push_back(Scope{ScopeContext::kArray});
73 }
74
CloseArray()75 void CloseArray() {
76 bool needs_newline = !is_empty_scope();
77 stack_.pop_back();
78 if (needs_newline) {
79 MaybeAppendNewline();
80 MaybeAppendIndent();
81 }
82 Append("]");
83 if (is_array_scope() && !is_empty_scope()) {
84 Append(",");
85 }
86 }
87
Key(const std::string & key)88 void Key(const std::string& key) {
89 if (is_object_scope() && !is_empty_scope()) {
90 Append(",");
91 }
92 MaybeAppendNewline();
93 MaybeAppendIndent();
94 Append(EscapeString(base::StringView(key)));
95 Append(":");
96 MaybeAppendSpace();
97 MarkScopeAsNonEmpty();
98 }
99
100 template <typename T>
NumberValue(T v)101 void NumberValue(T v) {
102 AppendValue(std::to_string(v));
103 }
104
BoolValue(bool v)105 void BoolValue(bool v) { AppendValue(v ? "true" : "false"); }
106
FloatValue(float v)107 void FloatValue(float v) { NumberValue(v); }
108
DoubleValue(double v)109 void DoubleValue(double v) { NumberValue(v); }
110
StringValue(base::StringView v)111 void StringValue(base::StringView v) { AppendValue(EscapeString(v)); }
112
AddError(const std::string & s)113 void AddError(const std::string& s) { errors_.push_back(s); }
114
ToString()115 std::string ToString() { return base::Join(parts_, ""); }
116
is_empty_scope()117 bool is_empty_scope() { return !stack_.empty() && stack_.back().is_empty; }
118
is_pretty() const119 bool is_pretty() const { return flags_ & Flags::kPretty; }
120
is_inline_errors() const121 bool is_inline_errors() const { return flags_ & Flags::kInlineErrors; }
122
errors() const123 const std::vector<std::string>& errors() const { return errors_; }
124
125 private:
126 enum class ScopeContext {
127 kObject,
128 kArray,
129 };
130
131 struct Scope {
132 ScopeContext ctx;
133 bool is_empty = true;
134 };
135
136 int flags_;
137 std::vector<std::string> parts_;
138 std::vector<Scope> stack_;
139 std::vector<std::string> errors_;
140
is_object_scope()141 bool is_object_scope() {
142 return !stack_.empty() && stack_.back().ctx == ScopeContext::kObject;
143 }
144
is_array_scope()145 bool is_array_scope() {
146 return !stack_.empty() && stack_.back().ctx == ScopeContext::kArray;
147 }
148
MarkScopeAsNonEmpty()149 void MarkScopeAsNonEmpty() {
150 if (!stack_.empty()) {
151 stack_.back().is_empty = false;
152 }
153 }
154
MaybeAppendSpace()155 void MaybeAppendSpace() {
156 if (is_pretty()) {
157 Append(" ");
158 }
159 }
160
MaybeAppendIndent()161 void MaybeAppendIndent() {
162 if (is_pretty()) {
163 Append(std::string(stack_.size() * 2, ' '));
164 }
165 }
166
MaybeAppendNewline()167 void MaybeAppendNewline() {
168 if (is_pretty()) {
169 Append("\n");
170 }
171 }
172
AppendValue(const std::string & s)173 void AppendValue(const std::string& s) {
174 if (is_array_scope() && !is_empty_scope()) {
175 Append(",");
176 }
177 if (is_array_scope()) {
178 MaybeAppendNewline();
179 MaybeAppendIndent();
180 }
181 Append(s);
182 MarkScopeAsNonEmpty();
183 }
184
Append(const std::string & s)185 void Append(const std::string& s) { parts_.push_back(s); }
186
EscapeString(base::StringView raw)187 std::string EscapeString(base::StringView raw) {
188 std::string result;
189 result.reserve(raw.size() + 2);
190 result += "\"";
191 for (size_t i = 0; i < raw.size(); ++i) {
192 char c = *(raw.begin() + i);
193 switch (c) {
194 case '"':
195 case '\\':
196 result += '\\';
197 result += c;
198 break;
199 case '\n':
200 result += R"(\n)";
201 break;
202 case '\b':
203 result += R"(\b)";
204 break;
205 case '\f':
206 result += R"(\f)";
207 break;
208 case '\r':
209 result += R"(\r)";
210 break;
211 case '\t':
212 result += R"(\t)";
213 break;
214 default:
215 // ASCII characters between 0x20 (space) and 0x7e (tilde) are
216 // inserted directly. All others are escaped.
217 if (c >= 0x20 && c <= 0x7e) {
218 result += c;
219 } else {
220 unsigned char uc = static_cast<unsigned char>(c);
221 uint32_t codepoint = 0;
222
223 // Compute the number of bytes:
224 size_t extra = 1 + (uc >= 0xc0u) + (uc >= 0xe0u) + (uc >= 0xf0u);
225
226 // We want to consume |extra| bytes but also need to not
227 // read out of bounds:
228 size_t stop = std::min(raw.size(), i + extra);
229
230 // Manually insert the bits from first byte:
231 codepoint |= uc & (0xff >> (extra + 1));
232
233 // Insert remaining bits:
234 for (size_t j = i + 1; j < stop; ++j) {
235 uc = static_cast<unsigned char>(*(raw.begin() + j));
236 codepoint = (codepoint << 6) | (uc & 0x3f);
237 }
238
239 // Update i to show the consumed chars:
240 i = stop - 1;
241
242 static const char hex_chars[] = "0123456789abcdef";
243 // JSON does not have proper utf-8 escapes. Instead you
244 // have to use utf-16 codes. For the low codepoints
245 // \uXXXX and for the high codepoints a surrogate pair:
246 // \uXXXX\uYYYY
247 if (codepoint <= 0xffff) {
248 result += R"(\u)";
249 result += hex_chars[(codepoint >> 12) & 0xf];
250 result += hex_chars[(codepoint >> 8) & 0xf];
251 result += hex_chars[(codepoint >> 4) & 0xf];
252 result += hex_chars[(codepoint >> 0) & 0xf];
253 } else {
254 uint32_t high = ((codepoint - 0x10000) >> 10) + 0xD800;
255 uint32_t low = (codepoint & 0x4fff) + 0xDC00;
256 result += R"(\u)";
257 result += hex_chars[(high >> 12) & 0xf];
258 result += hex_chars[(high >> 8) & 0xf];
259 result += hex_chars[(high >> 4) & 0xf];
260 result += hex_chars[(high >> 0) & 0xf];
261 result += R"(\u)";
262 result += hex_chars[(low >> 12) & 0xf];
263 result += hex_chars[(low >> 8) & 0xf];
264 result += hex_chars[(low >> 4) & 0xf];
265 result += hex_chars[(low >> 0) & 0xf];
266 }
267 }
268 break;
269 }
270 }
271 result += "\"";
272 return result;
273 }
274 };
275
HasFieldOptions(const FieldDescriptor & field_desc)276 bool HasFieldOptions(const FieldDescriptor& field_desc) {
277 return !field_desc.options().empty();
278 }
279
FulllyQualifiedFieldName(const ProtoDescriptor & desc,const FieldDescriptor & field_desc)280 std::string FulllyQualifiedFieldName(const ProtoDescriptor& desc,
281 const FieldDescriptor& field_desc) {
282 return desc.package_name().substr(1) + "." + field_desc.name();
283 }
284
IsTypeMatch(ProtoWireType wire,uint32_t type)285 bool IsTypeMatch(ProtoWireType wire, uint32_t type) {
286 switch (wire) {
287 case ProtoWireType::kVarInt:
288 switch (type) {
289 case FieldDescriptorProto::TYPE_INT32:
290 case FieldDescriptorProto::TYPE_SINT32:
291 case FieldDescriptorProto::TYPE_UINT32:
292 case FieldDescriptorProto::TYPE_INT64:
293 case FieldDescriptorProto::TYPE_SINT64:
294 case FieldDescriptorProto::TYPE_UINT64:
295 case FieldDescriptorProto::TYPE_BOOL:
296 case FieldDescriptorProto::TYPE_ENUM:
297 return true;
298 default:
299 return false;
300 }
301 case ProtoWireType::kLengthDelimited:
302 switch (type) {
303 case FieldDescriptorProto::TYPE_BYTES:
304 case FieldDescriptorProto::TYPE_MESSAGE:
305 case FieldDescriptorProto::TYPE_STRING:
306 // The normal case.
307 return true;
308 case FieldDescriptorProto::TYPE_INT32:
309 case FieldDescriptorProto::TYPE_SINT32:
310 case FieldDescriptorProto::TYPE_UINT32:
311 case FieldDescriptorProto::TYPE_INT64:
312 case FieldDescriptorProto::TYPE_SINT64:
313 case FieldDescriptorProto::TYPE_UINT64:
314 case FieldDescriptorProto::TYPE_BOOL:
315 case FieldDescriptorProto::TYPE_ENUM:
316 case FieldDescriptorProto::TYPE_FIXED32:
317 case FieldDescriptorProto::TYPE_SFIXED32:
318 case FieldDescriptorProto::TYPE_FLOAT:
319 case FieldDescriptorProto::TYPE_FIXED64:
320 case FieldDescriptorProto::TYPE_SFIXED64:
321 case FieldDescriptorProto::TYPE_DOUBLE:
322 // Packed repeated fields.
323 return true;
324 default:
325 return false;
326 }
327 case ProtoWireType::kFixed32:
328 switch (type) {
329 case FieldDescriptorProto::TYPE_FIXED32:
330 case FieldDescriptorProto::TYPE_SFIXED32:
331 case FieldDescriptorProto::TYPE_FLOAT:
332 return true;
333 default:
334 return false;
335 }
336 case ProtoWireType::kFixed64:
337 switch (type) {
338 case FieldDescriptorProto::TYPE_FIXED64:
339 case FieldDescriptorProto::TYPE_SFIXED64:
340 case FieldDescriptorProto::TYPE_DOUBLE:
341 return true;
342 default:
343 return false;
344 }
345 }
346 PERFETTO_FATAL("For GCC");
347 }
348
IsNumericFieldType(uint32_t type)349 bool IsNumericFieldType(uint32_t type) {
350 switch (type) {
351 case FieldDescriptorProto::TYPE_BYTES:
352 case FieldDescriptorProto::TYPE_MESSAGE:
353 case FieldDescriptorProto::TYPE_STRING:
354 return false;
355 case FieldDescriptorProto::TYPE_INT32:
356 case FieldDescriptorProto::TYPE_SINT32:
357 case FieldDescriptorProto::TYPE_UINT32:
358 case FieldDescriptorProto::TYPE_INT64:
359 case FieldDescriptorProto::TYPE_SINT64:
360 case FieldDescriptorProto::TYPE_UINT64:
361 case FieldDescriptorProto::TYPE_BOOL:
362 case FieldDescriptorProto::TYPE_ENUM:
363 case FieldDescriptorProto::TYPE_FIXED32:
364 case FieldDescriptorProto::TYPE_SFIXED32:
365 case FieldDescriptorProto::TYPE_FLOAT:
366 case FieldDescriptorProto::TYPE_FIXED64:
367 case FieldDescriptorProto::TYPE_SFIXED64:
368 case FieldDescriptorProto::TYPE_DOUBLE:
369 default:
370 return true;
371 }
372 }
373
374 void MessageField(const DescriptorPool& pool,
375 const std::string& type,
376 protozero::ConstBytes protobytes,
377 bool fully_qualify_extensions,
378 JsonBuilder* out);
379 void EnumField(const DescriptorPool& pool,
380 const FieldDescriptor& fd,
381 int32_t value,
382 JsonBuilder* out);
383
384 template <ProtoWireType W, typename T>
PackedField(const DescriptorPool & pool,const FieldDescriptor & fd,const protozero::Field & field,JsonBuilder * out)385 void PackedField(const DescriptorPool& pool,
386 const FieldDescriptor& fd,
387 const protozero::Field& field,
388 JsonBuilder* out) {
389 out->OpenArray();
390 bool e = false;
391 for (PackedRepeatedFieldIterator<W, T> it(field.data(), field.size(), &e); it;
392 it++) {
393 T value = *it;
394 if (fd.type() == FieldDescriptorProto::TYPE_ENUM) {
395 EnumField(pool, fd, static_cast<int32_t>(value), out);
396 } else {
397 out->NumberValue<T>(value);
398 }
399 }
400 out->CloseArray();
401 if (e) {
402 out->AddError(
403 std::string("Decoding failure for field '" + fd.name() + "'"));
404 }
405 }
406
407 template <ProtoWireType W>
PackedBoolField(const DescriptorPool &,const FieldDescriptor & fd,const protozero::Field & field,JsonBuilder * out)408 void PackedBoolField(const DescriptorPool&,
409 const FieldDescriptor& fd,
410 const protozero::Field& field,
411 JsonBuilder* out) {
412 out->OpenArray();
413 bool e = false;
414 for (PackedRepeatedFieldIterator<W, int32_t> it(field.data(), field.size(),
415 &e);
416 it; it++) {
417 bool value = *it;
418 out->BoolValue(value);
419 }
420 out->CloseArray();
421 if (e) {
422 out->AddError(
423 std::string("Decoding failure for field '" + fd.name() + "'"));
424 }
425 }
426
LengthField(const DescriptorPool & pool,const FieldDescriptor * fd,const protozero::Field & field,bool fully_qualify_extensions,JsonBuilder * out)427 void LengthField(const DescriptorPool& pool,
428 const FieldDescriptor* fd,
429 const protozero::Field& field,
430 bool fully_qualify_extensions,
431 JsonBuilder* out) {
432 uint32_t type = fd ? fd->type() : 0;
433 switch (type) {
434 case FieldDescriptorProto::TYPE_BYTES:
435 out->StringValue(field.as_string());
436 return;
437 case FieldDescriptorProto::TYPE_STRING:
438 out->StringValue(field.as_string());
439 return;
440 case FieldDescriptorProto::TYPE_MESSAGE:
441 MessageField(pool, fd->resolved_type_name(), field.as_bytes(),
442 fully_qualify_extensions, out);
443 return;
444 case FieldDescriptorProto::TYPE_DOUBLE:
445 PackedField<ProtoWireType::kFixed64, double>(pool, *fd, field, out);
446 return;
447 case FieldDescriptorProto::TYPE_FLOAT:
448 PackedField<ProtoWireType::kFixed32, float>(pool, *fd, field, out);
449 return;
450 case FieldDescriptorProto::TYPE_FIXED32:
451 PackedField<ProtoWireType::kFixed32, uint32_t>(pool, *fd, field, out);
452 return;
453 case FieldDescriptorProto::TYPE_SFIXED32:
454 PackedField<ProtoWireType::kFixed32, int32_t>(pool, *fd, field, out);
455 return;
456 case FieldDescriptorProto::TYPE_INT32:
457 PackedField<ProtoWireType::kVarInt, int32_t>(pool, *fd, field, out);
458 return;
459 case FieldDescriptorProto::TYPE_SINT32:
460 PackedField<ProtoWireType::kVarInt, int32_t>(pool, *fd, field, out);
461 return;
462 case FieldDescriptorProto::TYPE_UINT32:
463 PackedField<ProtoWireType::kVarInt, uint32_t>(pool, *fd, field, out);
464 return;
465 case FieldDescriptorProto::TYPE_FIXED64:
466 PackedField<ProtoWireType::kFixed64, uint64_t>(pool, *fd, field, out);
467 return;
468 case FieldDescriptorProto::TYPE_SFIXED64:
469 PackedField<ProtoWireType::kFixed64, int64_t>(pool, *fd, field, out);
470 return;
471 case FieldDescriptorProto::TYPE_INT64:
472 PackedField<ProtoWireType::kVarInt, int64_t>(pool, *fd, field, out);
473 return;
474 case FieldDescriptorProto::TYPE_SINT64:
475 PackedField<ProtoWireType::kVarInt, int64_t>(pool, *fd, field, out);
476 return;
477 case FieldDescriptorProto::TYPE_UINT64:
478 PackedField<ProtoWireType::kVarInt, uint64_t>(pool, *fd, field, out);
479 return;
480 case FieldDescriptorProto::TYPE_ENUM:
481 PackedField<ProtoWireType::kVarInt, int32_t>(pool, *fd, field, out);
482 return;
483 case FieldDescriptorProto::TYPE_BOOL:
484 PackedBoolField<ProtoWireType::kVarInt>(pool, *fd, field, out);
485 return;
486 case 0:
487 default:
488 // In the absence of specific information display bytes.
489 out->StringValue(field.as_string());
490 return;
491 }
492 }
493
EnumField(const DescriptorPool & pool,const FieldDescriptor & fd,int32_t value,JsonBuilder * out)494 void EnumField(const DescriptorPool& pool,
495 const FieldDescriptor& fd,
496 int32_t value,
497 JsonBuilder* out) {
498 auto opt_enum_descriptor_idx =
499 pool.FindDescriptorIdx(fd.resolved_type_name());
500 if (!opt_enum_descriptor_idx) {
501 out->NumberValue(value);
502 return;
503 }
504 auto opt_enum_string =
505 pool.descriptors()[*opt_enum_descriptor_idx].FindEnumString(value);
506 // If the enum value is unknown, treat it like a completely unknown field.
507 if (!opt_enum_string) {
508 out->NumberValue(value);
509 return;
510 }
511
512 out->StringValue(base::StringView(*opt_enum_string));
513 }
514
VarIntField(const DescriptorPool & pool,const FieldDescriptor * fd,const protozero::Field & field,JsonBuilder * out)515 void VarIntField(const DescriptorPool& pool,
516 const FieldDescriptor* fd,
517 const protozero::Field& field,
518 JsonBuilder* out) {
519 uint32_t type = fd ? fd->type() : 0;
520 switch (type) {
521 case FieldDescriptorProto::TYPE_INT32:
522 out->NumberValue(field.as_int32());
523 return;
524 case FieldDescriptorProto::TYPE_SINT32:
525 out->NumberValue(field.as_sint32());
526 return;
527 case FieldDescriptorProto::TYPE_UINT32:
528 out->NumberValue(field.as_uint32());
529 return;
530 case FieldDescriptorProto::TYPE_INT64:
531 out->NumberValue(field.as_int64());
532 return;
533 case FieldDescriptorProto::TYPE_SINT64:
534 out->NumberValue(field.as_sint64());
535 return;
536 case FieldDescriptorProto::TYPE_UINT64:
537 out->NumberValue(field.as_uint64());
538 return;
539 case FieldDescriptorProto::TYPE_BOOL:
540 out->BoolValue(field.as_bool());
541 return;
542 case FieldDescriptorProto::TYPE_ENUM:
543 EnumField(pool, *fd, field.as_int32(), out);
544 return;
545 case 0:
546 default:
547 out->NumberValue(field.as_int64());
548 return;
549 }
550 }
551
Fixed32Field(const FieldDescriptor * fd,const protozero::Field & field,JsonBuilder * out)552 void Fixed32Field(const FieldDescriptor* fd,
553 const protozero::Field& field,
554 JsonBuilder* out) {
555 uint32_t type = fd ? fd->type() : 0;
556 switch (type) {
557 case FieldDescriptorProto::TYPE_SFIXED32:
558 out->NumberValue(field.as_int32());
559 break;
560 case FieldDescriptorProto::TYPE_FIXED32:
561 out->NumberValue(field.as_uint32());
562 break;
563 case FieldDescriptorProto::TYPE_FLOAT:
564 out->FloatValue(field.as_float());
565 break;
566 case 0:
567 default:
568 out->NumberValue(field.as_uint32());
569 break;
570 }
571 }
572
Fixed64Field(const FieldDescriptor * fd,const protozero::Field & field,JsonBuilder * out)573 void Fixed64Field(const FieldDescriptor* fd,
574 const protozero::Field& field,
575 JsonBuilder* out) {
576 uint64_t type = fd ? fd->type() : 0;
577 switch (type) {
578 case FieldDescriptorProto::TYPE_SFIXED64:
579 out->NumberValue(field.as_int64());
580 break;
581 case FieldDescriptorProto::TYPE_FIXED64:
582 out->NumberValue(field.as_uint64());
583 break;
584 case FieldDescriptorProto::TYPE_DOUBLE:
585 out->DoubleValue(field.as_double());
586 break;
587 case 0:
588 default:
589 out->NumberValue(field.as_uint64());
590 break;
591 }
592 }
593
RepeatedVarInt(const DescriptorPool & pool,protozero::ConstBytes protobytes,const FieldDescriptor * fd,uint32_t id,JsonBuilder * out)594 void RepeatedVarInt(const DescriptorPool& pool,
595 protozero::ConstBytes protobytes,
596 const FieldDescriptor* fd,
597 uint32_t id,
598 JsonBuilder* out) {
599 out->OpenArray();
600 protozero::ProtoDecoder decoder(protobytes.data, protobytes.size);
601 for (auto field = decoder.ReadField(); field.valid();
602 field = decoder.ReadField()) {
603 if (field.id() == id) {
604 VarIntField(pool, fd, field, out);
605 }
606 }
607 out->CloseArray();
608 }
609
RepeatedLengthField(const DescriptorPool & pool,protozero::ConstBytes protobytes,const FieldDescriptor * fd,uint32_t id,bool fully_qualify_extensions,JsonBuilder * out)610 void RepeatedLengthField(const DescriptorPool& pool,
611 protozero::ConstBytes protobytes,
612 const FieldDescriptor* fd,
613 uint32_t id,
614 bool fully_qualify_extensions,
615 JsonBuilder* out) {
616 out->OpenArray();
617 protozero::ProtoDecoder decoder(protobytes.data, protobytes.size);
618 for (auto field = decoder.ReadField(); field.valid();
619 field = decoder.ReadField()) {
620 if (field.id() == id) {
621 LengthField(pool, fd, field, fully_qualify_extensions, out);
622 }
623 }
624 out->CloseArray();
625 }
626
RepeatedFixed64(protozero::ConstBytes protobytes,const FieldDescriptor * fd,uint32_t id,JsonBuilder * out)627 void RepeatedFixed64(protozero::ConstBytes protobytes,
628 const FieldDescriptor* fd,
629 uint32_t id,
630 JsonBuilder* out) {
631 out->OpenArray();
632 protozero::ProtoDecoder decoder(protobytes.data, protobytes.size);
633 for (auto field = decoder.ReadField(); field.valid();
634 field = decoder.ReadField()) {
635 if (field.id() == id) {
636 Fixed64Field(fd, field, out);
637 }
638 }
639 out->CloseArray();
640 }
641
RepeatedFixed32(protozero::ConstBytes protobytes,const FieldDescriptor * fd,uint32_t id,JsonBuilder * out)642 void RepeatedFixed32(protozero::ConstBytes protobytes,
643 const FieldDescriptor* fd,
644 uint32_t id,
645 JsonBuilder* out) {
646 out->OpenArray();
647 protozero::ProtoDecoder decoder(protobytes.data, protobytes.size);
648 for (auto field = decoder.ReadField(); field.valid();
649 field = decoder.ReadField()) {
650 if (field.id() == id) {
651 Fixed32Field(fd, field, out);
652 }
653 }
654 out->CloseArray();
655 }
656
InnerMessageField(const DescriptorPool & pool,const std::string & type,protozero::ConstBytes protobytes,bool fully_qualify_extensions,JsonBuilder * out)657 void InnerMessageField(const DescriptorPool& pool,
658 const std::string& type,
659 protozero::ConstBytes protobytes,
660 bool fully_qualify_extensions,
661 JsonBuilder* out) {
662 std::optional<uint32_t> opt_proto_desc_idx = pool.FindDescriptorIdx(type);
663 const ProtoDescriptor* opt_proto_descriptor =
664 opt_proto_desc_idx ? &pool.descriptors()[*opt_proto_desc_idx] : nullptr;
665
666 protozero::ProtoDecoder decoder(protobytes.data, protobytes.size);
667 std::unordered_set<uint32_t> fields_seen;
668
669 for (auto field = decoder.ReadField(); field.valid();
670 field = decoder.ReadField()) {
671 auto* opt_field_descriptor =
672 opt_proto_descriptor ? opt_proto_descriptor->FindFieldByTag(field.id())
673 : nullptr;
674 bool is_repeated = false;
675 if (opt_field_descriptor &&
676 IsTypeMatch(field.type(), opt_field_descriptor->type())) {
677 is_repeated = opt_field_descriptor->is_repeated();
678 // The first time we see a repeated field we consume them all:
679 if (fields_seen.count(field.id())) {
680 continue;
681 }
682 if (opt_field_descriptor->is_extension() && fully_qualify_extensions) {
683 out->Key(FulllyQualifiedFieldName(*opt_proto_descriptor,
684 *opt_field_descriptor));
685 } else {
686 out->Key(opt_field_descriptor->name());
687 }
688 } else {
689 out->Key(std::to_string(field.id()));
690 }
691 if (is_repeated) {
692 fields_seen.insert(field.id());
693
694 switch (field.type()) {
695 case ProtoWireType::kVarInt:
696 RepeatedVarInt(pool, protobytes, opt_field_descriptor, field.id(),
697 out);
698 break;
699 case ProtoWireType::kLengthDelimited:
700 if (opt_field_descriptor &&
701 IsNumericFieldType(opt_field_descriptor->type())) {
702 // wire_type = length + field_type in
703 // {u,s,}int{32,64}, float, double etc means this is the
704 // packed case:
705 LengthField(pool, opt_field_descriptor, field,
706 fully_qualify_extensions, out);
707 } else {
708 RepeatedLengthField(pool, protobytes, opt_field_descriptor,
709 field.id(), fully_qualify_extensions, out);
710 }
711 break;
712 case ProtoWireType::kFixed32:
713 RepeatedFixed32(protobytes, opt_field_descriptor, field.id(), out);
714 break;
715 case ProtoWireType::kFixed64:
716 RepeatedFixed64(protobytes, opt_field_descriptor, field.id(), out);
717 break;
718 }
719 } else {
720 switch (field.type()) {
721 case ProtoWireType::kVarInt:
722 VarIntField(pool, opt_field_descriptor, field, out);
723 break;
724 case ProtoWireType::kLengthDelimited:
725 LengthField(pool, opt_field_descriptor, field,
726 fully_qualify_extensions, out);
727 break;
728 case ProtoWireType::kFixed32:
729 Fixed32Field(opt_field_descriptor, field, out);
730 break;
731 case ProtoWireType::kFixed64:
732 Fixed64Field(opt_field_descriptor, field, out);
733 break;
734 }
735 }
736 }
737
738 if (decoder.bytes_left() != 0) {
739 out->AddError(std::to_string(decoder.bytes_left()) + " extra bytes");
740 }
741 }
742
MessageField(const DescriptorPool & pool,const std::string & type,protozero::ConstBytes protobytes,bool fully_qualify_extensions,JsonBuilder * out)743 void MessageField(const DescriptorPool& pool,
744 const std::string& type,
745 protozero::ConstBytes protobytes,
746 bool fully_qualify_extensions,
747 JsonBuilder* out) {
748 out->OpenObject();
749 InnerMessageField(pool, type, protobytes, fully_qualify_extensions, out);
750 out->CloseObject();
751 }
752
753 // Prints all field options for non-empty fields of a message. Example:
754 // --- Message definitions ---
755 // FooMessage {
756 // repeated int64 foo = 1 [op1 = val1, op2 = val2];
757 // optional BarMessage bar = 2 [op3 = val3];
758 // }
759 //
760 // BarMessage {
761 // optional int64 baz = 1 [op4 = val4];
762 // }
763 // --- MessageInstance ---
764 // foo_msg = { // (As JSON)
765 // foo: [23, 24, 25],
766 // bar: {
767 // baz: 42
768 // }
769 // }
770 // --- Output of MessageFieldOptionsToJson(foo_msg) ---
771 // foo: {
772 // __field_options: {
773 // op1: val1,
774 // op2: val2,
775 // },
776 // __repeated: true
777 // }
778 // bar: {
779 // __field_options: {
780 // op3 = val3,
781 // },
782 // baz: {
783 // __field_options: {
784 // op4 = val4
785 // },
786 // }
787 // }
MessageFieldOptionsToJson(const DescriptorPool & pool,const std::string & type,const std::string & field_prefix,const std::unordered_set<std::string> & allowed_fields,JsonBuilder * out)788 void MessageFieldOptionsToJson(
789 const DescriptorPool& pool,
790 const std::string& type,
791 const std::string& field_prefix,
792 const std::unordered_set<std::string>& allowed_fields,
793 JsonBuilder* out) {
794 std::optional<uint32_t> opt_proto_desc_idx = pool.FindDescriptorIdx(type);
795 if (!opt_proto_desc_idx) {
796 return;
797 }
798 const ProtoDescriptor& desc = pool.descriptors()[*opt_proto_desc_idx];
799 for (const auto& id_and_field : desc.fields()) {
800 const FieldDescriptor& field_desc = id_and_field.second;
801 std::string full_field_name = field_prefix + field_desc.name();
802 if (allowed_fields.find(full_field_name) == allowed_fields.end()) {
803 continue;
804 }
805 if (field_desc.is_extension()) {
806 out->Key(FulllyQualifiedFieldName(desc, field_desc));
807 } else {
808 out->Key(field_desc.name());
809 }
810 out->OpenObject();
811 if (HasFieldOptions(field_desc)) {
812 out->Key("__field_options");
813 MessageField(pool, ".google.protobuf.FieldOptions",
814 protozero::ConstBytes{field_desc.options().data(),
815 field_desc.options().size()},
816 false, out);
817 }
818 if (field_desc.type() == FieldDescriptorProto::Type::TYPE_MESSAGE) {
819 MessageFieldOptionsToJson(pool, field_desc.resolved_type_name(),
820 full_field_name + ".", allowed_fields, out);
821 }
822 if (field_desc.is_repeated()) {
823 out->Key("__repeated");
824 out->BoolValue(true);
825 }
826 out->CloseObject();
827 }
828 }
829
PopulateAllowedFieldOptionsSet(const DescriptorPool & pool,const std::string & type,const std::string & field_prefix,protozero::ConstBytes protobytes,std::unordered_set<std::string> & allowed_fields)830 bool PopulateAllowedFieldOptionsSet(
831 const DescriptorPool& pool,
832 const std::string& type,
833 const std::string& field_prefix,
834 protozero::ConstBytes protobytes,
835 std::unordered_set<std::string>& allowed_fields) {
836 std::optional<uint32_t> opt_proto_desc_idx = pool.FindDescriptorIdx(type);
837 if (!opt_proto_desc_idx) {
838 return false;
839 }
840 const ProtoDescriptor& desc = pool.descriptors()[*opt_proto_desc_idx];
841 protozero::ProtoDecoder decoder(protobytes);
842 bool allowed = false;
843 for (auto field = decoder.ReadField(); field.valid();
844 field = decoder.ReadField()) {
845 auto* opt_field_descriptor = desc.FindFieldByTag(field.id());
846 if (!opt_field_descriptor) {
847 continue;
848 }
849 std::string full_field_name = field_prefix + opt_field_descriptor->name();
850 bool nested = false;
851 if (opt_field_descriptor->type() ==
852 protos::pbzero::FieldDescriptorProto::TYPE_MESSAGE) {
853 nested = PopulateAllowedFieldOptionsSet(
854 pool, opt_field_descriptor->resolved_type_name(),
855 full_field_name + ".", field.as_bytes(), allowed_fields);
856 }
857 if (nested || HasFieldOptions(*opt_field_descriptor)) {
858 allowed_fields.emplace(full_field_name);
859 allowed = true;
860 }
861 }
862 return allowed;
863 }
864
865 } // namespace
866
ProtozeroToJson(const DescriptorPool & pool,const std::string & type,protozero::ConstBytes protobytes,int flags)867 std::string ProtozeroToJson(const DescriptorPool& pool,
868 const std::string& type,
869 protozero::ConstBytes protobytes,
870 int flags) {
871 JsonBuilder builder(flags);
872 builder.OpenObject();
873 InnerMessageField(pool, type, protobytes, true, &builder);
874 if (builder.is_inline_errors() && !builder.errors().empty()) {
875 builder.Key("__error");
876 builder.StringValue(base::StringView(base::Join(builder.errors(), "\n")));
877 }
878 if (flags & kInlineAnnotations) {
879 std::unordered_set<std::string> allowed_fields;
880 PopulateAllowedFieldOptionsSet(pool, type, "", protobytes, allowed_fields);
881 if (!allowed_fields.empty()) {
882 builder.Key("__annotations");
883 builder.OpenObject();
884 MessageFieldOptionsToJson(pool, type, "", allowed_fields, &builder);
885 builder.CloseObject();
886 }
887 }
888 builder.CloseObject();
889 return builder.ToString();
890 }
891
ProtozeroToJson(const DescriptorPool & pool,const std::string & type,const std::vector<uint8_t> & protobytes,int flags)892 std::string ProtozeroToJson(const DescriptorPool& pool,
893 const std::string& type,
894 const std::vector<uint8_t>& protobytes,
895 int flags) {
896 return ProtozeroToJson(
897 pool, type, protozero::ConstBytes{protobytes.data(), protobytes.size()},
898 flags);
899 }
900
901 } // namespace protozero_to_json
902 } // namespace trace_processor
903 } // namespace perfetto
904