xref: /aosp_15_r20/external/perfetto/src/trace_config_utils/txt_to_pb_unittest.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 <memory>
20 #include <string>
21 
22 #include "test/gtest_and_gmock.h"
23 
24 #include "protos/perfetto/config/data_source_config.gen.h"
25 #include "protos/perfetto/config/ftrace/ftrace_config.gen.h"
26 #include "protos/perfetto/config/test_config.gen.h"
27 #include "protos/perfetto/config/trace_config.gen.h"
28 
29 namespace perfetto {
30 namespace {
31 
32 using ::testing::Contains;
33 using ::testing::ElementsAre;
34 using ::testing::HasSubstr;
35 using ::testing::StrictMock;
36 using TraceConfig = ::perfetto::protos::gen::TraceConfig;
37 
ToProto(const std::string & input)38 TraceConfig ToProto(const std::string& input) {
39   base::StatusOr<std::vector<uint8_t>> output = TraceConfigTxtToPb(input);
40   EXPECT_TRUE(output.ok());
41   EXPECT_FALSE(output->empty());
42   TraceConfig config;
43   config.ParseFromArray(output->data(), output->size());
44   return config;
45 }
46 
TEST(TxtToPbTest,OneField)47 TEST(TxtToPbTest, OneField) {
48   TraceConfig config = ToProto(R"(
49     duration_ms: 1234
50   )");
51   EXPECT_EQ(config.duration_ms(), 1234u);
52 }
53 
TEST(TxtToPbTest,TwoFields)54 TEST(TxtToPbTest, TwoFields) {
55   TraceConfig config = ToProto(R"(
56     duration_ms: 1234
57     file_write_period_ms: 5678
58   )");
59   EXPECT_EQ(config.duration_ms(), 1234u);
60   EXPECT_EQ(config.file_write_period_ms(), 5678u);
61 }
62 
TEST(TxtToPbTest,Enum)63 TEST(TxtToPbTest, Enum) {
64   TraceConfig config = ToProto(R"(
65 compression_type: COMPRESSION_TYPE_DEFLATE
66 )");
67   EXPECT_EQ(config.compression_type(), 1);
68 }
69 
TEST(TxtToPbTest,LastCharacters)70 TEST(TxtToPbTest, LastCharacters) {
71   EXPECT_EQ(ToProto(R"(
72 duration_ms: 123;)")
73                 .duration_ms(),
74             123u);
75   EXPECT_EQ(ToProto(R"(
76   duration_ms: 123
77 )")
78                 .duration_ms(),
79             123u);
80   EXPECT_EQ(ToProto(R"(
81   duration_ms: 123#)")
82                 .duration_ms(),
83             123u);
84   EXPECT_EQ(ToProto(R"(
85   duration_ms: 123 )")
86                 .duration_ms(),
87             123u);
88 
89   EXPECT_EQ(ToProto(R"(
90 compression_type: COMPRESSION_TYPE_DEFLATE;)")
91                 .compression_type(),
92             1);
93   EXPECT_EQ(ToProto(R"(
94 compression_type: COMPRESSION_TYPE_DEFLATE
95 )")
96                 .compression_type(),
97             1);
98   EXPECT_EQ(ToProto(R"(
99   compression_type: COMPRESSION_TYPE_DEFLATE#)")
100                 .compression_type(),
101             1);
102   EXPECT_EQ(ToProto(R"(
103   compression_type: COMPRESSION_TYPE_DEFLATE )")
104                 .compression_type(),
105             1);
106 }
107 
TEST(TxtToPbTest,Semicolons)108 TEST(TxtToPbTest, Semicolons) {
109   TraceConfig config = ToProto(R"(
110     duration_ms: 1234;
111     file_write_period_ms: 5678;
112   )");
113   EXPECT_EQ(config.duration_ms(), 1234u);
114   EXPECT_EQ(config.file_write_period_ms(), 5678u);
115 }
116 
TEST(TxtToPbTest,NestedMessage)117 TEST(TxtToPbTest, NestedMessage) {
118   TraceConfig config = ToProto(R"(
119     buffers: {
120       size_kb: 123
121     }
122   )");
123   ASSERT_EQ(config.buffers().size(), 1u);
124   EXPECT_EQ(config.buffers()[0].size_kb(), 123u);
125 }
126 
TEST(TxtToPbTest,SplitNested)127 TEST(TxtToPbTest, SplitNested) {
128   TraceConfig config = ToProto(R"(
129     buffers: {
130       size_kb: 1
131     }
132     duration_ms: 1000;
133     buffers: {
134       size_kb: 2
135     }
136   )");
137   ASSERT_EQ(config.buffers().size(), 2u);
138   EXPECT_EQ(config.buffers()[0].size_kb(), 1u);
139   EXPECT_EQ(config.buffers()[1].size_kb(), 2u);
140   EXPECT_EQ(config.duration_ms(), 1000u);
141 }
142 
TEST(TxtToPbTest,MultipleNestedMessage)143 TEST(TxtToPbTest, MultipleNestedMessage) {
144   TraceConfig config = ToProto(R"(
145     buffers: {
146       size_kb: 1
147     }
148     buffers: {
149       size_kb: 2
150     }
151   )");
152   ASSERT_EQ(config.buffers().size(), 2u);
153   EXPECT_EQ(config.buffers()[0].size_kb(), 1u);
154   EXPECT_EQ(config.buffers()[1].size_kb(), 2u);
155 }
156 
TEST(TxtToPbTest,NestedMessageCrossFile)157 TEST(TxtToPbTest, NestedMessageCrossFile) {
158   TraceConfig config = ToProto(R"(
159 data_sources {
160   config {
161     ftrace_config {
162       drain_period_ms: 42
163     }
164   }
165 }
166   )");
167   protos::gen::FtraceConfig ftrace_config;
168   ASSERT_TRUE(ftrace_config.ParseFromString(
169       config.data_sources()[0].config().ftrace_config_raw()));
170   ASSERT_EQ(ftrace_config.drain_period_ms(), 42u);
171 }
172 
TEST(TxtToPbTest,Booleans)173 TEST(TxtToPbTest, Booleans) {
174   TraceConfig config = ToProto(R"(
175     write_into_file: false; deferred_start: true;
176   )");
177   EXPECT_EQ(config.write_into_file(), false);
178   EXPECT_EQ(config.deferred_start(), true);
179 }
180 
TEST(TxtToPbTest,Comments)181 TEST(TxtToPbTest, Comments) {
182   TraceConfig config = ToProto(R"(
183     write_into_file: false # deferred_start: true;
184     buffers# 1
185     # 2
186     :# 3
187     # 4
188     {# 5
189     # 6
190     fill_policy# 7
191     # 8
192     :# 9
193     # 10
194     RING_BUFFER# 11
195     # 12
196     ;# 13
197     # 14
198     } # 15
199     # 16
200   )");
201   EXPECT_EQ(config.write_into_file(), false);
202   EXPECT_EQ(config.deferred_start(), false);
203 }
204 
TEST(TxtToPbTest,Enums)205 TEST(TxtToPbTest, Enums) {
206   TraceConfig config = ToProto(R"(
207     buffers: {
208       fill_policy: RING_BUFFER
209     }
210   )");
211   const auto kRingBuffer = TraceConfig::BufferConfig::RING_BUFFER;
212   EXPECT_EQ(config.buffers()[0].fill_policy(), kRingBuffer);
213 }
214 
TEST(TxtToPbTest,AllFieldTypes)215 TEST(TxtToPbTest, AllFieldTypes) {
216   TraceConfig config = ToProto(R"(
217 data_sources {
218   config {
219     for_testing {
220       dummy_fields {
221         field_uint32: 1;
222         field_uint64: 2;
223         field_int32: 3;
224         field_int64: 4;
225         field_fixed64: 5;
226         field_sfixed64: 6;
227         field_fixed32: 7;
228         field_sfixed32: 8;
229         field_double: 9.9;
230         field_float: 10.10;
231         field_sint64: 11;
232         field_sint32: 12;
233         field_string: "13";
234         field_bytes: "14";
235       }
236     }
237   }
238 }
239   )");
240   const auto& fields =
241       config.data_sources()[0].config().for_testing().dummy_fields();
242   ASSERT_EQ(fields.field_uint32(), 1u);
243   ASSERT_EQ(fields.field_uint64(), 2u);
244   ASSERT_EQ(fields.field_int32(), 3);
245   ASSERT_EQ(fields.field_int64(), 4);
246   ASSERT_EQ(fields.field_fixed64(), 5u);
247   ASSERT_EQ(fields.field_sfixed64(), 6);
248   ASSERT_EQ(fields.field_fixed32(), 7u);
249   ASSERT_EQ(fields.field_sfixed32(), 8);
250   ASSERT_DOUBLE_EQ(fields.field_double(), 9.9);
251   ASSERT_FLOAT_EQ(fields.field_float(), 10.10f);
252   ASSERT_EQ(fields.field_sint64(), 11);
253   ASSERT_EQ(fields.field_sint32(), 12);
254   ASSERT_EQ(fields.field_string(), "13");
255   ASSERT_EQ(fields.field_bytes(), "14");
256 }
257 
TEST(TxtToPbTest,LeadingDots)258 TEST(TxtToPbTest, LeadingDots) {
259   TraceConfig config = ToProto(R"(
260 data_sources {
261   config {
262     for_testing {
263       dummy_fields {
264         field_double:  .1;
265         field_float:   .2;
266       }
267     }
268   }
269 }
270   )");
271   const auto& fields =
272       config.data_sources()[0].config().for_testing().dummy_fields();
273   ASSERT_DOUBLE_EQ(fields.field_double(), .1);
274   ASSERT_FLOAT_EQ(fields.field_float(), .2f);
275 }
276 
TEST(TxtToPbTest,NegativeNumbers)277 TEST(TxtToPbTest, NegativeNumbers) {
278   TraceConfig config = ToProto(R"(
279 data_sources {
280   config {
281     for_testing {
282       dummy_fields {
283         field_int32: -1;
284         field_int64: -2;
285         field_fixed64: -3;
286         field_sfixed64: -4;
287         field_fixed32: -5;
288         field_sfixed32: -6;
289         field_double: -7.7;
290         field_float: -8.8;
291         field_sint64: -9;
292         field_sint32: -10;
293       }
294     }
295   }
296 }
297   )");
298   const auto& fields =
299       config.data_sources()[0].config().for_testing().dummy_fields();
300   ASSERT_EQ(fields.field_int32(), -1);
301   ASSERT_EQ(fields.field_int64(), -2);
302   ASSERT_EQ(fields.field_fixed64(), static_cast<uint64_t>(-3));
303   ASSERT_EQ(fields.field_sfixed64(), -4);
304   ASSERT_EQ(fields.field_fixed32(), static_cast<uint32_t>(-5));
305   ASSERT_EQ(fields.field_sfixed32(), -6);
306   ASSERT_DOUBLE_EQ(fields.field_double(), -7.7);
307   ASSERT_FLOAT_EQ(fields.field_float(), -8.8f);
308   ASSERT_EQ(fields.field_sint64(), -9);
309   ASSERT_EQ(fields.field_sint32(), -10);
310 }
311 
TEST(TxtToPbTest,EofEndsNumeric)312 TEST(TxtToPbTest, EofEndsNumeric) {
313   TraceConfig config = ToProto(R"(duration_ms: 1234)");
314   EXPECT_EQ(config.duration_ms(), 1234u);
315 }
316 
TEST(TxtToPbTest,EofEndsIdentifier)317 TEST(TxtToPbTest, EofEndsIdentifier) {
318   TraceConfig config = ToProto(R"(enable_extra_guardrails: true)");
319   EXPECT_EQ(config.enable_extra_guardrails(), true);
320 }
321 
TEST(TxtToPbTest,ExampleConfig)322 TEST(TxtToPbTest, ExampleConfig) {
323   TraceConfig config = ToProto(R"(
324 buffers {
325   size_kb: 100024
326   fill_policy: RING_BUFFER
327 }
328 
329 data_sources {
330   config {
331     name: "linux.ftrace"
332     target_buffer: 0
333     ftrace_config {
334       buffer_size_kb: 512 # 4 (page size) * 128
335       drain_period_ms: 200
336       ftrace_events: "binder_lock"
337       ftrace_events: "binder_locked"
338       atrace_categories: "gfx"
339     }
340   }
341 }
342 
343 data_sources {
344   config {
345     name: "linux.process_stats"
346     target_buffer: 0
347   }
348 }
349 
350 data_sources {
351   config {
352     name: "linux.inode_file_map"
353     target_buffer: 0
354     inode_file_config {
355       scan_delay_ms: 1000
356       scan_interval_ms: 1000
357       scan_batch_size: 500
358       mount_point_mapping: {
359         mountpoint: "/data"
360         scan_roots: "/data/app"
361       }
362     }
363   }
364 }
365 
366 producers {
367   producer_name: "perfetto.traced_probes"
368   shm_size_kb: 4096
369   page_size_kb: 4
370 }
371 
372 duration_ms: 10000
373 )");
374   EXPECT_EQ(config.duration_ms(), 10000u);
375   EXPECT_EQ(config.buffers()[0].size_kb(), 100024u);
376   EXPECT_EQ(config.data_sources()[0].config().name(), "linux.ftrace");
377   EXPECT_EQ(config.data_sources()[0].config().target_buffer(), 0u);
378   EXPECT_EQ(config.producers()[0].producer_name(), "perfetto.traced_probes");
379 }
380 
TEST(TxtToPbTest,Strings)381 TEST(TxtToPbTest, Strings) {
382   TraceConfig config = ToProto(R"(
383 data_sources {
384   config {
385     ftrace_config {
386       ftrace_events: "binder_lock"
387       ftrace_events: "foo/bar"
388       ftrace_events: "foo\\bar"
389       ftrace_events: "newline\nnewline"
390       ftrace_events: "\"quoted\""
391       ftrace_events: "\a\b\f\n\r\t\v\\\'\"\?"
392       ftrace_events: "\0127_\03422.\177"
393     }
394   }
395 }
396 )");
397   protos::gen::FtraceConfig ftrace_config;
398   ASSERT_TRUE(ftrace_config.ParseFromString(
399       config.data_sources()[0].config().ftrace_config_raw()));
400   const auto& events = ftrace_config.ftrace_events();
401   EXPECT_THAT(events, Contains("binder_lock"));
402   EXPECT_THAT(events, Contains("foo/bar"));
403   EXPECT_THAT(events, Contains("foo\\bar"));
404   EXPECT_THAT(events, Contains("newline\nnewline"));
405   EXPECT_THAT(events, Contains("\"quoted\""));
406   EXPECT_THAT(events, Contains("\a\b\f\n\r\t\v\\\'\"\?"));
407   EXPECT_THAT(events, Contains("\0127_\03422.\177"));
408 }
409 
TEST(TxtToPbTest,UnknownField)410 TEST(TxtToPbTest, UnknownField) {
411   auto res = TraceConfigTxtToPb(R"(
412     not_a_label: false
413   )");
414   EXPECT_FALSE(res.ok());
415   EXPECT_THAT(res.status().message(),
416               HasSubstr("No field named \"not_a_label\" in proto TraceConfig"));
417 }
418 
TEST(TxtToPbTest,UnknownNestedField)419 TEST(TxtToPbTest, UnknownNestedField) {
420   auto res = TraceConfigTxtToPb(R"(
421 data_sources {
422   config {
423     not_a_field_name {
424     }
425   }
426 }
427   )");
428   EXPECT_FALSE(res.ok());
429   EXPECT_THAT(
430       res.status().message(),
431       HasSubstr(
432           "No field named \"not_a_field_name\" in proto DataSourceConfig"));
433 }
434 
TEST(TxtToPbTest,BadBoolean)435 TEST(TxtToPbTest, BadBoolean) {
436   auto res = TraceConfigTxtToPb(R"(
437     write_into_file: foo;
438   )");
439   EXPECT_FALSE(res.ok());
440   EXPECT_THAT(res.status().message(),
441               HasSubstr("Expected 'true' or 'false' for boolean field "
442                         "write_into_file in proto TraceConfig instead "
443                         "saw 'foo'"));
444 }
445 
TEST(TxtToPbTest,MissingBoolean)446 TEST(TxtToPbTest, MissingBoolean) {
447   auto res = TraceConfigTxtToPb(R"(
448     write_into_file:
449   )");
450   EXPECT_FALSE(res.ok());
451   EXPECT_THAT(res.status().message(), HasSubstr("Unexpected end of input"));
452 }
453 
TEST(TxtToPbTest,RootProtoMustNotEndWithBrace)454 TEST(TxtToPbTest, RootProtoMustNotEndWithBrace) {
455   auto res = TraceConfigTxtToPb("  }");
456   EXPECT_FALSE(res.ok());
457   EXPECT_THAT(res.status().message(), HasSubstr("Unmatched closing brace"));
458 }
459 
TEST(TxtToPbTest,SawNonRepeatedFieldTwice)460 TEST(TxtToPbTest, SawNonRepeatedFieldTwice) {
461   auto res = TraceConfigTxtToPb(R"(
462     write_into_file: true;
463     write_into_file: true;
464   )");
465   EXPECT_FALSE(res.ok());
466   EXPECT_THAT(
467       res.status().message(),
468       HasSubstr("Saw non-repeating field 'write_into_file' more than once"));
469 }
470 
TEST(TxtToPbTest,WrongTypeBoolean)471 TEST(TxtToPbTest, WrongTypeBoolean) {
472   auto res = TraceConfigTxtToPb(R"(
473     duration_ms: true;
474   )");
475   EXPECT_FALSE(res.ok());
476   EXPECT_THAT(
477       res.status().message(),
478       HasSubstr("Expected value of type uint32 for field duration_ms in "
479                 "proto TraceConfig instead saw 'true'"));
480 }
481 
TEST(TxtToPbTest,WrongTypeNumber)482 TEST(TxtToPbTest, WrongTypeNumber) {
483   auto res = TraceConfigTxtToPb(R"(
484     buffers: 100;
485   )");
486   EXPECT_FALSE(res.ok());
487   EXPECT_THAT(res.status().message(),
488               HasSubstr("Expected value of type message for field buffers in "
489                         "proto TraceConfig instead saw '100'"));
490 }
491 
TEST(TxtToPbTest,NestedMessageDidNotTerminate)492 TEST(TxtToPbTest, NestedMessageDidNotTerminate) {
493   auto res = TraceConfigTxtToPb(R"(
494     buffers: {
495   )");
496   EXPECT_FALSE(res.ok());
497   EXPECT_THAT(res.status().message(), HasSubstr("Nested message not closed"));
498 }
499 
TEST(TxtToPbTest,BadEscape)500 TEST(TxtToPbTest, BadEscape) {
501   auto res = TraceConfigTxtToPb(R"(
502   data_sources {
503     config {
504       ftrace_config {
505         ftrace_events: "\p"
506       }
507     }
508   })");
509   EXPECT_FALSE(res.ok());
510   EXPECT_THAT(res.status().message(),
511               HasSubstr("Unknown string escape in ftrace_events in "
512                         "proto FtraceConfig: '\\p'"));
513 }
514 
TEST(TxtToPbTest,BadEnumValue)515 TEST(TxtToPbTest, BadEnumValue) {
516   auto res = TraceConfigTxtToPb("compression_type: FOO");
517   EXPECT_FALSE(res.ok());
518   EXPECT_THAT(res.status().message(),
519               HasSubstr("Unexpected value 'FOO' for enum field "
520                         "compression_type in proto TraceConfig"));
521 }
522 
TEST(TxtToPbTest,UnexpectedBracket)523 TEST(TxtToPbTest, UnexpectedBracket) {
524   auto res = TraceConfigTxtToPb("{");
525   EXPECT_FALSE(res.ok());
526   EXPECT_THAT(res.status().message(), HasSubstr("Unexpected character '{'"));
527 }
528 
TEST(TxtToPbTest,UnknownNested)529 TEST(TxtToPbTest, UnknownNested) {
530   auto res = TraceConfigTxtToPb("foo {}; bar: 42");
531   EXPECT_FALSE(res.ok());
532   EXPECT_THAT(res.status().message(), HasSubstr("No field named \"foo\" in "
533                                                 "proto TraceConfig"));
534 }
535 
536 // TODO(hjd): Add these tests.
537 // TEST(TxtToPbTest, WrongTypeString)
538 // TEST(TxtToPbTest, OverflowOnIntegers)
539 // TEST(TxtToPbTest, NegativeNumbersForUnsignedInt)
540 // TEST(TxtToPbTest, UnterminatedString) {
541 // TEST(TxtToPbTest, NumberIsEof)
542 // TEST(TxtToPbTest, OneOf)
543 
544 }  // namespace
545 }  // namespace perfetto
546