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 "src/base/test/utils.h"
20 #include "src/traced/probes/ftrace/compact_sched.h"
21 #include "src/traced/probes/ftrace/event_info.h"
22 #include "src/traced/probes/ftrace/ftrace_procfs.h"
23 #include "test/gtest_and_gmock.h"
24
25 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
26 #include "protos/perfetto/trace/ftrace/generic.pbzero.h"
27
28 using testing::_;
29 using testing::AllOf;
30 using testing::AnyNumber;
31 using testing::Contains;
32 using testing::ElementsAre;
33 using testing::Eq;
34 using testing::IsNull;
35 using testing::NiceMock;
36 using testing::Pointee;
37 using testing::Return;
38 using testing::StrEq;
39 using testing::TestWithParam;
40 using testing::Values;
41 using testing::ValuesIn;
42
43 namespace perfetto {
44 namespace {
45 using protozero::proto_utils::ProtoSchemaType;
46
47 class MockFtraceProcfs : public FtraceProcfs {
48 public:
MockFtraceProcfs()49 MockFtraceProcfs() : FtraceProcfs("/root/") {}
50
51 MOCK_METHOD(std::string, ReadPageHeaderFormat, (), (const, override));
52 MOCK_METHOD(std::string,
53 ReadEventFormat,
54 (const std::string& group, const std::string& name),
55 (const, override));
56 };
57
58 class AllTranslationTableTest : public TestWithParam<const char*> {
59 public:
SetUp()60 void SetUp() override {
61 std::string path = base::GetTestDataPath(
62 "src/traced/probes/ftrace/test/data/" + std::string(GetParam()) + "/");
63 FtraceProcfs ftrace_procfs(path);
64 table_ = ProtoTranslationTable::Create(&ftrace_procfs, GetStaticEventInfo(),
65 GetStaticCommonFieldsInfo());
66 PERFETTO_CHECK(table_);
67 }
68
69 std::unique_ptr<ProtoTranslationTable> table_;
70 };
71
72 class TranslationTableCreationTest : public TestWithParam<uint16_t> {};
73
74 const char* kDevices[] = {
75 "android_seed_N2F62_3.10.49",
76 "android_hammerhead_MRA59G_3.4.0",
77 };
78
TEST_P(AllTranslationTableTest,Create)79 TEST_P(AllTranslationTableTest, Create) {
80 EXPECT_TRUE(table_);
81 EXPECT_TRUE(table_->GetEvent(GroupAndName("ftrace", "print")));
82 EXPECT_TRUE(table_->GetEvent(GroupAndName("sched", "sched_switch")));
83 EXPECT_TRUE(table_->GetEvent(GroupAndName("sched", "sched_wakeup")));
84 EXPECT_TRUE(table_->GetEvent(GroupAndName("ext4", "ext4_da_write_begin")));
85 for (const Event& event : table_->events()) {
86 if (!event.ftrace_event_id)
87 continue;
88 EXPECT_TRUE(event.name);
89 EXPECT_TRUE(event.group);
90 EXPECT_TRUE(event.proto_field_id);
91 for (const Field& field : event.fields) {
92 EXPECT_TRUE(field.proto_field_id);
93 EXPECT_TRUE(field.ftrace_type);
94 EXPECT_TRUE(static_cast<int>(field.proto_field_type));
95 }
96 }
97 ASSERT_LT(0u, table_->common_fields().size());
98 const Field& pid_field = table_->common_fields().at(0);
99 EXPECT_EQ(std::string(pid_field.ftrace_name), "common_pid");
100 EXPECT_EQ(pid_field.proto_field_id, 2u);
101
102 {
103 auto event = table_->GetEvent(GroupAndName("ftrace", "print"));
104 EXPECT_TRUE(event);
105 EXPECT_EQ(std::string(event->name), "print");
106 EXPECT_EQ(std::string(event->group), "ftrace");
107
108 EXPECT_EQ(event->fields.at(0).proto_field_type, ProtoSchemaType::kString);
109 EXPECT_EQ(event->fields.at(0).ftrace_type, kFtraceCString);
110 EXPECT_EQ(event->fields.at(0).strategy, kCStringToString);
111 }
112 }
113
114 INSTANTIATE_TEST_SUITE_P(ByDevice, AllTranslationTableTest, ValuesIn(kDevices));
115
TEST(TranslationTableTest,Seed)116 TEST(TranslationTableTest, Seed) {
117 std::string path = base::GetTestDataPath(
118 "src/traced/probes/ftrace/test/data/android_seed_N2F62_3.10.49/");
119 FtraceProcfs ftrace_procfs(path);
120 auto table = ProtoTranslationTable::Create(
121 &ftrace_procfs, GetStaticEventInfo(), GetStaticCommonFieldsInfo());
122 PERFETTO_CHECK(table);
123 const Field& pid_field = table->common_fields().at(0);
124 EXPECT_EQ(std::string(pid_field.ftrace_name), "common_pid");
125 EXPECT_EQ(pid_field.proto_field_id, 2u);
126 EXPECT_EQ(pid_field.ftrace_offset, 4u);
127 EXPECT_EQ(pid_field.ftrace_size, 4u);
128
129 {
130 auto event = table->GetEvent(GroupAndName("sched", "sched_switch"));
131 EXPECT_EQ(std::string(event->name), "sched_switch");
132 EXPECT_EQ(std::string(event->group), "sched");
133 EXPECT_EQ(event->ftrace_event_id, 68ul);
134 EXPECT_EQ(event->fields.at(0).ftrace_offset, 8u);
135 EXPECT_EQ(event->fields.at(0).ftrace_size, 16u);
136 }
137
138 {
139 auto event = table->GetEvent(GroupAndName("sched", "sched_wakeup"));
140 EXPECT_EQ(std::string(event->name), "sched_wakeup");
141 EXPECT_EQ(std::string(event->group), "sched");
142 EXPECT_EQ(event->ftrace_event_id, 70ul);
143 EXPECT_EQ(event->fields.at(0).ftrace_offset, 8u);
144 EXPECT_EQ(event->fields.at(0).ftrace_size, 16u);
145 }
146
147 {
148 auto event = table->GetEvent(GroupAndName("ext4", "ext4_da_write_begin"));
149 EXPECT_EQ(std::string(event->name), "ext4_da_write_begin");
150 EXPECT_EQ(std::string(event->group), "ext4");
151 EXPECT_EQ(event->ftrace_event_id, 303ul);
152 EXPECT_EQ(event->fields.at(0).ftrace_offset, 8u);
153 EXPECT_EQ(event->fields.at(0).ftrace_size, 4u);
154 }
155 }
156
TEST_P(TranslationTableCreationTest,Create)157 TEST_P(TranslationTableCreationTest, Create) {
158 MockFtraceProcfs ftrace;
159 std::vector<Field> common_fields;
160 std::vector<Event> events;
161
162 ON_CALL(ftrace, ReadPageHeaderFormat())
163 .WillByDefault(Return(
164 R"( field: u64 timestamp; offset:0; size:8; signed:0;
165 field: local_t commit; offset:8; size:)" +
166 std::to_string(GetParam()) + R"(; signed:1;
167 field: int overwrite; offset:8; size:1; signed:1;
168 field: char data; offset:16; size:4080; signed:0;)"));
169 ON_CALL(ftrace, ReadEventFormat(_, _)).WillByDefault(Return(""));
170 ON_CALL(ftrace, ReadEventFormat("group", "foo"))
171 .WillByDefault(Return(R"(name: foo
172 ID: 42
173 format:
174 field:unsigned short common_type; offset:0; size:2; signed:0;
175 field:int common_pid; offset:4; size:4; signed:1;
176
177 field:char field_a[16]; offset:8; size:16; signed:0;
178 field:int field_b; offset:24; size:4; signed:1;
179 field:int field_d; offset:28; size:4; signed:1;
180 field:u32 field_e; offset:32; size:4; signed:0;
181
182 print fmt: "some format")"));
183
184 EXPECT_CALL(ftrace, ReadPageHeaderFormat()).Times(AnyNumber());
185 EXPECT_CALL(ftrace, ReadEventFormat(_, _)).Times(AnyNumber());
186
187 {
188 events.emplace_back(Event{});
189 Event* event = &events.back();
190 event->name = "foo";
191 event->group = "group";
192 event->proto_field_id = 21;
193
194 {
195 // We should get this field.
196 event->fields.emplace_back(Field{});
197 Field* field = &event->fields.back();
198 field->proto_field_id = 501;
199 field->proto_field_type = ProtoSchemaType::kString;
200 field->ftrace_name = "field_a";
201 }
202
203 {
204 // We shouldn't get this field: don't know how to read int -> string.
205 event->fields.emplace_back(Field{});
206 Field* field = &event->fields.back();
207 field->proto_field_id = 502;
208 field->proto_field_type = ProtoSchemaType::kString;
209 field->ftrace_name = "field_b";
210 }
211
212 {
213 // We shouldn't get this field: no matching field in the format file.
214 event->fields.emplace_back(Field{});
215 Field* field = &event->fields.back();
216 field->proto_field_id = 503;
217 field->proto_field_type = ProtoSchemaType::kString;
218 field->ftrace_name = "field_c";
219 }
220
221 {
222 // We should get this field.
223 event->fields.emplace_back(Field{});
224 Field* field = &event->fields.back();
225 field->proto_field_id = 504;
226 field->proto_field_type = ProtoSchemaType::kUint64;
227 field->ftrace_name = "field_e";
228 }
229 }
230
231 {
232 events.emplace_back(Event{});
233 Event* event = &events.back();
234 event->name = "bar";
235 event->group = "group";
236 event->proto_field_id = 22;
237 }
238
239 auto table = ProtoTranslationTable::Create(&ftrace, std::move(events),
240 std::move(common_fields));
241 PERFETTO_CHECK(table);
242 EXPECT_EQ(table->largest_id(), 42ul);
243 EXPECT_EQ(table->EventToFtraceId(GroupAndName("group", "foo")), 42ul);
244 EXPECT_EQ(table->EventToFtraceId(GroupAndName("group", "bar")), 0ul);
245 EXPECT_FALSE(table->GetEventById(43ul));
246 ASSERT_TRUE(table->GetEventById(42ul));
247 EXPECT_EQ(table->ftrace_page_header_spec().timestamp.size, 8);
248 EXPECT_EQ(table->ftrace_page_header_spec().size.size, GetParam());
249 EXPECT_EQ(table->ftrace_page_header_spec().overwrite.size, 1);
250 auto event = table->GetEventById(42);
251 EXPECT_EQ(event->ftrace_event_id, 42ul);
252 EXPECT_EQ(event->proto_field_id, 21ul);
253 EXPECT_EQ(event->size, 36u);
254 EXPECT_EQ(std::string(event->name), "foo");
255 EXPECT_EQ(std::string(event->group), "group");
256
257 ASSERT_EQ(event->fields.size(), 2ul);
258 auto field_a = event->fields.at(0);
259 EXPECT_EQ(field_a.proto_field_id, 501ul);
260 EXPECT_EQ(field_a.strategy, kFixedCStringToString);
261
262 auto field_e = event->fields.at(1);
263 EXPECT_EQ(field_e.proto_field_id, 504ul);
264 EXPECT_EQ(field_e.strategy, kUint32ToUint64);
265 }
266
267 INSTANTIATE_TEST_SUITE_P(BySize, TranslationTableCreationTest, Values(4, 8));
268
TEST(TranslationTableTest,CompactSchedFormatParsingWalleyeData)269 TEST(TranslationTableTest, CompactSchedFormatParsingWalleyeData) {
270 std::string path = base::GetTestDataPath(
271 "src/traced/probes/ftrace/test/data/"
272 "android_walleye_OPM5.171019.017.A1_4.4.88/");
273 FtraceProcfs ftrace_procfs(path);
274 auto table = ProtoTranslationTable::Create(
275 &ftrace_procfs, GetStaticEventInfo(), GetStaticCommonFieldsInfo());
276 PERFETTO_CHECK(table);
277 const CompactSchedEventFormat& format = table->compact_sched_format();
278
279 // Format matches compile-time assumptions.
280 ASSERT_TRUE(format.format_valid);
281
282 // Check exact sched_switch format (note: 64 bit long prev_state).
283 EXPECT_EQ(47u, format.sched_switch.event_id);
284 EXPECT_EQ(64u, format.sched_switch.size);
285 EXPECT_EQ(56u, format.sched_switch.next_pid_offset);
286 EXPECT_EQ(FtraceFieldType::kFtracePid32, format.sched_switch.next_pid_type);
287 EXPECT_EQ(60u, format.sched_switch.next_prio_offset);
288 EXPECT_EQ(FtraceFieldType::kFtraceInt32, format.sched_switch.next_prio_type);
289 EXPECT_EQ(32u, format.sched_switch.prev_state_offset);
290 EXPECT_EQ(FtraceFieldType::kFtraceInt64, format.sched_switch.prev_state_type);
291 EXPECT_EQ(40u, format.sched_switch.next_comm_offset);
292
293 // Check exact sched_waking format.
294 EXPECT_EQ(44u, format.sched_waking.event_id);
295 EXPECT_EQ(40u, format.sched_waking.size);
296 EXPECT_EQ(24u, format.sched_waking.pid_offset);
297 EXPECT_EQ(FtraceFieldType::kFtracePid32, format.sched_waking.pid_type);
298 EXPECT_EQ(36u, format.sched_waking.target_cpu_offset);
299 EXPECT_EQ(FtraceFieldType::kFtraceInt32, format.sched_waking.target_cpu_type);
300 EXPECT_EQ(28u, format.sched_waking.prio_offset);
301 EXPECT_EQ(FtraceFieldType::kFtraceInt32, format.sched_waking.prio_type);
302 EXPECT_EQ(8u, format.sched_waking.comm_offset);
303 }
304
TEST(TranslationTableTest,CompactSchedFormatParsingSeedData)305 TEST(TranslationTableTest, CompactSchedFormatParsingSeedData) {
306 std::string path =
307 "src/traced/probes/ftrace/test/data/android_seed_N2F62_3.10.49/";
308 FtraceProcfs ftrace_procfs(path);
309 auto table = ProtoTranslationTable::Create(
310 &ftrace_procfs, GetStaticEventInfo(), GetStaticCommonFieldsInfo());
311 PERFETTO_CHECK(table);
312 const CompactSchedEventFormat& format = table->compact_sched_format();
313
314 // We consider the entire format invalid as there's no sched_waking event
315 // available. This is a simplifying assumption. We could instead look at each
316 // event independently (and in this case, sched_switch does match compile-time
317 // assumptions).
318 ASSERT_FALSE(format.format_valid);
319 }
320
TEST(TranslationTableTest,InferFtraceType)321 TEST(TranslationTableTest, InferFtraceType) {
322 FtraceFieldType type;
323
324 ASSERT_TRUE(InferFtraceType("char foo[16]", 16, false, &type));
325 EXPECT_EQ(type, kFtraceFixedCString);
326
327 ASSERT_TRUE(InferFtraceType("char comm[TASK_COMM_LEN]", 16, false, &type));
328 EXPECT_EQ(type, kFtraceFixedCString);
329
330 ASSERT_TRUE(InferFtraceType("char identifier22[16]", 16, false, &type));
331 EXPECT_EQ(type, kFtraceFixedCString);
332
333 EXPECT_FALSE(InferFtraceType("char 2invalid[16]", 16, false, &type));
334
335 ASSERT_TRUE(InferFtraceType("char[] foo", 8, false, &type));
336 EXPECT_EQ(type, kFtraceStringPtr);
337
338 ASSERT_TRUE(InferFtraceType("char * foo", 8, false, &type));
339 EXPECT_EQ(type, kFtraceStringPtr);
340
341 ASSERT_TRUE(InferFtraceType("char foo[64]", 64, false, &type));
342 EXPECT_EQ(type, kFtraceFixedCString);
343
344 ASSERT_TRUE(InferFtraceType("u32 foo", 4, false, &type));
345 EXPECT_EQ(type, kFtraceUint32);
346
347 ASSERT_TRUE(InferFtraceType("i_ino foo", 4, false, &type));
348 ASSERT_EQ(type, kFtraceInode32);
349
350 ASSERT_TRUE(InferFtraceType("i_ino foo", 8, false, &type));
351 ASSERT_EQ(type, kFtraceInode64);
352
353 ASSERT_TRUE(InferFtraceType("ino_t foo", 4, false, &type));
354 ASSERT_EQ(type, kFtraceInode32);
355
356 ASSERT_TRUE(InferFtraceType("ino_t foo", 8, false, &type));
357 ASSERT_EQ(type, kFtraceInode64);
358
359 ASSERT_TRUE(InferFtraceType("dev_t foo", 4, false, &type));
360 ASSERT_EQ(type, kFtraceDevId32);
361
362 ASSERT_TRUE(InferFtraceType("dev_t foo", 8, false, &type));
363 ASSERT_EQ(type, kFtraceDevId64);
364
365 ASSERT_TRUE(InferFtraceType("pid_t foo", 4, false, &type));
366 ASSERT_EQ(type, kFtracePid32);
367
368 ASSERT_TRUE(InferFtraceType("int common_pid", 4, false, &type));
369 ASSERT_EQ(type, kFtraceCommonPid32);
370
371 ASSERT_TRUE(InferFtraceType("char foo", 1, true, &type));
372 ASSERT_EQ(type, kFtraceInt8);
373
374 ASSERT_TRUE(InferFtraceType("__data_loc char[] foo", 4, false, &type));
375 ASSERT_EQ(type, kFtraceDataLoc);
376 ASSERT_FALSE(InferFtraceType("__data_loc char[] foo", 8, false, &type));
377
378 ASSERT_TRUE(InferFtraceType("unsigned long args[6]", 24, true, &type));
379 ASSERT_EQ(type, kFtraceUint32);
380 ASSERT_TRUE(InferFtraceType("unsigned long args[6]", 48, true, &type));
381 ASSERT_EQ(type, kFtraceUint64);
382 ASSERT_FALSE(InferFtraceType("unsigned long args[6]", 96, true, &type));
383
384 EXPECT_FALSE(InferFtraceType("foo", 64, false, &type));
385 }
386
TEST(TranslationTableTest,Getters)387 TEST(TranslationTableTest, Getters) {
388 MockFtraceProcfs ftrace;
389 std::vector<Field> common_fields;
390 std::vector<Event> events;
391
392 {
393 Event event{};
394 event.name = "foo";
395 event.group = "group_one";
396 event.ftrace_event_id = 1;
397 events.push_back(event);
398 }
399
400 {
401 Event event{};
402 event.name = "bar";
403 event.group = "group_one";
404 event.ftrace_event_id = 2;
405 events.push_back(event);
406 }
407
408 {
409 Event event{};
410 event.name = "baz";
411 event.group = "group_two";
412 event.ftrace_event_id = 100;
413 events.push_back(event);
414 }
415
416 ProtoTranslationTable table(
417 &ftrace, events, std::move(common_fields),
418 ProtoTranslationTable::DefaultPageHeaderSpecForTesting(),
419 InvalidCompactSchedEventFormatForTesting(), PrintkMap());
420
421 EXPECT_EQ(table.largest_id(), 100ul);
422 EXPECT_EQ(table.EventToFtraceId(GroupAndName("group_one", "foo")), 1ul);
423 EXPECT_EQ(table.EventToFtraceId(GroupAndName("group_two", "baz")), 100ul);
424 EXPECT_EQ(table.EventToFtraceId(GroupAndName("group_one", "no_such_event")),
425 0ul);
426 EXPECT_EQ(table.GetEventById(1)->name, "foo");
427 EXPECT_EQ(table.GetEventById(3), nullptr);
428 EXPECT_EQ(table.GetEventById(200), nullptr);
429 EXPECT_EQ(table.GetEventById(0), nullptr);
430 EXPECT_EQ(table.GetEvent(GroupAndName("group_one", "foo"))->ftrace_event_id,
431 1u);
432 EXPECT_THAT(*table.GetEventsByGroup("group_one"),
433 Contains(testing::Field(&Event::name, "foo")));
434 EXPECT_THAT(*table.GetEventsByGroup("group_one"),
435 Contains(testing::Field(&Event::name, "bar")));
436 EXPECT_THAT(*table.GetEventsByGroup("group_two"),
437 Contains(testing::Field(&Event::name, "baz")));
438 EXPECT_THAT(table.GetEventsByGroup("group_three"), IsNull());
439 }
440
TEST(TranslationTableTest,Generic)441 TEST(TranslationTableTest, Generic) {
442 MockFtraceProcfs ftrace;
443 std::vector<Field> common_fields;
444 std::vector<Event> events;
445
446 ON_CALL(ftrace, ReadPageHeaderFormat())
447 .WillByDefault(Return(
448 R"( field: u64 timestamp; offset:0; size:8; signed:0;
449 field: local_t commit; offset:8; size:4; signed:1;
450 field: int overwrite; offset:8; size:1; signed:1;
451 field: char data; offset:16; size:4080; signed:0;)"));
452 ON_CALL(ftrace, ReadEventFormat(_, _)).WillByDefault(Return(""));
453 ON_CALL(ftrace, ReadEventFormat("group", "foo"))
454 .WillByDefault(Return(R"(name: foo
455 ID: 42
456 format:
457 field:unsigned short common_type; offset:0; size:2; signed:0;
458 field:int common_pid; offset:4; size:4; signed:1;
459
460 field:char field_a[16]; offset:8; size:16; signed:0;
461 field:bool field_b; offset:24; size:1; signed:0;
462 field:int field_c; offset:25; size:4; signed:1;
463 field:u32 field_d; offset:33; size:4; signed:0;
464
465 print fmt: "some format")"));
466
467 EXPECT_CALL(ftrace, ReadPageHeaderFormat()).Times(AnyNumber());
468 EXPECT_CALL(ftrace, ReadEventFormat(_, _)).Times(AnyNumber());
469
470 auto table = ProtoTranslationTable::Create(&ftrace, std::move(events),
471 std::move(common_fields));
472 PERFETTO_CHECK(table);
473 EXPECT_EQ(table->largest_id(), 0ul);
474 GroupAndName group_and_name("group", "foo");
475 const Event* e = table->GetOrCreateEvent(group_and_name);
476 EXPECT_EQ(table->largest_id(), 42ul);
477 EXPECT_EQ(table->EventToFtraceId(group_and_name), 42ul);
478
479 // Check getters
480 EXPECT_EQ(static_cast<int>(table->GetEventById(42)->proto_field_id),
481 protos::pbzero::FtraceEvent::kGenericFieldNumber);
482 EXPECT_EQ(static_cast<int>(table->GetEvent(group_and_name)->proto_field_id),
483 protos::pbzero::FtraceEvent::kGenericFieldNumber);
484 EXPECT_EQ(table->GetEventsByGroup("group")->front()->name,
485 group_and_name.name());
486
487 EXPECT_EQ(e->fields.size(), 4ul);
488 const std::vector<Field>& fields = e->fields;
489 // Check string field
490 const auto& str_field = fields[0];
491 EXPECT_STREQ(str_field.ftrace_name, "field_a");
492 EXPECT_EQ(static_cast<int>(str_field.proto_field_id),
493 protos::pbzero::GenericFtraceEvent::Field::kStrValueFieldNumber);
494 EXPECT_EQ(str_field.proto_field_type, ProtoSchemaType::kString);
495 EXPECT_EQ(str_field.ftrace_type, kFtraceFixedCString);
496 EXPECT_EQ(str_field.ftrace_size, 16);
497 EXPECT_EQ(str_field.ftrace_offset, 8);
498
499 // Check bool field
500 const auto& bool_field = fields[1];
501 EXPECT_STREQ(bool_field.ftrace_name, "field_b");
502 EXPECT_EQ(static_cast<int>(bool_field.proto_field_id),
503 protos::pbzero::GenericFtraceEvent::Field::kUintValueFieldNumber);
504 EXPECT_EQ(bool_field.proto_field_type, ProtoSchemaType::kUint64);
505 EXPECT_EQ(bool_field.ftrace_type, kFtraceBool);
506 EXPECT_EQ(bool_field.ftrace_size, 1);
507 EXPECT_EQ(bool_field.ftrace_offset, 24);
508
509 // Check int field
510 const auto& int_field = fields[2];
511 EXPECT_STREQ(int_field.ftrace_name, "field_c");
512 EXPECT_EQ(static_cast<int>(int_field.proto_field_id),
513 protos::pbzero::GenericFtraceEvent::Field::kIntValueFieldNumber);
514 EXPECT_EQ(int_field.proto_field_type, ProtoSchemaType::kInt64);
515 EXPECT_EQ(int_field.ftrace_type, kFtraceInt32);
516 EXPECT_EQ(int_field.ftrace_size, 4);
517 EXPECT_EQ(int_field.ftrace_offset, 25);
518
519 // Check uint field
520 const auto& uint_field = fields[3];
521 EXPECT_STREQ(uint_field.ftrace_name, "field_d");
522 EXPECT_EQ(static_cast<int>(uint_field.proto_field_id),
523 protos::pbzero::GenericFtraceEvent::Field::kUintValueFieldNumber);
524 EXPECT_EQ(uint_field.proto_field_type, ProtoSchemaType::kUint64);
525 EXPECT_EQ(uint_field.ftrace_type, kFtraceUint32);
526 EXPECT_EQ(uint_field.ftrace_size, 4);
527 EXPECT_EQ(uint_field.ftrace_offset, 33);
528 }
529
TEST(EventFilterTest,EnableEventsFrom)530 TEST(EventFilterTest, EnableEventsFrom) {
531 EventFilter filter;
532 filter.AddEnabledEvent(1);
533 filter.AddEnabledEvent(17);
534
535 EventFilter or_filter;
536 or_filter.AddEnabledEvent(4);
537 or_filter.AddEnabledEvent(17);
538
539 filter.EnableEventsFrom(or_filter);
540 EXPECT_TRUE(filter.IsEventEnabled(4));
541 EXPECT_TRUE(filter.IsEventEnabled(17));
542 EXPECT_TRUE(filter.IsEventEnabled(1));
543 EXPECT_FALSE(filter.IsEventEnabled(2));
544
545 EventFilter empty_filter;
546 filter.EnableEventsFrom(empty_filter);
547 EXPECT_TRUE(filter.IsEventEnabled(4));
548 EXPECT_TRUE(filter.IsEventEnabled(17));
549 EXPECT_TRUE(filter.IsEventEnabled(1));
550
551 empty_filter.EnableEventsFrom(filter);
552 EXPECT_TRUE(empty_filter.IsEventEnabled(4));
553 EXPECT_TRUE(empty_filter.IsEventEnabled(17));
554 EXPECT_TRUE(empty_filter.IsEventEnabled(1));
555 }
556
TEST(TranslationTableTest,FuncgraphEvents)557 TEST(TranslationTableTest, FuncgraphEvents) {
558 std::string path =
559 base::GetTestDataPath("src/traced/probes/ftrace/test/data/synthetic/");
560 FtraceProcfs ftrace_procfs(path);
561 auto table = ProtoTranslationTable::Create(
562 &ftrace_procfs, GetStaticEventInfo(), GetStaticCommonFieldsInfo());
563 PERFETTO_CHECK(table);
564
565 {
566 auto* event = table->GetEvent(GroupAndName("ftrace", "funcgraph_entry"));
567 EXPECT_EQ(std::string(event->name), "funcgraph_entry");
568 EXPECT_EQ(std::string(event->group), "ftrace");
569
570 // field:unsigned long func; offset:8; size:8; signed:0;
571 // field:int depth; offset:16; size:4; signed:1;
572 ASSERT_EQ(event->fields.size(), 2u);
573
574 // note: fields in struct are ordered as in the proto, not the format file
575 EXPECT_THAT(
576 event->fields,
577 Contains(
578 AllOf(testing::Field(&Field::ftrace_name, StrEq("func")),
579 testing::Field(&Field::ftrace_offset, Eq(8u)),
580 testing::Field(&Field::ftrace_type, kFtraceSymAddr64),
581 testing::Field(&Field::strategy, kFtraceSymAddr64ToUint64))));
582 }
583 {
584 auto* event = table->GetEvent(GroupAndName("ftrace", "funcgraph_exit"));
585 EXPECT_EQ(std::string(event->name), "funcgraph_exit");
586 EXPECT_EQ(std::string(event->group), "ftrace");
587
588 // field:unsigned long func; offset:8; size:8; signed:0;
589 // field:int depth; offset:16; size:4; signed:1;
590 // field:unsigned int overrun; offset:20; size:4; signed:0;
591 // field:unsigned long long calltime; offset:24; size:8; signed:0;
592 // field:unsigned long long rettime; offset:32; size:8; signed:0;
593 ASSERT_EQ(event->fields.size(), 5u);
594 // note: fields in struct are ordered as in the proto, not the format file
595 EXPECT_THAT(
596 event->fields,
597 Contains(
598 AllOf(testing::Field(&Field::ftrace_name, StrEq("func")),
599 testing::Field(&Field::ftrace_offset, Eq(8u)),
600 testing::Field(&Field::ftrace_type, kFtraceSymAddr64),
601 testing::Field(&Field::strategy, kFtraceSymAddr64ToUint64))));
602 }
603 }
604
TEST(TranslationTableTest,CreateRemoveKprobeEvent)605 TEST(TranslationTableTest, CreateRemoveKprobeEvent) {
606 NiceMock<MockFtraceProcfs> ftrace;
607 ON_CALL(ftrace, ReadEventFormat(_, _)).WillByDefault(Return(""));
608 ON_CALL(ftrace, ReadPageHeaderFormat())
609 .WillByDefault(Return(
610 R"( field: u64 timestamp; offset:0; size:8; signed:0;
611 field: local_t commit; offset:8; size:4; signed:1;
612 field: int overwrite; offset:8; size:1; signed:1;
613 field: char data; offset:16; size:4080; signed:0;)"));
614 auto table = ProtoTranslationTable::Create(&ftrace, GetStaticEventInfo(),
615 GetStaticCommonFieldsInfo());
616 PERFETTO_CHECK(table);
617
618 EXPECT_CALL(ftrace,
619 ReadEventFormat("perfetto_kprobe", "fuse_file_write_iter"))
620 .WillOnce(Return(R"format(name: fuse_file_write_iter
621 ID: 1535
622 format:
623 field:unsigned short common_type; offset:0; size:2; signed:0;
624 field:unsigned char common_flags; offset:2; size:1; signed:0;
625 field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
626 field:int common_pid; offset:4; size:4; signed:1;
627
628 field:unsigned long __probe_ip; offset:8; size:8; signed:0;
629
630 print fmt: "(%lx)", REC->__probe_ip
631 )format"));
632 const Event* event = table->GetOrCreateKprobeEvent(
633 {"perfetto_kprobe", "fuse_file_write_iter"});
634 ASSERT_NE(event, nullptr);
635 EXPECT_EQ(event->ftrace_event_id, 1535u);
636 EXPECT_EQ(table->GetEventByName("fuse_file_write_iter"), event);
637 EXPECT_THAT(table->GetEventsByGroup("perfetto_kprobe"),
638 Pointee(ElementsAre(event)));
639 EXPECT_EQ(table->GetEventById(1535), event);
640
641 table->RemoveEvent({"perfetto_kprobe", "fuse_file_write_iter"});
642 EXPECT_EQ(table->GetEventByName("fuse_file_write_iter"), nullptr);
643 EXPECT_EQ(table->GetEventsByGroup("perfetto_kprobe"), nullptr);
644 EXPECT_EQ(table->GetEventById(1535), nullptr);
645
646 EXPECT_CALL(ftrace,
647 ReadEventFormat("perfetto_kprobe", "fuse_file_write_iter"))
648 .WillOnce(Return(R"format(name: fuse_file_write_iter
649 ID: 1536
650 format:
651 field:unsigned short common_type; offset:0; size:2; signed:0;
652 field:unsigned char common_flags; offset:2; size:1; signed:0;
653 field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
654 field:int common_pid; offset:4; size:4; signed:1;
655
656 field:unsigned long __probe_ip; offset:8; size:8; signed:0;
657
658 print fmt: "(%lx)", REC->__probe_ip
659 )format"));
660 event = table->GetOrCreateKprobeEvent(
661 {"perfetto_kprobe", "fuse_file_write_iter"});
662 ASSERT_NE(event, nullptr);
663 EXPECT_EQ(event->ftrace_event_id, 1536u);
664 EXPECT_EQ(table->GetEventByName("fuse_file_write_iter"), event);
665 EXPECT_THAT(table->GetEventsByGroup("perfetto_kprobe"),
666 Pointee(ElementsAre(event)));
667 EXPECT_EQ(table->GetEventById(1536), event);
668 }
669
670 } // namespace
671 } // namespace perfetto
672