• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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