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