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 <ctype.h>
18
19 #include <map>
20 #include <set>
21 #include <stack>
22 #include <string>
23
24 #include "src/perfetto_cmd/pbtxt_to_pb.h"
25
26 #include "perfetto/base/logging.h"
27 #include "perfetto/ext/base/file_utils.h"
28 #include "perfetto/ext/base/optional.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/perfetto_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::__anon5c3d75eb0111::Token136 size_t size() const { return txt.size(); }
ToStdStringperfetto::__anon5c3d75eb0111::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 void BeginNestedMessage(Token key, Token value) {
380 const FieldDescriptorProto* field =
381 FindFieldByName(key, value,
382 {
383 FieldDescriptorProto::TYPE_MESSAGE,
384 });
385 if (!field)
386 return;
387 uint32_t field_id = static_cast<uint32_t>(field->number());
388 const std::string& type_name = field->type_name();
389 const DescriptorProto* nested_descriptor = name_to_descriptor_[type_name];
390 PERFETTO_CHECK(nested_descriptor);
391 auto* nested_msg = msg()->BeginNestedMessage<protozero::Message>(field_id);
392 ctx_.push(ParserDelegateContext{nested_descriptor, nested_msg, {}});
393 }
394
EndNestedMessage()395 void EndNestedMessage() {
396 msg()->Finalize();
397 ctx_.pop();
398 }
399
Eof()400 void Eof() {}
401
AddError(size_t row,size_t column,const char * fmt,const std::map<std::string,std::string> & args)402 void AddError(size_t row,
403 size_t column,
404 const char* fmt,
405 const std::map<std::string, std::string>& args) {
406 reporter_->AddError(row, column, 0, Format(fmt, args));
407 }
408
AddError(Token token,const char * fmt,const std::map<std::string,std::string> & args)409 void AddError(Token token,
410 const char* fmt,
411 const std::map<std::string, std::string>& args) {
412 reporter_->AddError(token.row, token.column, token.size(),
413 Format(fmt, args));
414 }
415
416 private:
417 template <typename T>
VarIntField(const FieldDescriptorProto * field,Token t)418 void VarIntField(const FieldDescriptorProto* field, Token t) {
419 uint32_t field_id = static_cast<uint32_t>(field->number());
420 uint64_t n = 0;
421 PERFETTO_CHECK(ParseInteger(t.txt, &n));
422 if (field->type() == FieldDescriptorProto::TYPE_SINT64 ||
423 field->type() == FieldDescriptorProto::TYPE_SINT32) {
424 msg()->AppendSignedVarInt<T>(field_id, static_cast<T>(n));
425 } else {
426 msg()->AppendVarInt<T>(field_id, static_cast<T>(n));
427 }
428 }
429
430 template <typename T>
FixedField(const FieldDescriptorProto * field,Token t)431 void FixedField(const FieldDescriptorProto* field, Token t) {
432 uint32_t field_id = static_cast<uint32_t>(field->number());
433 uint64_t n = 0;
434 PERFETTO_CHECK(ParseInteger(t.txt, &n));
435 msg()->AppendFixed<T>(field_id, static_cast<T>(n));
436 }
437
438 template <typename T>
FixedFloatField(const FieldDescriptorProto * field,Token t)439 void FixedFloatField(const FieldDescriptorProto* field, Token t) {
440 uint32_t field_id = static_cast<uint32_t>(field->number());
441 base::Optional<double> opt_n = base::StringToDouble(t.ToStdString());
442 msg()->AppendFixed<T>(field_id, static_cast<T>(opt_n.value_or(0l)));
443 }
444
445 template <typename T>
ParseInteger(base::StringView s,T * number_ptr)446 bool ParseInteger(base::StringView s, T* number_ptr) {
447 uint64_t n = 0;
448 PERFETTO_CHECK(sscanf(s.ToStdString().c_str(), "%" PRIu64, &n) == 1);
449 PERFETTO_CHECK(n <= std::numeric_limits<T>::max());
450 *number_ptr = static_cast<T>(n);
451 return true;
452 }
453
FindFieldByName(Token key,Token value,std::set<FieldDescriptorProto::Type> valid_field_types)454 const FieldDescriptorProto* FindFieldByName(
455 Token key,
456 Token value,
457 std::set<FieldDescriptorProto::Type> valid_field_types) {
458 const std::string field_name = key.ToStdString();
459 const FieldDescriptorProto* field_descriptor = nullptr;
460 for (const auto& f : descriptor()->field()) {
461 if (f.name() == field_name) {
462 field_descriptor = &f;
463 break;
464 }
465 }
466
467 if (!field_descriptor) {
468 AddError(key, "No field named \"$n\" in proto $p",
469 {
470 {"$n", field_name},
471 {"$p", descriptor_name()},
472 });
473 return nullptr;
474 }
475
476 bool is_repeated =
477 field_descriptor->label() == FieldDescriptorProto::LABEL_REPEATED;
478 auto it_and_inserted = ctx_.top().seen_fields.emplace(field_name);
479 if (!it_and_inserted.second && !is_repeated) {
480 AddError(key, "Saw non-repeating field '$f' more than once",
481 {
482 {"$f", field_name},
483 });
484 }
485
486 if (!valid_field_types.count(field_descriptor->type())) {
487 AddError(value,
488 "Expected value of type $t for field $k in proto $n "
489 "instead saw '$v'",
490 {
491 {"$t", FieldToTypeName(field_descriptor)},
492 {"$k", field_name},
493 {"$n", descriptor_name()},
494 {"$v", value.ToStdString()},
495 });
496 return nullptr;
497 }
498
499 return field_descriptor;
500 }
501
descriptor()502 const DescriptorProto* descriptor() {
503 PERFETTO_CHECK(!ctx_.empty());
504 return ctx_.top().descriptor;
505 }
506
descriptor_name()507 const std::string& descriptor_name() { return descriptor()->name(); }
508
msg()509 protozero::Message* msg() {
510 PERFETTO_CHECK(!ctx_.empty());
511 return ctx_.top().message;
512 }
513
514 std::stack<ParserDelegateContext> ctx_;
515 ErrorReporter* reporter_;
516 std::map<std::string, const DescriptorProto*> name_to_descriptor_;
517 std::map<std::string, const EnumDescriptorProto*> name_to_enum_;
518 };
519
Parse(const std::string & input,ParserDelegate * delegate)520 void Parse(const std::string& input, ParserDelegate* delegate) {
521 ParseState state = kWaitingForKey;
522 size_t column = 0;
523 size_t row = 1;
524 size_t depth = 0;
525 bool saw_colon_for_this_key = false;
526 bool saw_semicolon_for_this_value = true;
527 bool comment_till_eol = false;
528 Token key{};
529 Token value{};
530
531 for (size_t i = 0; i < input.size(); i++, column++) {
532 bool last_character = i + 1 == input.size();
533 char c = input.at(i);
534 if (c == '\n') {
535 column = 0;
536 row++;
537 if (comment_till_eol) {
538 comment_till_eol = false;
539 continue;
540 }
541 }
542 if (comment_till_eol)
543 continue;
544
545 switch (state) {
546 case kWaitingForKey:
547 if (isspace(c))
548 continue;
549 if (c == '#') {
550 comment_till_eol = true;
551 continue;
552 }
553 if (c == '}') {
554 if (depth == 0) {
555 delegate->AddError(row, column, "Unmatched closing brace", {});
556 return;
557 }
558 saw_semicolon_for_this_value = false;
559 depth--;
560 delegate->EndNestedMessage();
561 continue;
562 }
563 if (!saw_semicolon_for_this_value && c == ';') {
564 saw_semicolon_for_this_value = true;
565 continue;
566 }
567 if (IsIdentifierStart(c)) {
568 saw_colon_for_this_key = false;
569 state = kReadingKey;
570 key.offset = i;
571 key.row = row;
572 key.column = column;
573 continue;
574 }
575 break;
576
577 case kReadingKey:
578 if (IsIdentifierBody(c))
579 continue;
580 key.txt = base::StringView(input.data() + key.offset, i - key.offset);
581 state = kWaitingForValue;
582 if (c == '#')
583 comment_till_eol = true;
584 continue;
585
586 case kWaitingForValue:
587 if (isspace(c))
588 continue;
589 if (c == '#') {
590 comment_till_eol = true;
591 continue;
592 }
593 value.offset = i;
594 value.row = row;
595 value.column = column;
596
597 if (c == ':' && !saw_colon_for_this_key) {
598 saw_colon_for_this_key = true;
599 continue;
600 }
601 if (c == '"') {
602 state = kReadingStringValue;
603 continue;
604 }
605 if (c == '-' || IsDigit(c) || c == '.') {
606 state = kReadingNumericValue;
607 continue;
608 }
609 if (IsIdentifierStart(c)) {
610 state = kReadingIdentifierValue;
611 continue;
612 }
613 if (c == '{') {
614 state = kWaitingForKey;
615 depth++;
616 value.txt = base::StringView(input.data() + value.offset, 1);
617 delegate->BeginNestedMessage(key, value);
618 continue;
619 }
620 break;
621
622 case kReadingNumericValue:
623 if (isspace(c) || c == ';' || last_character) {
624 bool keep_last = last_character && !(isspace(c) || c == ';');
625 size_t size = i - value.offset + (keep_last ? 1 : 0);
626 value.txt = base::StringView(input.data() + value.offset, size);
627 saw_semicolon_for_this_value = c == ';';
628 state = kWaitingForKey;
629 delegate->NumericField(key, value);
630 continue;
631 }
632 if (IsDigit(c) || c == '.')
633 continue;
634 break;
635
636 case kReadingStringValue:
637 if (c == '\\') {
638 state = kReadingStringEscape;
639 } else if (c == '"') {
640 size_t size = i - value.offset - 1;
641 value.column++;
642 value.txt = base::StringView(input.data() + value.offset + 1, size);
643 saw_semicolon_for_this_value = false;
644 state = kWaitingForKey;
645 delegate->StringField(key, value);
646 }
647 continue;
648
649 case kReadingStringEscape:
650 state = kReadingStringValue;
651 continue;
652
653 case kReadingIdentifierValue:
654 if (isspace(c) || c == ';' || c == '#' || last_character) {
655 bool keep_last =
656 last_character && !(isspace(c) || c == ';' || c == '#');
657 size_t size = i - value.offset + (keep_last ? 1 : 0);
658 value.txt = base::StringView(input.data() + value.offset, size);
659 comment_till_eol = c == '#';
660 saw_semicolon_for_this_value = c == ';';
661 state = kWaitingForKey;
662 delegate->IdentifierField(key, value);
663 continue;
664 }
665 if (IsIdentifierBody(c)) {
666 continue;
667 }
668 break;
669 }
670 PERFETTO_FATAL("Unexpected char %c", c);
671 } // for
672 if (depth > 0)
673 delegate->AddError(row, column, "Nested message not closed", {});
674 if (state != kWaitingForKey)
675 delegate->AddError(row, column, "Unexpected end of input", {});
676 delegate->Eof();
677 }
678
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)679 void AddNestedDescriptors(
680 const std::string& prefix,
681 const DescriptorProto* descriptor,
682 std::map<std::string, const DescriptorProto*>* name_to_descriptor,
683 std::map<std::string, const EnumDescriptorProto*>* name_to_enum) {
684 for (const EnumDescriptorProto& enum_descriptor : descriptor->enum_type()) {
685 const std::string name = prefix + "." + enum_descriptor.name();
686 (*name_to_enum)[name] = &enum_descriptor;
687 }
688 for (const DescriptorProto& nested_descriptor : descriptor->nested_type()) {
689 const std::string name = prefix + "." + nested_descriptor.name();
690 (*name_to_descriptor)[name] = &nested_descriptor;
691 AddNestedDescriptors(name, &nested_descriptor, name_to_descriptor,
692 name_to_enum);
693 }
694 }
695
696 } // namespace
697
698 ErrorReporter::ErrorReporter() = default;
699 ErrorReporter::~ErrorReporter() = default;
700
PbtxtToPb(const std::string & input,ErrorReporter * reporter)701 std::vector<uint8_t> PbtxtToPb(const std::string& input,
702 ErrorReporter* reporter) {
703 std::map<std::string, const DescriptorProto*> name_to_descriptor;
704 std::map<std::string, const EnumDescriptorProto*> name_to_enum;
705 FileDescriptorSet file_descriptor_set;
706
707 {
708 file_descriptor_set.ParseFromArray(
709 kPerfettoConfigDescriptor.data(),
710 static_cast<int>(kPerfettoConfigDescriptor.size()));
711 for (const auto& file_descriptor : file_descriptor_set.file()) {
712 for (const auto& enum_descriptor : file_descriptor.enum_type()) {
713 const std::string name =
714 "." + file_descriptor.package() + "." + enum_descriptor.name();
715 name_to_enum[name] = &enum_descriptor;
716 }
717 for (const auto& descriptor : file_descriptor.message_type()) {
718 const std::string name =
719 "." + file_descriptor.package() + "." + descriptor.name();
720 name_to_descriptor[name] = &descriptor;
721 AddNestedDescriptors(name, &descriptor, &name_to_descriptor,
722 &name_to_enum);
723 }
724 }
725 }
726
727 const DescriptorProto* descriptor = name_to_descriptor[kConfigProtoName];
728 PERFETTO_CHECK(descriptor);
729
730 protozero::HeapBuffered<protozero::Message> message;
731 ParserDelegate delegate(descriptor, message.get(), reporter,
732 std::move(name_to_descriptor),
733 std::move(name_to_enum));
734 Parse(input, &delegate);
735 return message.SerializeAsArray();
736 }
737
738 } // namespace perfetto
739