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