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