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