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