xref: /aosp_15_r20/external/perfetto/src/trace_config_utils/txt_to_pb.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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/trace_config_utils/txt_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 
36 #include "protos/perfetto/common/descriptor.gen.h"
37 #include "src/trace_config_utils/config.descriptor.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::__anona3f603400111::Token136   size_t size() const { return txt.size(); }
ToStdStringperfetto::__anona3f603400111::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 ErrorReporter {
147  public:
ErrorReporter(std::string file_name,const char * config)148   ErrorReporter(std::string file_name, const char* config)
149       : file_name_(std::move(file_name)), config_(config) {}
150 
AddError(size_t row,size_t column,size_t length,const std::string & message)151   void AddError(size_t row,
152                 size_t column,
153                 size_t length,
154                 const std::string& message) {
155     // Protobuf uses 1-indexed for row and column. Although in some rare cases
156     // they can be 0 if it can't locate the error.
157     row = row > 0 ? row - 1 : 0;
158     column = column > 0 ? column - 1 : 0;
159     parsed_successfully_ = false;
160     std::string line = ExtractLine(row).ToStdString();
161     if (!line.empty() && line[line.length() - 1] == '\n') {
162       line.erase(line.length() - 1);
163     }
164 
165     std::string guide(column + length, ' ');
166     for (size_t i = column; i < column + length; i++) {
167       guide[i] = i == column ? '^' : '~';
168     }
169     error_ += file_name_ + ":" + std::to_string(row+1) + ":" +
170               std::to_string(column + 1) + " error: " + message + "\n";
171     error_ += line + "\n";
172     error_ += guide + "\n";
173   }
174 
success() const175   bool success() const { return parsed_successfully_; }
error() const176   const std::string& error() const { return error_; }
177 
178  private:
ExtractLine(size_t line)179   base::StringView ExtractLine(size_t line) {
180     const char* start = config_;
181     const char* end = config_;
182 
183     for (size_t i = 0; i < line + 1; i++) {
184       start = end;
185       char c;
186       while ((c = *end++) && c != '\n')
187         ;
188     }
189     return base::StringView(start, static_cast<size_t>(end - start));
190   }
191 
192   bool parsed_successfully_ = true;
193   std::string file_name_;
194   std::string error_;
195   const char* config_;
196 };
197 
198 class ParserDelegate {
199  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)200   ParserDelegate(
201       const DescriptorProto* descriptor,
202       protozero::Message* message,
203       ErrorReporter* reporter,
204       std::map<std::string, const DescriptorProto*> name_to_descriptor,
205       std::map<std::string, const EnumDescriptorProto*> name_to_enum)
206       : reporter_(reporter),
207         name_to_descriptor_(std::move(name_to_descriptor)),
208         name_to_enum_(std::move(name_to_enum)) {
209     ctx_.push(ParserDelegateContext{descriptor, message, {}});
210   }
211 
NumericField(const Token & key,const Token & value)212   void NumericField(const Token& key, const Token& value) {
213     const FieldDescriptorProto* field =
214         FindFieldByName(key, value,
215                         {
216                             FieldDescriptorProto::TYPE_UINT64,
217                             FieldDescriptorProto::TYPE_UINT32,
218                             FieldDescriptorProto::TYPE_INT64,
219                             FieldDescriptorProto::TYPE_SINT64,
220                             FieldDescriptorProto::TYPE_INT32,
221                             FieldDescriptorProto::TYPE_SINT32,
222                             FieldDescriptorProto::TYPE_FIXED64,
223                             FieldDescriptorProto::TYPE_SFIXED64,
224                             FieldDescriptorProto::TYPE_FIXED32,
225                             FieldDescriptorProto::TYPE_SFIXED32,
226                             FieldDescriptorProto::TYPE_DOUBLE,
227                             FieldDescriptorProto::TYPE_FLOAT,
228                         });
229     if (!field)
230       return;
231     const auto& field_type = field->type();
232     switch (field_type) {
233       case FieldDescriptorProto::TYPE_UINT64:
234         return VarIntField<uint64_t>(field, value);
235       case FieldDescriptorProto::TYPE_UINT32:
236         return VarIntField<uint32_t>(field, value);
237       case FieldDescriptorProto::TYPE_INT64:
238       case FieldDescriptorProto::TYPE_SINT64:
239         return VarIntField<int64_t>(field, value);
240       case FieldDescriptorProto::TYPE_INT32:
241       case FieldDescriptorProto::TYPE_SINT32:
242         return VarIntField<int32_t>(field, value);
243 
244       case FieldDescriptorProto::TYPE_FIXED64:
245       case FieldDescriptorProto::TYPE_SFIXED64:
246         return FixedField<int64_t>(field, value);
247 
248       case FieldDescriptorProto::TYPE_FIXED32:
249       case FieldDescriptorProto::TYPE_SFIXED32:
250         return FixedField<int32_t>(field, value);
251 
252       case FieldDescriptorProto::TYPE_DOUBLE:
253         return FixedFloatField<double>(field, value);
254       case FieldDescriptorProto::TYPE_FLOAT:
255         return FixedFloatField<float>(field, value);
256 
257       case FieldDescriptorProto::TYPE_BOOL:
258       case FieldDescriptorProto::TYPE_STRING:
259       case FieldDescriptorProto::TYPE_BYTES:
260       case FieldDescriptorProto::TYPE_GROUP:
261       case FieldDescriptorProto::TYPE_MESSAGE:
262       case FieldDescriptorProto::TYPE_ENUM:
263         PERFETTO_FATAL("Invalid type");
264     }
265   }
266 
StringField(const Token & key,const Token & value)267   void StringField(const Token& key, const Token& value) {
268     const FieldDescriptorProto* field =
269         FindFieldByName(key, value,
270                         {
271                             FieldDescriptorProto::TYPE_STRING,
272                             FieldDescriptorProto::TYPE_BYTES,
273                         });
274     if (!field)
275       return;
276     uint32_t field_id = static_cast<uint32_t>(field->number());
277     const auto& field_type = field->type();
278     PERFETTO_CHECK(field_type == FieldDescriptorProto::TYPE_STRING ||
279                    field_type == FieldDescriptorProto::TYPE_BYTES);
280 
281     std::unique_ptr<char, base::FreeDeleter> s(
282         static_cast<char*>(malloc(value.size())));
283     size_t j = 0;
284     const char* const txt = value.txt.data();
285     for (size_t i = 0; i < value.size(); i++) {
286       char c = txt[i];
287       if (c == '\\') {
288         if (i + 1 >= value.size()) {
289           // This should be caught by the lexer.
290           PERFETTO_FATAL("Escape at end of string.");
291           return;
292         }
293         char next = txt[++i];
294         switch (next) {
295           case '\\':
296           case '\'':
297           case '"':
298           case '?':
299             s.get()[j++] = next;
300             break;
301           case 'a':
302             s.get()[j++] = '\a';
303             break;
304           case 'b':
305             s.get()[j++] = '\b';
306             break;
307           case 'f':
308             s.get()[j++] = '\f';
309             break;
310           case 'n':
311             s.get()[j++] = '\n';
312             break;
313           case 'r':
314             s.get()[j++] = '\r';
315             break;
316           case 't':
317             s.get()[j++] = '\t';
318             break;
319           case 'v':
320             s.get()[j++] = '\v';
321             break;
322           case '0':
323           case '1':
324           case '2':
325           case '3':
326           case '4':
327           case '5':
328           case '6':
329           case '7':
330           case '8':
331           case '9': {
332             // Cases 8 and 9 are not really required and are only added for the
333             // sake of error reporting.
334             bool oct_err = false;
335             if (i + 2 >= value.size() || !IsOct(txt[i + 1]) ||
336                 !IsOct(txt[i + 2])) {
337               oct_err = true;
338             } else {
339               char buf[4]{next, txt[++i], txt[++i], '\0'};
340               auto octval = base::CStringToUInt32(buf, 8);
341               if (!octval.has_value() || *octval > 0xff) {
342                 oct_err = true;
343               } else {
344                 s.get()[j++] = static_cast<char>(static_cast<uint8_t>(*octval));
345               }
346             }
347             if (oct_err) {
348               AddError(value,
349                        "Malformed string escape in $k in proto $n on '$v'. "
350                        "\\NNN escapes must be exactly three octal digits <= "
351                        "\\377 (0xff).",
352                        std::map<std::string, std::string>{
353                            {"$k", key.ToStdString()},
354                            {"$n", descriptor_name()},
355                            {"$v", value.ToStdString()},
356                        });
357             }
358             break;
359           }
360           default:
361             AddError(value,
362                      "Unknown string escape in $k in "
363                      "proto $n: '$v'",
364                      std::map<std::string, std::string>{
365                          {"$k", key.ToStdString()},
366                          {"$n", descriptor_name()},
367                          {"$v", value.ToStdString()},
368                      });
369             return;
370         }
371       } else {
372         s.get()[j++] = c;
373       }
374     }
375     msg()->AppendBytes(field_id, s.get(), j);
376   }
377 
IdentifierField(const Token & key,const Token & value)378   void IdentifierField(const Token& key, const Token& value) {
379     const FieldDescriptorProto* field =
380         FindFieldByName(key, value,
381                         {
382                             FieldDescriptorProto::TYPE_BOOL,
383                             FieldDescriptorProto::TYPE_ENUM,
384                         });
385     if (!field)
386       return;
387     uint32_t field_id = static_cast<uint32_t>(field->number());
388     const auto& field_type = field->type();
389     if (field_type == FieldDescriptorProto::TYPE_BOOL) {
390       if (value.txt != "true" && value.txt != "false") {
391         AddError(value,
392                  "Expected 'true' or 'false' for boolean field $k in "
393                  "proto $n instead saw '$v'",
394                  std::map<std::string, std::string>{
395                      {"$k", key.ToStdString()},
396                      {"$n", descriptor_name()},
397                      {"$v", value.ToStdString()},
398                  });
399         return;
400       }
401       msg()->AppendTinyVarInt(field_id, value.txt == "true" ? 1 : 0);
402     } else if (field_type == FieldDescriptorProto::TYPE_ENUM) {
403       const std::string& type_name = field->type_name();
404       const EnumDescriptorProto* enum_descriptor = name_to_enum_[type_name];
405       PERFETTO_CHECK(enum_descriptor);
406       bool found_value = false;
407       int32_t enum_value_number = 0;
408       for (const EnumValueDescriptorProto& enum_value :
409            enum_descriptor->value()) {
410         if (value.ToStdString() != enum_value.name())
411           continue;
412         found_value = true;
413         enum_value_number = enum_value.number();
414         break;
415       }
416       if (!found_value) {
417         AddError(value,
418                  "Unexpected value '$v' for enum field $k in "
419                  "proto $n",
420                  std::map<std::string, std::string>{
421                      {"$v", value.ToStdString()},
422                      {"$k", key.ToStdString()},
423                      {"$n", descriptor_name()},
424                  });
425         return;
426       }
427       msg()->AppendVarInt<int32_t>(field_id, enum_value_number);
428     }
429   }
430 
BeginNestedMessage(const Token & key,const Token & value)431   bool BeginNestedMessage(const Token& key, const Token& value) {
432     const FieldDescriptorProto* field =
433         FindFieldByName(key, value,
434                         {
435                             FieldDescriptorProto::TYPE_MESSAGE,
436                         });
437     if (!field) {
438       // FindFieldByName adds an error.
439       return false;
440     }
441     uint32_t field_id = static_cast<uint32_t>(field->number());
442     const std::string& type_name = field->type_name();
443     const DescriptorProto* nested_descriptor = name_to_descriptor_[type_name];
444     PERFETTO_CHECK(nested_descriptor);
445     auto* nested_msg = msg()->BeginNestedMessage<protozero::Message>(field_id);
446     ctx_.push(ParserDelegateContext{nested_descriptor, nested_msg, {}});
447     return true;
448   }
449 
EndNestedMessage()450   void EndNestedMessage() {
451     msg()->Finalize();
452     ctx_.pop();
453   }
454 
Eof()455   void Eof() {}
456 
AddError(size_t row,size_t column,const char * fmt,const std::map<std::string,std::string> & args)457   void AddError(size_t row,
458                 size_t column,
459                 const char* fmt,
460                 const std::map<std::string, std::string>& args) {
461     reporter_->AddError(row, column, 0, Format(fmt, args));
462   }
463 
AddError(const Token & token,const char * fmt,const std::map<std::string,std::string> & args)464   void AddError(const Token& token,
465                 const char* fmt,
466                 const std::map<std::string, std::string>& args) {
467     reporter_->AddError(token.row, token.column, token.size(),
468                         Format(fmt, args));
469   }
470 
471  private:
472   template <typename T>
VarIntField(const FieldDescriptorProto * field,Token t)473   void VarIntField(const FieldDescriptorProto* field, Token t) {
474     uint32_t field_id = static_cast<uint32_t>(field->number());
475     uint64_t n = 0;
476     PERFETTO_CHECK(ParseInteger(t.txt, &n));
477     if (field->type() == FieldDescriptorProto::TYPE_SINT64 ||
478         field->type() == FieldDescriptorProto::TYPE_SINT32) {
479       msg()->AppendSignedVarInt<T>(field_id, static_cast<T>(n));
480     } else {
481       msg()->AppendVarInt<T>(field_id, static_cast<T>(n));
482     }
483   }
484 
485   template <typename T>
FixedField(const FieldDescriptorProto * field,const Token & t)486   void FixedField(const FieldDescriptorProto* field, const Token& t) {
487     uint32_t field_id = static_cast<uint32_t>(field->number());
488     uint64_t n = 0;
489     PERFETTO_CHECK(ParseInteger(t.txt, &n));
490     msg()->AppendFixed<T>(field_id, static_cast<T>(n));
491   }
492 
493   template <typename T>
FixedFloatField(const FieldDescriptorProto * field,const Token & t)494   void FixedFloatField(const FieldDescriptorProto* field, const Token& t) {
495     uint32_t field_id = static_cast<uint32_t>(field->number());
496     std::optional<double> opt_n = base::StringToDouble(t.ToStdString());
497     msg()->AppendFixed<T>(field_id, static_cast<T>(opt_n.value_or(0l)));
498   }
499 
500   template <typename T>
ParseInteger(base::StringView s,T * number_ptr)501   bool ParseInteger(base::StringView s, T* number_ptr) {
502     uint64_t n = 0;
503     PERFETTO_CHECK(sscanf(s.ToStdString().c_str(), "%" PRIu64, &n) == 1);
504     PERFETTO_CHECK(n <= std::numeric_limits<T>::max());
505     *number_ptr = static_cast<T>(n);
506     return true;
507   }
508 
FindFieldByName(const Token & key,const Token & value,std::set<FieldDescriptorProto::Type> valid_field_types)509   const FieldDescriptorProto* FindFieldByName(
510       const Token& key,
511       const Token& value,
512       std::set<FieldDescriptorProto::Type> valid_field_types) {
513     const std::string field_name = key.ToStdString();
514     const FieldDescriptorProto* field_descriptor = nullptr;
515     for (const auto& f : descriptor()->field()) {
516       if (f.name() == field_name) {
517         field_descriptor = &f;
518         break;
519       }
520     }
521 
522     if (!field_descriptor) {
523       AddError(key, "No field named \"$n\" in proto $p",
524                {
525                    {"$n", field_name},
526                    {"$p", descriptor_name()},
527                });
528       return nullptr;
529     }
530 
531     bool is_repeated =
532         field_descriptor->label() == FieldDescriptorProto::LABEL_REPEATED;
533     auto it_and_inserted = ctx_.top().seen_fields.emplace(field_name);
534     if (!it_and_inserted.second && !is_repeated) {
535       AddError(key, "Saw non-repeating field '$f' more than once",
536                {
537                    {"$f", field_name},
538                });
539     }
540 
541     if (!valid_field_types.count(field_descriptor->type())) {
542       AddError(value,
543                "Expected value of type $t for field $k in proto $n "
544                "instead saw '$v'",
545                {
546                    {"$t", FieldToTypeName(field_descriptor)},
547                    {"$k", field_name},
548                    {"$n", descriptor_name()},
549                    {"$v", value.ToStdString()},
550                });
551       return nullptr;
552     }
553 
554     return field_descriptor;
555   }
556 
descriptor()557   const DescriptorProto* descriptor() {
558     PERFETTO_CHECK(!ctx_.empty());
559     return ctx_.top().descriptor;
560   }
561 
descriptor_name()562   const std::string& descriptor_name() { return descriptor()->name(); }
563 
msg()564   protozero::Message* msg() {
565     PERFETTO_CHECK(!ctx_.empty());
566     return ctx_.top().message;
567   }
568 
569   std::stack<ParserDelegateContext> ctx_;
570   ErrorReporter* reporter_;
571   std::map<std::string, const DescriptorProto*> name_to_descriptor_;
572   std::map<std::string, const EnumDescriptorProto*> name_to_enum_;
573 };
574 
Parse(const std::string & input,ParserDelegate * delegate)575 void Parse(const std::string& input, ParserDelegate* delegate) {
576   ParseState state = kWaitingForKey;
577   size_t column = 0;
578   size_t row = 1;
579   size_t depth = 0;
580   bool saw_colon_for_this_key = false;
581   bool saw_semicolon_for_this_value = true;
582   bool comment_till_eol = false;
583   Token key{};
584   Token value{};
585 
586   for (size_t i = 0; i < input.size(); i++, column++) {
587     bool last_character = i + 1 == input.size();
588     char c = input.at(i);
589     if (c == '\n') {
590       column = 0;
591       row++;
592       if (comment_till_eol) {
593         comment_till_eol = false;
594         continue;
595       }
596     }
597     if (comment_till_eol)
598       continue;
599 
600     switch (state) {
601       case kWaitingForKey:
602         if (isspace(c))
603           continue;
604         if (c == '#') {
605           comment_till_eol = true;
606           continue;
607         }
608         if (c == '}') {
609           if (depth == 0) {
610             delegate->AddError(row, column, "Unmatched closing brace", {});
611             return;
612           }
613           saw_semicolon_for_this_value = false;
614           depth--;
615           delegate->EndNestedMessage();
616           continue;
617         }
618         if (!saw_semicolon_for_this_value && c == ';') {
619           saw_semicolon_for_this_value = true;
620           continue;
621         }
622         if (IsIdentifierStart(c)) {
623           saw_colon_for_this_key = false;
624           state = kReadingKey;
625           key.offset = i;
626           key.row = row;
627           key.column = column;
628           continue;
629         }
630         break;
631 
632       case kReadingKey:
633         if (IsIdentifierBody(c))
634           continue;
635         key.txt = base::StringView(input.data() + key.offset, i - key.offset);
636         state = kWaitingForValue;
637         if (c == '#')
638           comment_till_eol = true;
639         continue;
640 
641       case kWaitingForValue:
642         if (isspace(c))
643           continue;
644         if (c == '#') {
645           comment_till_eol = true;
646           continue;
647         }
648         value.offset = i;
649         value.row = row;
650         value.column = column;
651 
652         if (c == ':' && !saw_colon_for_this_key) {
653           saw_colon_for_this_key = true;
654           continue;
655         }
656         if (c == '"') {
657           state = kReadingStringValue;
658           continue;
659         }
660         if (c == '-' || IsDigit(c) || c == '.') {
661           state = kReadingNumericValue;
662           continue;
663         }
664         if (IsIdentifierStart(c)) {
665           state = kReadingIdentifierValue;
666           continue;
667         }
668         if (c == '{') {
669           state = kWaitingForKey;
670           depth++;
671           value.txt = base::StringView(input.data() + value.offset, 1);
672           if (!delegate->BeginNestedMessage(key, value)) {
673             return;
674           }
675           continue;
676         }
677         break;
678 
679       case kReadingNumericValue:
680         if (isspace(c) || c == ';' || last_character) {
681           bool keep_last = last_character && !(isspace(c) || c == ';');
682           size_t size = i - value.offset + (keep_last ? 1 : 0);
683           value.txt = base::StringView(input.data() + value.offset, size);
684           saw_semicolon_for_this_value = c == ';';
685           state = kWaitingForKey;
686           delegate->NumericField(key, value);
687           continue;
688         }
689         if (IsDigit(c) || c == '.')
690           continue;
691         break;
692 
693       case kReadingStringValue:
694         if (c == '\\') {
695           state = kReadingStringEscape;
696         } else if (c == '"') {
697           size_t size = i - value.offset - 1;
698           value.column++;
699           value.txt = base::StringView(input.data() + value.offset + 1, size);
700           saw_semicolon_for_this_value = false;
701           state = kWaitingForKey;
702           delegate->StringField(key, value);
703         }
704         continue;
705 
706       case kReadingStringEscape:
707         state = kReadingStringValue;
708         continue;
709 
710       case kReadingIdentifierValue:
711         if (isspace(c) || c == ';' || c == '#' || last_character) {
712           bool keep_last =
713               last_character && !(isspace(c) || c == ';' || c == '#');
714           size_t size = i - value.offset + (keep_last ? 1 : 0);
715           value.txt = base::StringView(input.data() + value.offset, size);
716           comment_till_eol = c == '#';
717           saw_semicolon_for_this_value = c == ';';
718           state = kWaitingForKey;
719           delegate->IdentifierField(key, value);
720           continue;
721         }
722         if (IsIdentifierBody(c)) {
723           continue;
724         }
725         break;
726     }
727     delegate->AddError(row, column, "Unexpected character '$c'",
728                        std::map<std::string, std::string>{
729                            {"$c", std::string(1, c)},
730                        });
731     return;
732   }  // for
733   if (depth > 0)
734     delegate->AddError(row, column, "Nested message not closed", {});
735   if (state != kWaitingForKey)
736     delegate->AddError(row, column, "Unexpected end of input", {});
737   delegate->Eof();
738 }
739 
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)740 void AddNestedDescriptors(
741     const std::string& prefix,
742     const DescriptorProto* descriptor,
743     std::map<std::string, const DescriptorProto*>* name_to_descriptor,
744     std::map<std::string, const EnumDescriptorProto*>* name_to_enum) {
745   for (const EnumDescriptorProto& enum_descriptor : descriptor->enum_type()) {
746     const std::string name = prefix + "." + enum_descriptor.name();
747     (*name_to_enum)[name] = &enum_descriptor;
748   }
749   for (const DescriptorProto& nested_descriptor : descriptor->nested_type()) {
750     const std::string name = prefix + "." + nested_descriptor.name();
751     (*name_to_descriptor)[name] = &nested_descriptor;
752     AddNestedDescriptors(name, &nested_descriptor, name_to_descriptor,
753                          name_to_enum);
754   }
755 }
756 
757 }  // namespace
758 
TraceConfigTxtToPb(const std::string & input,const std::string & file_name)759 perfetto::base::StatusOr<std::vector<uint8_t>> TraceConfigTxtToPb(
760     const std::string& input,
761     const std::string& file_name) {
762   std::map<std::string, const DescriptorProto*> name_to_descriptor;
763   std::map<std::string, const EnumDescriptorProto*> name_to_enum;
764   FileDescriptorSet file_descriptor_set;
765 
766   {
767     file_descriptor_set.ParseFromArray(
768         kConfigDescriptor.data(), static_cast<int>(kConfigDescriptor.size()));
769     for (const auto& file_descriptor : file_descriptor_set.file()) {
770       for (const auto& enum_descriptor : file_descriptor.enum_type()) {
771         const std::string name =
772             "." + file_descriptor.package() + "." + enum_descriptor.name();
773         name_to_enum[name] = &enum_descriptor;
774       }
775       for (const auto& descriptor : file_descriptor.message_type()) {
776         const std::string name =
777             "." + file_descriptor.package() + "." + descriptor.name();
778         name_to_descriptor[name] = &descriptor;
779         AddNestedDescriptors(name, &descriptor, &name_to_descriptor,
780                              &name_to_enum);
781       }
782     }
783   }
784 
785   const DescriptorProto* descriptor = name_to_descriptor[kConfigProtoName];
786   PERFETTO_CHECK(descriptor);
787 
788   protozero::HeapBuffered<protozero::Message> message;
789   ErrorReporter reporter(file_name, input.c_str());
790   ParserDelegate delegate(descriptor, message.get(), &reporter,
791                           std::move(name_to_descriptor),
792                           std::move(name_to_enum));
793   Parse(input, &delegate);
794   if (!reporter.success())
795     return base::ErrStatus("%s", reporter.error().c_str());
796   return message.SerializeAsArray();
797 }
798 
799 }  // namespace perfetto
800