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