1 /*
2 * Copyright (C) 2018 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/perfetto_cmd/pbtxt_to_pb.h"
18
19 #include <ctype.h>
20 #include <limits>
21 #include <map>
22 #include <optional>
23 #include <set>
24 #include <stack>
25 #include <string>
26
27 #include "perfetto/base/logging.h"
28 #include "perfetto/ext/base/file_utils.h"
29 #include "perfetto/ext/base/string_utils.h"
30 #include "perfetto/ext/base/string_view.h"
31 #include "perfetto/ext/base/utils.h"
32 #include "perfetto/protozero/message.h"
33 #include "perfetto/protozero/message_handle.h"
34 #include "perfetto/protozero/scattered_heap_buffer.h"
35 #include "src/perfetto_cmd/config.descriptor.h"
36
37 #include "protos/perfetto/common/descriptor.gen.h"
38
39 namespace perfetto {
40 constexpr char kConfigProtoName[] = ".perfetto.protos.TraceConfig";
41
42 using protos::gen::DescriptorProto;
43 using protos::gen::EnumDescriptorProto;
44 using protos::gen::EnumValueDescriptorProto;
45 using protos::gen::FieldDescriptorProto;
46 using protos::gen::FileDescriptorSet;
47
48 namespace {
49
IsOct(char c)50 constexpr bool IsOct(char c) {
51 return (c >= '0' && c <= '7');
52 }
53
IsDigit(char c)54 constexpr bool IsDigit(char c) {
55 return (c >= '0' && c <= '9');
56 }
57
IsIdentifierStart(char c)58 constexpr bool IsIdentifierStart(char c) {
59 return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || c == '_';
60 }
61
IsIdentifierBody(char c)62 constexpr bool IsIdentifierBody(char c) {
63 return IsIdentifierStart(c) || IsDigit(c);
64 }
65
FieldToTypeName(const FieldDescriptorProto * field)66 const char* FieldToTypeName(const FieldDescriptorProto* field) {
67 switch (field->type()) {
68 case FieldDescriptorProto::TYPE_UINT64:
69 return "uint64";
70 case FieldDescriptorProto::TYPE_UINT32:
71 return "uint32";
72 case FieldDescriptorProto::TYPE_INT64:
73 return "int64";
74 case FieldDescriptorProto::TYPE_SINT64:
75 return "sint64";
76 case FieldDescriptorProto::TYPE_INT32:
77 return "int32";
78 case FieldDescriptorProto::TYPE_SINT32:
79 return "sint32";
80 case FieldDescriptorProto::TYPE_FIXED64:
81 return "fixed64";
82 case FieldDescriptorProto::TYPE_SFIXED64:
83 return "sfixed64";
84 case FieldDescriptorProto::TYPE_FIXED32:
85 return "fixed32";
86 case FieldDescriptorProto::TYPE_SFIXED32:
87 return "sfixed32";
88 case FieldDescriptorProto::TYPE_DOUBLE:
89 return "double";
90 case FieldDescriptorProto::TYPE_FLOAT:
91 return "float";
92 case FieldDescriptorProto::TYPE_BOOL:
93 return "bool";
94 case FieldDescriptorProto::TYPE_STRING:
95 return "string";
96 case FieldDescriptorProto::TYPE_BYTES:
97 return "bytes";
98 case FieldDescriptorProto::TYPE_GROUP:
99 return "group";
100 case FieldDescriptorProto::TYPE_MESSAGE:
101 return "message";
102 case FieldDescriptorProto::TYPE_ENUM:
103 return "enum";
104 }
105 // For gcc
106 PERFETTO_FATAL("Non complete switch");
107 }
108
Format(const char * fmt,std::map<std::string,std::string> args)109 std::string Format(const char* fmt, std::map<std::string, std::string> args) {
110 std::string result(fmt);
111 for (const auto& key_value : args) {
112 size_t start = result.find(key_value.first);
113 PERFETTO_CHECK(start != std::string::npos);
114 result.replace(start, key_value.first.size(), key_value.second);
115 PERFETTO_CHECK(result.find(key_value.first) == std::string::npos);
116 }
117 return result;
118 }
119
120 enum ParseState {
121 kWaitingForKey,
122 kReadingKey,
123 kWaitingForValue,
124 kReadingStringValue,
125 kReadingStringEscape,
126 kReadingNumericValue,
127 kReadingIdentifierValue,
128 };
129
130 struct Token {
131 size_t offset;
132 size_t column;
133 size_t row;
134 base::StringView txt;
135
sizeperfetto::__anoncae73b490111::Token136 size_t size() const { return txt.size(); }
ToStdStringperfetto::__anoncae73b490111::Token137 std::string ToStdString() const { return txt.ToStdString(); }
138 };
139
140 struct ParserDelegateContext {
141 const DescriptorProto* descriptor;
142 protozero::Message* message;
143 std::set<std::string> seen_fields;
144 };
145
146 class ParserDelegate {
147 public:
ParserDelegate(const DescriptorProto * descriptor,protozero::Message * message,ErrorReporter * reporter,std::map<std::string,const DescriptorProto * > name_to_descriptor,std::map<std::string,const EnumDescriptorProto * > name_to_enum)148 ParserDelegate(
149 const DescriptorProto* descriptor,
150 protozero::Message* message,
151 ErrorReporter* reporter,
152 std::map<std::string, const DescriptorProto*> name_to_descriptor,
153 std::map<std::string, const EnumDescriptorProto*> name_to_enum)
154 : reporter_(reporter),
155 name_to_descriptor_(std::move(name_to_descriptor)),
156 name_to_enum_(std::move(name_to_enum)) {
157 ctx_.push(ParserDelegateContext{descriptor, message, {}});
158 }
159
NumericField(Token key,Token value)160 void NumericField(Token key, Token value) {
161 const FieldDescriptorProto* field =
162 FindFieldByName(key, value,
163 {
164 FieldDescriptorProto::TYPE_UINT64,
165 FieldDescriptorProto::TYPE_UINT32,
166 FieldDescriptorProto::TYPE_INT64,
167 FieldDescriptorProto::TYPE_SINT64,
168 FieldDescriptorProto::TYPE_INT32,
169 FieldDescriptorProto::TYPE_SINT32,
170 FieldDescriptorProto::TYPE_FIXED64,
171 FieldDescriptorProto::TYPE_SFIXED64,
172 FieldDescriptorProto::TYPE_FIXED32,
173 FieldDescriptorProto::TYPE_SFIXED32,
174 FieldDescriptorProto::TYPE_DOUBLE,
175 FieldDescriptorProto::TYPE_FLOAT,
176 });
177 if (!field)
178 return;
179 const auto& field_type = field->type();
180 switch (field_type) {
181 case FieldDescriptorProto::TYPE_UINT64:
182 return VarIntField<uint64_t>(field, value);
183 case FieldDescriptorProto::TYPE_UINT32:
184 return VarIntField<uint32_t>(field, value);
185 case FieldDescriptorProto::TYPE_INT64:
186 case FieldDescriptorProto::TYPE_SINT64:
187 return VarIntField<int64_t>(field, value);
188 case FieldDescriptorProto::TYPE_INT32:
189 case FieldDescriptorProto::TYPE_SINT32:
190 return VarIntField<int32_t>(field, value);
191
192 case FieldDescriptorProto::TYPE_FIXED64:
193 case FieldDescriptorProto::TYPE_SFIXED64:
194 return FixedField<int64_t>(field, value);
195
196 case FieldDescriptorProto::TYPE_FIXED32:
197 case FieldDescriptorProto::TYPE_SFIXED32:
198 return FixedField<int32_t>(field, value);
199
200 case FieldDescriptorProto::TYPE_DOUBLE:
201 return FixedFloatField<double>(field, value);
202 case FieldDescriptorProto::TYPE_FLOAT:
203 return FixedFloatField<float>(field, value);
204
205 case FieldDescriptorProto::TYPE_BOOL:
206 case FieldDescriptorProto::TYPE_STRING:
207 case FieldDescriptorProto::TYPE_BYTES:
208 case FieldDescriptorProto::TYPE_GROUP:
209 case FieldDescriptorProto::TYPE_MESSAGE:
210 case FieldDescriptorProto::TYPE_ENUM:
211 PERFETTO_FATAL("Invalid type");
212 }
213 }
214
StringField(Token key,Token value)215 void StringField(Token key, Token value) {
216 const FieldDescriptorProto* field =
217 FindFieldByName(key, value,
218 {
219 FieldDescriptorProto::TYPE_STRING,
220 FieldDescriptorProto::TYPE_BYTES,
221 });
222 if (!field)
223 return;
224 uint32_t field_id = static_cast<uint32_t>(field->number());
225 const auto& field_type = field->type();
226 PERFETTO_CHECK(field_type == FieldDescriptorProto::TYPE_STRING ||
227 field_type == FieldDescriptorProto::TYPE_BYTES);
228
229 std::unique_ptr<char, base::FreeDeleter> s(
230 static_cast<char*>(malloc(value.size())));
231 size_t j = 0;
232 const char* const txt = value.txt.data();
233 for (size_t i = 0; i < value.size(); i++) {
234 char c = txt[i];
235 if (c == '\\') {
236 if (i + 1 >= value.size()) {
237 // This should be caught by the lexer.
238 PERFETTO_FATAL("Escape at end of string.");
239 return;
240 }
241 char next = txt[++i];
242 switch (next) {
243 case '\\':
244 case '\'':
245 case '"':
246 case '?':
247 s.get()[j++] = next;
248 break;
249 case 'a':
250 s.get()[j++] = '\a';
251 break;
252 case 'b':
253 s.get()[j++] = '\b';
254 break;
255 case 'f':
256 s.get()[j++] = '\f';
257 break;
258 case 'n':
259 s.get()[j++] = '\n';
260 break;
261 case 'r':
262 s.get()[j++] = '\r';
263 break;
264 case 't':
265 s.get()[j++] = '\t';
266 break;
267 case 'v':
268 s.get()[j++] = '\v';
269 break;
270 case '0':
271 case '1':
272 case '2':
273 case '3':
274 case '4':
275 case '5':
276 case '6':
277 case '7':
278 case '8':
279 case '9': {
280 // Cases 8 and 9 are not really required and are only added for the
281 // sake of error reporting.
282 bool oct_err = false;
283 if (i + 2 >= value.size() || !IsOct(txt[i + 1]) ||
284 !IsOct(txt[i + 2])) {
285 oct_err = true;
286 } else {
287 char buf[4]{next, txt[++i], txt[++i], '\0'};
288 auto octval = base::CStringToUInt32(buf, 8);
289 if (!octval.has_value() || *octval > 0xff) {
290 oct_err = true;
291 } else {
292 s.get()[j++] = static_cast<char>(static_cast<uint8_t>(*octval));
293 }
294 }
295 if (oct_err) {
296 AddError(value,
297 "Malformed string escape in $k in proto $n on '$v'. "
298 "\\NNN escapes must be exactly three octal digits <= "
299 "\\377 (0xff).",
300 std::map<std::string, std::string>{
301 {"$k", key.ToStdString()},
302 {"$n", descriptor_name()},
303 {"$v", value.ToStdString()},
304 });
305 }
306 break;
307 }
308 default:
309 AddError(value,
310 "Unknown string escape in $k in "
311 "proto $n: '$v'",
312 std::map<std::string, std::string>{
313 {"$k", key.ToStdString()},
314 {"$n", descriptor_name()},
315 {"$v", value.ToStdString()},
316 });
317 return;
318 }
319 } else {
320 s.get()[j++] = c;
321 }
322 }
323 msg()->AppendBytes(field_id, s.get(), j);
324 }
325
IdentifierField(Token key,Token value)326 void IdentifierField(Token key, Token value) {
327 const FieldDescriptorProto* field =
328 FindFieldByName(key, value,
329 {
330 FieldDescriptorProto::TYPE_BOOL,
331 FieldDescriptorProto::TYPE_ENUM,
332 });
333 if (!field)
334 return;
335 uint32_t field_id = static_cast<uint32_t>(field->number());
336 const auto& field_type = field->type();
337 if (field_type == FieldDescriptorProto::TYPE_BOOL) {
338 if (value.txt != "true" && value.txt != "false") {
339 AddError(value,
340 "Expected 'true' or 'false' for boolean field $k in "
341 "proto $n instead saw '$v'",
342 std::map<std::string, std::string>{
343 {"$k", key.ToStdString()},
344 {"$n", descriptor_name()},
345 {"$v", value.ToStdString()},
346 });
347 return;
348 }
349 msg()->AppendTinyVarInt(field_id, value.txt == "true" ? 1 : 0);
350 } else if (field_type == FieldDescriptorProto::TYPE_ENUM) {
351 const std::string& type_name = field->type_name();
352 const EnumDescriptorProto* enum_descriptor = name_to_enum_[type_name];
353 PERFETTO_CHECK(enum_descriptor);
354 bool found_value = false;
355 int32_t enum_value_number = 0;
356 for (const EnumValueDescriptorProto& enum_value :
357 enum_descriptor->value()) {
358 if (value.ToStdString() != enum_value.name())
359 continue;
360 found_value = true;
361 enum_value_number = enum_value.number();
362 break;
363 }
364 if (!found_value) {
365 AddError(value,
366 "Unexpected value '$v' for enum field $k in "
367 "proto $n",
368 std::map<std::string, std::string>{
369 {"$v", value.ToStdString()},
370 {"$k", key.ToStdString()},
371 {"$n", descriptor_name()},
372 });
373 return;
374 }
375 msg()->AppendVarInt<int32_t>(field_id, enum_value_number);
376 }
377 }
378
BeginNestedMessage(Token key,Token value)379 bool BeginNestedMessage(Token key, Token value) {
380 const FieldDescriptorProto* field =
381 FindFieldByName(key, value,
382 {
383 FieldDescriptorProto::TYPE_MESSAGE,
384 });
385 if (!field) {
386 // FindFieldByName adds an error.
387 return false;
388 }
389 uint32_t field_id = static_cast<uint32_t>(field->number());
390 const std::string& type_name = field->type_name();
391 const DescriptorProto* nested_descriptor = name_to_descriptor_[type_name];
392 PERFETTO_CHECK(nested_descriptor);
393 auto* nested_msg = msg()->BeginNestedMessage<protozero::Message>(field_id);
394 ctx_.push(ParserDelegateContext{nested_descriptor, nested_msg, {}});
395 return true;
396 }
397
EndNestedMessage()398 void EndNestedMessage() {
399 msg()->Finalize();
400 ctx_.pop();
401 }
402
Eof()403 void Eof() {}
404
AddError(size_t row,size_t column,const char * fmt,const std::map<std::string,std::string> & args)405 void AddError(size_t row,
406 size_t column,
407 const char* fmt,
408 const std::map<std::string, std::string>& args) {
409 reporter_->AddError(row, column, 0, Format(fmt, args));
410 }
411
AddError(Token token,const char * fmt,const std::map<std::string,std::string> & args)412 void AddError(Token token,
413 const char* fmt,
414 const std::map<std::string, std::string>& args) {
415 reporter_->AddError(token.row, token.column, token.size(),
416 Format(fmt, args));
417 }
418
419 private:
420 template <typename T>
VarIntField(const FieldDescriptorProto * field,Token t)421 void VarIntField(const FieldDescriptorProto* field, Token t) {
422 uint32_t field_id = static_cast<uint32_t>(field->number());
423 uint64_t n = 0;
424 PERFETTO_CHECK(ParseInteger(t.txt, &n));
425 if (field->type() == FieldDescriptorProto::TYPE_SINT64 ||
426 field->type() == FieldDescriptorProto::TYPE_SINT32) {
427 msg()->AppendSignedVarInt<T>(field_id, static_cast<T>(n));
428 } else {
429 msg()->AppendVarInt<T>(field_id, static_cast<T>(n));
430 }
431 }
432
433 template <typename T>
FixedField(const FieldDescriptorProto * field,Token t)434 void FixedField(const FieldDescriptorProto* field, Token t) {
435 uint32_t field_id = static_cast<uint32_t>(field->number());
436 uint64_t n = 0;
437 PERFETTO_CHECK(ParseInteger(t.txt, &n));
438 msg()->AppendFixed<T>(field_id, static_cast<T>(n));
439 }
440
441 template <typename T>
FixedFloatField(const FieldDescriptorProto * field,Token t)442 void FixedFloatField(const FieldDescriptorProto* field, Token t) {
443 uint32_t field_id = static_cast<uint32_t>(field->number());
444 std::optional<double> opt_n = base::StringToDouble(t.ToStdString());
445 msg()->AppendFixed<T>(field_id, static_cast<T>(opt_n.value_or(0l)));
446 }
447
448 template <typename T>
ParseInteger(base::StringView s,T * number_ptr)449 bool ParseInteger(base::StringView s, T* number_ptr) {
450 uint64_t n = 0;
451 PERFETTO_CHECK(sscanf(s.ToStdString().c_str(), "%" PRIu64, &n) == 1);
452 PERFETTO_CHECK(n <= std::numeric_limits<T>::max());
453 *number_ptr = static_cast<T>(n);
454 return true;
455 }
456
FindFieldByName(Token key,Token value,std::set<FieldDescriptorProto::Type> valid_field_types)457 const FieldDescriptorProto* FindFieldByName(
458 Token key,
459 Token value,
460 std::set<FieldDescriptorProto::Type> valid_field_types) {
461 const std::string field_name = key.ToStdString();
462 const FieldDescriptorProto* field_descriptor = nullptr;
463 for (const auto& f : descriptor()->field()) {
464 if (f.name() == field_name) {
465 field_descriptor = &f;
466 break;
467 }
468 }
469
470 if (!field_descriptor) {
471 AddError(key, "No field named \"$n\" in proto $p",
472 {
473 {"$n", field_name},
474 {"$p", descriptor_name()},
475 });
476 return nullptr;
477 }
478
479 bool is_repeated =
480 field_descriptor->label() == FieldDescriptorProto::LABEL_REPEATED;
481 auto it_and_inserted = ctx_.top().seen_fields.emplace(field_name);
482 if (!it_and_inserted.second && !is_repeated) {
483 AddError(key, "Saw non-repeating field '$f' more than once",
484 {
485 {"$f", field_name},
486 });
487 }
488
489 if (!valid_field_types.count(field_descriptor->type())) {
490 AddError(value,
491 "Expected value of type $t for field $k in proto $n "
492 "instead saw '$v'",
493 {
494 {"$t", FieldToTypeName(field_descriptor)},
495 {"$k", field_name},
496 {"$n", descriptor_name()},
497 {"$v", value.ToStdString()},
498 });
499 return nullptr;
500 }
501
502 return field_descriptor;
503 }
504
descriptor()505 const DescriptorProto* descriptor() {
506 PERFETTO_CHECK(!ctx_.empty());
507 return ctx_.top().descriptor;
508 }
509
descriptor_name()510 const std::string& descriptor_name() { return descriptor()->name(); }
511
msg()512 protozero::Message* msg() {
513 PERFETTO_CHECK(!ctx_.empty());
514 return ctx_.top().message;
515 }
516
517 std::stack<ParserDelegateContext> ctx_;
518 ErrorReporter* reporter_;
519 std::map<std::string, const DescriptorProto*> name_to_descriptor_;
520 std::map<std::string, const EnumDescriptorProto*> name_to_enum_;
521 };
522
Parse(const std::string & input,ParserDelegate * delegate)523 void Parse(const std::string& input, ParserDelegate* delegate) {
524 ParseState state = kWaitingForKey;
525 size_t column = 0;
526 size_t row = 1;
527 size_t depth = 0;
528 bool saw_colon_for_this_key = false;
529 bool saw_semicolon_for_this_value = true;
530 bool comment_till_eol = false;
531 Token key{};
532 Token value{};
533
534 for (size_t i = 0; i < input.size(); i++, column++) {
535 bool last_character = i + 1 == input.size();
536 char c = input.at(i);
537 if (c == '\n') {
538 column = 0;
539 row++;
540 if (comment_till_eol) {
541 comment_till_eol = false;
542 continue;
543 }
544 }
545 if (comment_till_eol)
546 continue;
547
548 switch (state) {
549 case kWaitingForKey:
550 if (isspace(c))
551 continue;
552 if (c == '#') {
553 comment_till_eol = true;
554 continue;
555 }
556 if (c == '}') {
557 if (depth == 0) {
558 delegate->AddError(row, column, "Unmatched closing brace", {});
559 return;
560 }
561 saw_semicolon_for_this_value = false;
562 depth--;
563 delegate->EndNestedMessage();
564 continue;
565 }
566 if (!saw_semicolon_for_this_value && c == ';') {
567 saw_semicolon_for_this_value = true;
568 continue;
569 }
570 if (IsIdentifierStart(c)) {
571 saw_colon_for_this_key = false;
572 state = kReadingKey;
573 key.offset = i;
574 key.row = row;
575 key.column = column;
576 continue;
577 }
578 break;
579
580 case kReadingKey:
581 if (IsIdentifierBody(c))
582 continue;
583 key.txt = base::StringView(input.data() + key.offset, i - key.offset);
584 state = kWaitingForValue;
585 if (c == '#')
586 comment_till_eol = true;
587 continue;
588
589 case kWaitingForValue:
590 if (isspace(c))
591 continue;
592 if (c == '#') {
593 comment_till_eol = true;
594 continue;
595 }
596 value.offset = i;
597 value.row = row;
598 value.column = column;
599
600 if (c == ':' && !saw_colon_for_this_key) {
601 saw_colon_for_this_key = true;
602 continue;
603 }
604 if (c == '"') {
605 state = kReadingStringValue;
606 continue;
607 }
608 if (c == '-' || IsDigit(c) || c == '.') {
609 state = kReadingNumericValue;
610 continue;
611 }
612 if (IsIdentifierStart(c)) {
613 state = kReadingIdentifierValue;
614 continue;
615 }
616 if (c == '{') {
617 state = kWaitingForKey;
618 depth++;
619 value.txt = base::StringView(input.data() + value.offset, 1);
620 if (!delegate->BeginNestedMessage(key, value)) {
621 return;
622 }
623 continue;
624 }
625 break;
626
627 case kReadingNumericValue:
628 if (isspace(c) || c == ';' || last_character) {
629 bool keep_last = last_character && !(isspace(c) || c == ';');
630 size_t size = i - value.offset + (keep_last ? 1 : 0);
631 value.txt = base::StringView(input.data() + value.offset, size);
632 saw_semicolon_for_this_value = c == ';';
633 state = kWaitingForKey;
634 delegate->NumericField(key, value);
635 continue;
636 }
637 if (IsDigit(c) || c == '.')
638 continue;
639 break;
640
641 case kReadingStringValue:
642 if (c == '\\') {
643 state = kReadingStringEscape;
644 } else if (c == '"') {
645 size_t size = i - value.offset - 1;
646 value.column++;
647 value.txt = base::StringView(input.data() + value.offset + 1, size);
648 saw_semicolon_for_this_value = false;
649 state = kWaitingForKey;
650 delegate->StringField(key, value);
651 }
652 continue;
653
654 case kReadingStringEscape:
655 state = kReadingStringValue;
656 continue;
657
658 case kReadingIdentifierValue:
659 if (isspace(c) || c == ';' || c == '#' || last_character) {
660 bool keep_last =
661 last_character && !(isspace(c) || c == ';' || c == '#');
662 size_t size = i - value.offset + (keep_last ? 1 : 0);
663 value.txt = base::StringView(input.data() + value.offset, size);
664 comment_till_eol = c == '#';
665 saw_semicolon_for_this_value = c == ';';
666 state = kWaitingForKey;
667 delegate->IdentifierField(key, value);
668 continue;
669 }
670 if (IsIdentifierBody(c)) {
671 continue;
672 }
673 break;
674 }
675 delegate->AddError(row, column, "Unexpected character '$c'",
676 std::map<std::string, std::string>{
677 {"$c", std::string(1, c)},
678 });
679 return;
680 } // for
681 if (depth > 0)
682 delegate->AddError(row, column, "Nested message not closed", {});
683 if (state != kWaitingForKey)
684 delegate->AddError(row, column, "Unexpected end of input", {});
685 delegate->Eof();
686 }
687
AddNestedDescriptors(const std::string & prefix,const DescriptorProto * descriptor,std::map<std::string,const DescriptorProto * > * name_to_descriptor,std::map<std::string,const EnumDescriptorProto * > * name_to_enum)688 void AddNestedDescriptors(
689 const std::string& prefix,
690 const DescriptorProto* descriptor,
691 std::map<std::string, const DescriptorProto*>* name_to_descriptor,
692 std::map<std::string, const EnumDescriptorProto*>* name_to_enum) {
693 for (const EnumDescriptorProto& enum_descriptor : descriptor->enum_type()) {
694 const std::string name = prefix + "." + enum_descriptor.name();
695 (*name_to_enum)[name] = &enum_descriptor;
696 }
697 for (const DescriptorProto& nested_descriptor : descriptor->nested_type()) {
698 const std::string name = prefix + "." + nested_descriptor.name();
699 (*name_to_descriptor)[name] = &nested_descriptor;
700 AddNestedDescriptors(name, &nested_descriptor, name_to_descriptor,
701 name_to_enum);
702 }
703 }
704
705 } // namespace
706
707 ErrorReporter::ErrorReporter() = default;
708 ErrorReporter::~ErrorReporter() = default;
709
PbtxtToPb(const std::string & input,ErrorReporter * reporter)710 std::vector<uint8_t> PbtxtToPb(const std::string& input,
711 ErrorReporter* reporter) {
712 std::map<std::string, const DescriptorProto*> name_to_descriptor;
713 std::map<std::string, const EnumDescriptorProto*> name_to_enum;
714 FileDescriptorSet file_descriptor_set;
715
716 {
717 file_descriptor_set.ParseFromArray(
718 kConfigDescriptor.data(), static_cast<int>(kConfigDescriptor.size()));
719 for (const auto& file_descriptor : file_descriptor_set.file()) {
720 for (const auto& enum_descriptor : file_descriptor.enum_type()) {
721 const std::string name =
722 "." + file_descriptor.package() + "." + enum_descriptor.name();
723 name_to_enum[name] = &enum_descriptor;
724 }
725 for (const auto& descriptor : file_descriptor.message_type()) {
726 const std::string name =
727 "." + file_descriptor.package() + "." + descriptor.name();
728 name_to_descriptor[name] = &descriptor;
729 AddNestedDescriptors(name, &descriptor, &name_to_descriptor,
730 &name_to_enum);
731 }
732 }
733 }
734
735 const DescriptorProto* descriptor = name_to_descriptor[kConfigProtoName];
736 PERFETTO_CHECK(descriptor);
737
738 protozero::HeapBuffered<protozero::Message> message;
739 ParserDelegate delegate(descriptor, message.get(), reporter,
740 std::move(name_to_descriptor),
741 std::move(name_to_enum));
742 Parse(input, &delegate);
743 return message.SerializeAsArray();
744 }
745
746 } // namespace perfetto
747