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