1 /*
2  * Copyright (C) 2017 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/traced/probes/ftrace/proto_translation_table.h"
18 
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
21 #include "perfetto/trace/ftrace/ftrace_event.pbzero.h"
22 #include "perfetto/trace/ftrace/generic.pbzero.h"
23 #include "src/base/test/gtest_test_suite.h"
24 #include "src/traced/probes/ftrace/event_info.h"
25 #include "src/traced/probes/ftrace/ftrace_procfs.h"
26 
27 using testing::_;
28 using testing::Values;
29 using testing::ValuesIn;
30 using testing::TestWithParam;
31 using testing::Return;
32 using testing::AnyNumber;
33 using testing::IsNull;
34 using testing::Contains;
35 using testing::Eq;
36 using testing::Pointee;
37 
38 namespace perfetto {
39 namespace {
40 using protozero::proto_utils::ProtoSchemaType;
41 
42 class MockFtraceProcfs : public FtraceProcfs {
43  public:
MockFtraceProcfs()44   MockFtraceProcfs() : FtraceProcfs("/root/") {}
45 
46   MOCK_CONST_METHOD0(ReadPageHeaderFormat, std::string());
47   MOCK_CONST_METHOD2(ReadEventFormat,
48                      std::string(const std::string& group,
49                                  const std::string& name));
50 };
51 
52 class AllTranslationTableTest : public TestWithParam<const char*> {
53  public:
SetUp()54   void SetUp() override {
55     std::string path =
56         "src/traced/probes/ftrace/test/data/" + std::string(GetParam()) + "/";
57     FtraceProcfs ftrace_procfs(path);
58     table_ = ProtoTranslationTable::Create(&ftrace_procfs, GetStaticEventInfo(),
59                                            GetStaticCommonFieldsInfo());
60     PERFETTO_CHECK(table_);
61   }
62 
63   std::unique_ptr<ProtoTranslationTable> table_;
64 };
65 
66 class TranslationTableCreationTest : public TestWithParam<uint16_t> {};
67 
68 const char* kDevices[] = {
69     "android_seed_N2F62_3.10.49", "android_hammerhead_MRA59G_3.4.0",
70 };
71 
TEST_P(AllTranslationTableTest,Create)72 TEST_P(AllTranslationTableTest, Create) {
73   EXPECT_TRUE(table_);
74   EXPECT_TRUE(table_->GetEvent(GroupAndName("ftrace", "print")));
75   EXPECT_TRUE(table_->GetEvent(GroupAndName("sched", "sched_switch")));
76   EXPECT_TRUE(table_->GetEvent(GroupAndName("sched", "sched_wakeup")));
77   EXPECT_TRUE(table_->GetEvent(GroupAndName("ext4", "ext4_da_write_begin")));
78   for (const Event& event : table_->events()) {
79     if (!event.ftrace_event_id)
80       continue;
81     EXPECT_TRUE(event.name);
82     EXPECT_TRUE(event.group);
83     EXPECT_TRUE(event.proto_field_id);
84     for (const Field& field : event.fields) {
85       EXPECT_TRUE(field.proto_field_id);
86       EXPECT_TRUE(field.ftrace_type);
87       EXPECT_TRUE(static_cast<int>(field.proto_field_type));
88     }
89   }
90   ASSERT_EQ(table_->common_fields().size(), 1u);
91   const Field& pid_field = table_->common_fields().at(0);
92   EXPECT_EQ(std::string(pid_field.ftrace_name), "common_pid");
93   EXPECT_EQ(pid_field.proto_field_id, 2u);
94 
95   {
96     auto event = table_->GetEvent(GroupAndName("ftrace", "print"));
97     EXPECT_TRUE(event);
98     EXPECT_EQ(std::string(event->name), "print");
99     EXPECT_EQ(std::string(event->group), "ftrace");
100     EXPECT_EQ(event->fields.at(1).proto_field_type, ProtoSchemaType::kString);
101     EXPECT_EQ(event->fields.at(1).ftrace_type, kFtraceCString);
102     EXPECT_EQ(event->fields.at(1).strategy, kCStringToString);
103   }
104 }
105 
106 INSTANTIATE_TEST_SUITE_P(ByDevice, AllTranslationTableTest, ValuesIn(kDevices));
107 
TEST(TranslationTableTest,Seed)108 TEST(TranslationTableTest, Seed) {
109   std::string path =
110       "src/traced/probes/ftrace/test/data/android_seed_N2F62_3.10.49/";
111   FtraceProcfs ftrace_procfs(path);
112   auto table = ProtoTranslationTable::Create(
113       &ftrace_procfs, GetStaticEventInfo(), GetStaticCommonFieldsInfo());
114   PERFETTO_CHECK(table);
115   const Field& pid_field = table->common_fields().at(0);
116   EXPECT_EQ(std::string(pid_field.ftrace_name), "common_pid");
117   EXPECT_EQ(pid_field.proto_field_id, 2u);
118   EXPECT_EQ(pid_field.ftrace_offset, 4u);
119   EXPECT_EQ(pid_field.ftrace_size, 4u);
120 
121   {
122     auto event = table->GetEvent(GroupAndName("sched", "sched_switch"));
123     EXPECT_EQ(std::string(event->name), "sched_switch");
124     EXPECT_EQ(std::string(event->group), "sched");
125     EXPECT_EQ(event->ftrace_event_id, 68ul);
126     EXPECT_EQ(event->fields.at(0).ftrace_offset, 8u);
127     EXPECT_EQ(event->fields.at(0).ftrace_size, 16u);
128   }
129 
130   {
131     auto event = table->GetEvent(GroupAndName("sched", "sched_wakeup"));
132     EXPECT_EQ(std::string(event->name), "sched_wakeup");
133     EXPECT_EQ(std::string(event->group), "sched");
134     EXPECT_EQ(event->ftrace_event_id, 70ul);
135     EXPECT_EQ(event->fields.at(0).ftrace_offset, 8u);
136     EXPECT_EQ(event->fields.at(0).ftrace_size, 16u);
137   }
138 
139   {
140     auto event = table->GetEvent(GroupAndName("ext4", "ext4_da_write_begin"));
141     EXPECT_EQ(std::string(event->name), "ext4_da_write_begin");
142     EXPECT_EQ(std::string(event->group), "ext4");
143     EXPECT_EQ(event->ftrace_event_id, 303ul);
144     EXPECT_EQ(event->fields.at(0).ftrace_offset, 8u);
145     EXPECT_EQ(event->fields.at(0).ftrace_size, 4u);
146   }
147 }
148 
TEST_P(TranslationTableCreationTest,Create)149 TEST_P(TranslationTableCreationTest, Create) {
150   MockFtraceProcfs ftrace;
151   std::vector<Field> common_fields;
152   std::vector<Event> events;
153 
154   ON_CALL(ftrace, ReadPageHeaderFormat())
155       .WillByDefault(Return(
156           R"(	field: u64 timestamp;	offset:0;	size:8;	signed:0;
157 	field: local_t commit;	offset:8;	size:)" +
158           std::to_string(GetParam()) + R"(;	signed:1;
159 	field: int overwrite;	offset:8;	size:1;	signed:1;
160 	field: char data;	offset:16;	size:4080;	signed:0;)"));
161   ON_CALL(ftrace, ReadEventFormat(_, _)).WillByDefault(Return(""));
162   ON_CALL(ftrace, ReadEventFormat("group", "foo"))
163       .WillByDefault(Return(R"(name: foo
164 ID: 42
165 format:
166 	field:unsigned short common_type;	offset:0;	size:2;	signed:0;
167 	field:int common_pid;	offset:4;	size:4;	signed:1;
168 
169 	field:char field_a[16];	offset:8;	size:16;	signed:0;
170 	field:int field_b;	offset:24;	size:4;	signed:1;
171 	field:int field_d;	offset:28;	size:4;	signed:1;
172 	field:u32 field_e;	offset:32;	size:4;	signed:0;
173 
174 print fmt: "some format")"));
175   ;
176 
177   EXPECT_CALL(ftrace, ReadPageHeaderFormat()).Times(AnyNumber());
178   EXPECT_CALL(ftrace, ReadEventFormat(_, _)).Times(AnyNumber());
179 
180   {
181     events.emplace_back(Event{});
182     Event* event = &events.back();
183     event->name = "foo";
184     event->group = "group";
185     event->proto_field_id = 21;
186 
187     {
188       // We should get this field.
189       event->fields.emplace_back(Field{});
190       Field* field = &event->fields.back();
191       field->proto_field_id = 501;
192       field->proto_field_type = ProtoSchemaType::kString;
193       field->ftrace_name = "field_a";
194     }
195 
196     {
197       // We shouldn't get this field: don't know how to read int -> string.
198       event->fields.emplace_back(Field{});
199       Field* field = &event->fields.back();
200       field->proto_field_id = 502;
201       field->proto_field_type = ProtoSchemaType::kString;
202       field->ftrace_name = "field_b";
203     }
204 
205     {
206       // We shouldn't get this field: no matching field in the format file.
207       event->fields.emplace_back(Field{});
208       Field* field = &event->fields.back();
209       field->proto_field_id = 503;
210       field->proto_field_type = ProtoSchemaType::kString;
211       field->ftrace_name = "field_c";
212     }
213 
214     {
215       // We should get this field.
216       event->fields.emplace_back(Field{});
217       Field* field = &event->fields.back();
218       field->proto_field_id = 504;
219       field->proto_field_type = ProtoSchemaType::kUint64;
220       field->ftrace_name = "field_e";
221     }
222   }
223 
224   {
225     events.emplace_back(Event{});
226     Event* event = &events.back();
227     event->name = "bar";
228     event->group = "group";
229     event->proto_field_id = 22;
230   }
231 
232   auto table = ProtoTranslationTable::Create(&ftrace, std::move(events),
233                                              std::move(common_fields));
234   PERFETTO_CHECK(table);
235   EXPECT_EQ(table->largest_id(), 42ul);
236   EXPECT_EQ(table->EventToFtraceId(GroupAndName("group", "foo")), 42ul);
237   EXPECT_EQ(table->EventToFtraceId(GroupAndName("group", "bar")), 0ul);
238   EXPECT_FALSE(table->GetEventById(43ul));
239   ASSERT_TRUE(table->GetEventById(42ul));
240   EXPECT_EQ(table->ftrace_page_header_spec().timestamp.size, 8);
241   EXPECT_EQ(table->ftrace_page_header_spec().size.size, GetParam());
242   EXPECT_EQ(table->ftrace_page_header_spec().overwrite.size, 1);
243   auto event = table->GetEventById(42);
244   EXPECT_EQ(event->ftrace_event_id, 42ul);
245   EXPECT_EQ(event->proto_field_id, 21ul);
246   EXPECT_EQ(event->size, 36u);
247   EXPECT_EQ(std::string(event->name), "foo");
248   EXPECT_EQ(std::string(event->group), "group");
249 
250   ASSERT_EQ(event->fields.size(), 2ul);
251   auto field_a = event->fields.at(0);
252   EXPECT_EQ(field_a.proto_field_id, 501ul);
253   EXPECT_EQ(field_a.strategy, kFixedCStringToString);
254 
255   auto field_e = event->fields.at(1);
256   EXPECT_EQ(field_e.proto_field_id, 504ul);
257   EXPECT_EQ(field_e.strategy, kUint32ToUint64);
258 }
259 
260 INSTANTIATE_TEST_SUITE_P(BySize, TranslationTableCreationTest, Values(4, 8));
261 
TEST(TranslationTableTest,InferFtraceType)262 TEST(TranslationTableTest, InferFtraceType) {
263   FtraceFieldType type;
264 
265   ASSERT_TRUE(InferFtraceType("char foo[16]", 16, false, &type));
266   EXPECT_EQ(type, kFtraceFixedCString);
267 
268   ASSERT_TRUE(InferFtraceType("char[] foo", 8, false, &type));
269   EXPECT_EQ(type, kFtraceStringPtr);
270 
271   ASSERT_TRUE(InferFtraceType("char * foo", 8, false, &type));
272   EXPECT_EQ(type, kFtraceStringPtr);
273 
274   ASSERT_TRUE(InferFtraceType("char foo[64]", 64, false, &type));
275   EXPECT_EQ(type, kFtraceFixedCString);
276 
277   ASSERT_TRUE(InferFtraceType("u32 foo", 4, false, &type));
278   EXPECT_EQ(type, kFtraceUint32);
279 
280   ASSERT_TRUE(InferFtraceType("i_ino foo", 4, false, &type));
281   ASSERT_EQ(type, kFtraceInode32);
282 
283   ASSERT_TRUE(InferFtraceType("i_ino foo", 8, false, &type));
284   ASSERT_EQ(type, kFtraceInode64);
285 
286   ASSERT_TRUE(InferFtraceType("ino_t foo", 4, false, &type));
287   ASSERT_EQ(type, kFtraceInode32);
288 
289   ASSERT_TRUE(InferFtraceType("ino_t foo", 8, false, &type));
290   ASSERT_EQ(type, kFtraceInode64);
291 
292   ASSERT_TRUE(InferFtraceType("dev_t foo", 4, false, &type));
293   ASSERT_EQ(type, kFtraceDevId32);
294 
295   ASSERT_TRUE(InferFtraceType("dev_t foo", 8, false, &type));
296   ASSERT_EQ(type, kFtraceDevId64);
297 
298   ASSERT_TRUE(InferFtraceType("pid_t foo", 4, false, &type));
299   ASSERT_EQ(type, kFtracePid32);
300 
301   ASSERT_TRUE(InferFtraceType("int common_pid", 4, false, &type));
302   ASSERT_EQ(type, kFtraceCommonPid32);
303 
304   ASSERT_TRUE(InferFtraceType("char foo", 1, true, &type));
305   ASSERT_EQ(type, kFtraceInt8);
306 
307   ASSERT_TRUE(InferFtraceType("__data_loc char[] foo", 4, false, &type));
308   ASSERT_EQ(type, kFtraceDataLoc);
309   ASSERT_FALSE(InferFtraceType("__data_loc char[] foo", 8, false, &type));
310 
311   EXPECT_FALSE(InferFtraceType("foo", 64, false, &type));
312 }
313 
TEST(TranslationTableTest,Getters)314 TEST(TranslationTableTest, Getters) {
315   MockFtraceProcfs ftrace;
316   std::vector<Field> common_fields;
317   std::vector<Event> events;
318 
319   {
320     Event event;
321     event.name = "foo";
322     event.group = "group_one";
323     event.ftrace_event_id = 1;
324     events.push_back(event);
325   }
326 
327   {
328     Event event;
329     event.name = "bar";
330     event.group = "group_one";
331     event.ftrace_event_id = 2;
332     events.push_back(event);
333   }
334 
335   {
336     Event event;
337     event.name = "baz";
338     event.group = "group_two";
339     event.ftrace_event_id = 100;
340     events.push_back(event);
341   }
342 
343   ProtoTranslationTable table(
344       &ftrace, events, std::move(common_fields),
345       ProtoTranslationTable::DefaultPageHeaderSpecForTesting());
346   EXPECT_EQ(table.largest_id(), 100ul);
347   EXPECT_EQ(table.EventToFtraceId(GroupAndName("group_one", "foo")), 1ul);
348   EXPECT_EQ(table.EventToFtraceId(GroupAndName("group_two", "baz")), 100ul);
349   EXPECT_EQ(table.EventToFtraceId(GroupAndName("group_one", "no_such_event")),
350             0ul);
351   EXPECT_EQ(table.GetEventById(1)->name, "foo");
352   EXPECT_EQ(table.GetEventById(3), nullptr);
353   EXPECT_EQ(table.GetEventById(200), nullptr);
354   EXPECT_EQ(table.GetEventById(0), nullptr);
355   EXPECT_EQ(table.GetEvent(GroupAndName("group_one", "foo"))->ftrace_event_id,
356             1u);
357   EXPECT_THAT(*table.GetEventsByGroup("group_one"),
358               Contains(testing::Field(&Event::name, "foo")));
359   EXPECT_THAT(*table.GetEventsByGroup("group_one"),
360               Contains(testing::Field(&Event::name, "bar")));
361   EXPECT_THAT(*table.GetEventsByGroup("group_two"),
362               Contains(testing::Field(&Event::name, "baz")));
363   EXPECT_THAT(table.GetEventsByGroup("group_three"), IsNull());
364 }
365 
TEST(TranslationTableTest,Generic)366 TEST(TranslationTableTest, Generic) {
367   MockFtraceProcfs ftrace;
368   std::vector<Field> common_fields;
369   std::vector<Event> events;
370 
371   ON_CALL(ftrace, ReadPageHeaderFormat())
372       .WillByDefault(Return(
373           R"(	field: u64 timestamp;	offset:0;	size:8;	signed:0;
374 	field: local_t commit;	offset:8;	size:4;	signed:1;
375 	field: int overwrite;	offset:8;	size:1;	signed:1;
376 	field: char data;	offset:16;	size:4080;	signed:0;)"));
377   ON_CALL(ftrace, ReadEventFormat(_, _)).WillByDefault(Return(""));
378   ON_CALL(ftrace, ReadEventFormat("group", "foo"))
379       .WillByDefault(Return(R"(name: foo
380 ID: 42
381 format:
382 	field:unsigned short common_type;	offset:0;	size:2;	signed:0;
383 	field:int common_pid;	offset:4;	size:4;	signed:1;
384 
385 	field:char field_a[16];	offset:8;	size:16;	signed:0;
386 	field:bool field_b;	offset:24;	size:1;	signed:0;
387 	field:int field_c;	offset:25;	size:4;	signed:1;
388 	field:u32 field_d;	offset:33;	size:4;	signed:0;
389 
390 print fmt: "some format")"));
391 
392   EXPECT_CALL(ftrace, ReadPageHeaderFormat()).Times(AnyNumber());
393   EXPECT_CALL(ftrace, ReadEventFormat(_, _)).Times(AnyNumber());
394 
395   auto table = ProtoTranslationTable::Create(&ftrace, std::move(events),
396                                              std::move(common_fields));
397   PERFETTO_CHECK(table);
398   EXPECT_EQ(table->largest_id(), 0ul);
399   GroupAndName group_and_name("group", "foo");
400   const Event* e = table->GetOrCreateEvent(group_and_name);
401   EXPECT_EQ(table->largest_id(), 42ul);
402   EXPECT_EQ(table->EventToFtraceId(group_and_name), 42ul);
403 
404   // Check getters
405   EXPECT_EQ(table->GetEventById(42)->proto_field_id,
406             protos::pbzero::FtraceEvent::kGenericFieldNumber);
407   EXPECT_EQ(table->GetEvent(group_and_name)->proto_field_id,
408             protos::pbzero::FtraceEvent::kGenericFieldNumber);
409   EXPECT_EQ(table->GetEventsByGroup("group")->front()->name,
410             group_and_name.name());
411 
412   EXPECT_EQ(e->fields.size(), 4ul);
413   const std::vector<Field>& fields = e->fields;
414   // Check string field
415   const auto& str_field = fields[0];
416   EXPECT_STREQ(str_field.ftrace_name, "field_a");
417   EXPECT_EQ(str_field.proto_field_id,
418             protos::pbzero::GenericFtraceEvent::Field::kStrValueFieldNumber);
419   EXPECT_EQ(str_field.proto_field_type, ProtoSchemaType::kString);
420   EXPECT_EQ(str_field.ftrace_type, kFtraceFixedCString);
421   EXPECT_EQ(str_field.ftrace_size, 16);
422   EXPECT_EQ(str_field.ftrace_offset, 8);
423 
424   // Check bool field
425   const auto& bool_field = fields[1];
426   EXPECT_STREQ(bool_field.ftrace_name, "field_b");
427   EXPECT_EQ(bool_field.proto_field_id,
428             protos::pbzero::GenericFtraceEvent::Field::kUintValueFieldNumber);
429   EXPECT_EQ(bool_field.proto_field_type, ProtoSchemaType::kUint64);
430   EXPECT_EQ(bool_field.ftrace_type, kFtraceBool);
431   EXPECT_EQ(bool_field.ftrace_size, 1);
432   EXPECT_EQ(bool_field.ftrace_offset, 24);
433 
434   // Check int field
435   const auto& int_field = fields[2];
436   EXPECT_STREQ(int_field.ftrace_name, "field_c");
437   EXPECT_EQ(int_field.proto_field_id,
438             protos::pbzero::GenericFtraceEvent::Field::kIntValueFieldNumber);
439   EXPECT_EQ(int_field.proto_field_type, ProtoSchemaType::kInt64);
440   EXPECT_EQ(int_field.ftrace_type, kFtraceInt32);
441   EXPECT_EQ(int_field.ftrace_size, 4);
442   EXPECT_EQ(int_field.ftrace_offset, 25);
443 
444   // Check uint field
445   const auto& uint_field = fields[3];
446   EXPECT_STREQ(uint_field.ftrace_name, "field_d");
447   EXPECT_EQ(uint_field.proto_field_id,
448             protos::pbzero::GenericFtraceEvent::Field::kUintValueFieldNumber);
449   EXPECT_EQ(uint_field.proto_field_type, ProtoSchemaType::kUint64);
450   EXPECT_EQ(uint_field.ftrace_type, kFtraceUint32);
451   EXPECT_EQ(uint_field.ftrace_size, 4);
452   EXPECT_EQ(uint_field.ftrace_offset, 33);
453 }
454 
TEST(EventFilterTest,EnableEventsFrom)455 TEST(EventFilterTest, EnableEventsFrom) {
456   EventFilter filter;
457   filter.AddEnabledEvent(1);
458   filter.AddEnabledEvent(17);
459 
460   EventFilter or_filter;
461   or_filter.AddEnabledEvent(4);
462   or_filter.AddEnabledEvent(17);
463 
464   filter.EnableEventsFrom(or_filter);
465   EXPECT_TRUE(filter.IsEventEnabled(4));
466   EXPECT_TRUE(filter.IsEventEnabled(17));
467   EXPECT_TRUE(filter.IsEventEnabled(1));
468   EXPECT_FALSE(filter.IsEventEnabled(2));
469 
470   EventFilter empty_filter;
471   filter.EnableEventsFrom(empty_filter);
472   EXPECT_TRUE(filter.IsEventEnabled(4));
473   EXPECT_TRUE(filter.IsEventEnabled(17));
474   EXPECT_TRUE(filter.IsEventEnabled(1));
475 
476   empty_filter.EnableEventsFrom(filter);
477   EXPECT_TRUE(empty_filter.IsEventEnabled(4));
478   EXPECT_TRUE(empty_filter.IsEventEnabled(17));
479   EXPECT_TRUE(empty_filter.IsEventEnabled(1));
480 }
481 
482 }  // namespace
483 }  // namespace perfetto
484