• 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 
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