/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "src/ftrace_reader/proto_translation_table.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "src/ftrace_reader/event_info.h" #include "src/ftrace_reader/ftrace_procfs.h" using testing::_; using testing::ValuesIn; using testing::TestWithParam; using testing::Return; using testing::AnyNumber; using testing::IsNull; using testing::Contains; using testing::Eq; using testing::Pointee; namespace perfetto { namespace { class MockFtraceProcfs : public FtraceProcfs { public: MockFtraceProcfs() : FtraceProcfs("/root/") {} MOCK_CONST_METHOD2(ReadEventFormat, std::string(const std::string& group, const std::string& name)); }; class AllTranslationTableTest : public TestWithParam<const char*> { public: void SetUp() override { std::string path = "src/ftrace_reader/test/data/" + std::string(GetParam()) + "/"; FtraceProcfs ftrace_procfs(path); table_ = ProtoTranslationTable::Create(&ftrace_procfs, GetStaticEventInfo(), GetStaticCommonFieldsInfo()); PERFETTO_CHECK(table_); } std::unique_ptr<ProtoTranslationTable> table_; }; const char* kDevices[] = { "android_seed_N2F62_3.10.49", "android_hammerhead_MRA59G_3.4.0", }; TEST_P(AllTranslationTableTest, Create) { EXPECT_TRUE(table_); EXPECT_TRUE(table_->GetEventByName("print")); EXPECT_TRUE(table_->GetEventByName("sched_switch")); EXPECT_TRUE(table_->GetEventByName("sched_wakeup")); EXPECT_TRUE(table_->GetEventByName("ext4_da_write_begin")); for (const Event& event : table_->events()) { if (!event.ftrace_event_id) continue; EXPECT_TRUE(event.name); EXPECT_TRUE(event.group); EXPECT_TRUE(event.proto_field_id); for (const Field& field : event.fields) { EXPECT_TRUE(field.proto_field_id); EXPECT_TRUE(field.ftrace_type); EXPECT_TRUE(field.proto_field_type); } } ASSERT_EQ(table_->common_fields().size(), 1u); const Field& pid_field = table_->common_fields().at(0); EXPECT_EQ(std::string(pid_field.ftrace_name), "common_pid"); EXPECT_EQ(pid_field.proto_field_id, 2u); { auto event = table_->GetEventByName("print"); EXPECT_TRUE(event); EXPECT_EQ(std::string(event->name), "print"); EXPECT_EQ(std::string(event->group), "ftrace"); EXPECT_EQ(event->fields.at(1).proto_field_type, kProtoString); EXPECT_EQ(event->fields.at(1).ftrace_type, kFtraceCString); EXPECT_EQ(event->fields.at(1).strategy, kCStringToString); } } INSTANTIATE_TEST_CASE_P(ByDevice, AllTranslationTableTest, ValuesIn(kDevices)); TEST(TranslationTableTest, Seed) { std::string path = "src/ftrace_reader/test/data/android_seed_N2F62_3.10.49/"; FtraceProcfs ftrace_procfs(path); auto table = ProtoTranslationTable::Create( &ftrace_procfs, GetStaticEventInfo(), GetStaticCommonFieldsInfo()); const Field& pid_field = table->common_fields().at(0); EXPECT_EQ(std::string(pid_field.ftrace_name), "common_pid"); EXPECT_EQ(pid_field.proto_field_id, 2u); EXPECT_EQ(pid_field.ftrace_offset, 4u); EXPECT_EQ(pid_field.ftrace_size, 4u); { auto event = table->GetEventByName("sched_switch"); EXPECT_EQ(std::string(event->name), "sched_switch"); EXPECT_EQ(std::string(event->group), "sched"); EXPECT_EQ(event->ftrace_event_id, 68ul); EXPECT_EQ(event->fields.at(0).ftrace_offset, 8u); EXPECT_EQ(event->fields.at(0).ftrace_size, 16u); } { auto event = table->GetEventByName("sched_wakeup"); EXPECT_EQ(std::string(event->name), "sched_wakeup"); EXPECT_EQ(std::string(event->group), "sched"); EXPECT_EQ(event->ftrace_event_id, 70ul); EXPECT_EQ(event->fields.at(0).ftrace_offset, 8u); EXPECT_EQ(event->fields.at(0).ftrace_size, 16u); } { auto event = table->GetEventByName("cpufreq_interactive_target"); EXPECT_EQ(std::string(event->name), "cpufreq_interactive_target"); EXPECT_EQ(std::string(event->group), "cpufreq_interactive"); EXPECT_EQ(event->ftrace_event_id, 509ul); EXPECT_EQ(event->fields.at(0).ftrace_offset, 8u); EXPECT_EQ(event->fields.at(0).ftrace_size, 4u); } { auto event = table->GetEventByName("ext4_da_write_begin"); EXPECT_EQ(std::string(event->name), "ext4_da_write_begin"); EXPECT_EQ(std::string(event->group), "ext4"); EXPECT_EQ(event->ftrace_event_id, 303ul); EXPECT_EQ(event->fields.at(0).ftrace_offset, 8u); EXPECT_EQ(event->fields.at(0).ftrace_size, 4u); } } TEST(TranslationTableTest, Create) { MockFtraceProcfs ftrace; std::vector<Field> common_fields; std::vector<Event> events; ON_CALL(ftrace, ReadEventFormat(_, _)).WillByDefault(Return("")); ON_CALL(ftrace, ReadEventFormat("group", "foo")) .WillByDefault(Return(R"(name: foo ID: 42 format: field:unsigned short common_type; offset:0; size:2; signed:0; field:int common_pid; offset:4; size:4; signed:1; field:char field_a[16]; offset:8; size:16; signed:0; field:int field_b; offset:24; size:4; signed:1; field:int field_d; offset:28; size:4; signed:1; field:u32 field_e; offset:32; size:4; signed:0; print fmt: "some format")")); ; EXPECT_CALL(ftrace, ReadEventFormat(_, _)).Times(AnyNumber()); { events.emplace_back(Event{}); Event* event = &events.back(); event->name = "foo"; event->group = "group"; event->proto_field_id = 21; { // We should get this field. event->fields.emplace_back(Field{}); Field* field = &event->fields.back(); field->proto_field_id = 501; field->proto_field_type = kProtoString; field->ftrace_name = "field_a"; } { // We shouldn't get this field: don't know how to read int -> string. event->fields.emplace_back(Field{}); Field* field = &event->fields.back(); field->proto_field_id = 502; field->proto_field_type = kProtoString; field->ftrace_name = "field_b"; } { // We shouldn't get this field: no matching field in the format file. event->fields.emplace_back(Field{}); Field* field = &event->fields.back(); field->proto_field_id = 503; field->proto_field_type = kProtoString; field->ftrace_name = "field_c"; } { // We should get this field. event->fields.emplace_back(Field{}); Field* field = &event->fields.back(); field->proto_field_id = 504; field->proto_field_type = kProtoUint64; field->ftrace_name = "field_e"; } } { events.emplace_back(Event{}); Event* event = &events.back(); event->name = "bar"; event->group = "group"; event->proto_field_id = 22; } auto table = ProtoTranslationTable::Create(&ftrace, std::move(events), std::move(common_fields)); EXPECT_EQ(table->largest_id(), 42ul); EXPECT_EQ(table->EventNameToFtraceId("foo"), 42ul); EXPECT_EQ(table->EventNameToFtraceId("bar"), 0ul); EXPECT_EQ(table->EventNameToFtraceId("bar"), 0ul); EXPECT_FALSE(table->GetEventById(43ul)); ASSERT_TRUE(table->GetEventById(42ul)); auto event = table->GetEventById(42); EXPECT_EQ(event->ftrace_event_id, 42ul); EXPECT_EQ(event->proto_field_id, 21ul); EXPECT_EQ(event->size, 36u); EXPECT_EQ(std::string(event->name), "foo"); EXPECT_EQ(std::string(event->group), "group"); ASSERT_EQ(event->fields.size(), 2ul); auto field_a = event->fields.at(0); EXPECT_EQ(field_a.proto_field_id, 501ul); EXPECT_EQ(field_a.strategy, kFixedCStringToString); auto field_e = event->fields.at(1); EXPECT_EQ(field_e.proto_field_id, 504ul); EXPECT_EQ(field_e.strategy, kUint32ToUint64); } TEST(TranslationTableTest, InferFtraceType) { FtraceFieldType type; ASSERT_TRUE(InferFtraceType("char foo[16]", 16, false, &type)); EXPECT_EQ(type, kFtraceFixedCString); ASSERT_TRUE(InferFtraceType("__data_loc char[] foo", 4, false, &type)); EXPECT_EQ(type, kFtraceStringPtr); ASSERT_TRUE(InferFtraceType("char[] foo", 8, false, &type)); EXPECT_EQ(type, kFtraceStringPtr); ASSERT_TRUE(InferFtraceType("char * foo", 8, false, &type)); EXPECT_EQ(type, kFtraceStringPtr); ASSERT_TRUE(InferFtraceType("char foo[64]", 64, false, &type)); EXPECT_EQ(type, kFtraceFixedCString); ASSERT_TRUE(InferFtraceType("u32 foo", 4, false, &type)); EXPECT_EQ(type, kFtraceUint32); ASSERT_TRUE(InferFtraceType("i_ino foo", 4, false, &type)); ASSERT_EQ(type, kFtraceInode32); ASSERT_TRUE(InferFtraceType("i_ino foo", 8, false, &type)); ASSERT_EQ(type, kFtraceInode64); ASSERT_TRUE(InferFtraceType("ino_t foo", 4, false, &type)); ASSERT_EQ(type, kFtraceInode32); ASSERT_TRUE(InferFtraceType("ino_t foo", 8, false, &type)); ASSERT_EQ(type, kFtraceInode64); ASSERT_TRUE(InferFtraceType("dev_t foo", 4, false, &type)); ASSERT_EQ(type, kFtraceDevId32); ASSERT_TRUE(InferFtraceType("dev_t foo", 8, false, &type)); ASSERT_EQ(type, kFtraceDevId64); ASSERT_TRUE(InferFtraceType("pid_t foo", 4, false, &type)); ASSERT_EQ(type, kFtracePid32); ASSERT_TRUE(InferFtraceType("int common_pid", 4, false, &type)); ASSERT_EQ(type, kFtraceCommonPid32); ASSERT_TRUE(InferFtraceType("char foo", 1, true, &type)); ASSERT_EQ(type, kFtraceInt8); EXPECT_FALSE(InferFtraceType("foo", 64, false, &type)); } TEST(TranslationTableTest, Getters) { std::vector<Field> common_fields; std::vector<Event> events; { Event event; event.name = "foo"; event.group = "group_one"; event.ftrace_event_id = 1; events.push_back(event); } { Event event; event.name = "bar"; event.group = "group_one"; event.ftrace_event_id = 2; events.push_back(event); } { Event event; event.name = "baz"; event.group = "group_two"; event.ftrace_event_id = 100; events.push_back(event); } ProtoTranslationTable table(events, std::move(common_fields)); EXPECT_EQ(table.largest_id(), 100ul); EXPECT_EQ(table.EventNameToFtraceId("foo"), 1ul); EXPECT_EQ(table.EventNameToFtraceId("baz"), 100ul); EXPECT_EQ(table.EventNameToFtraceId("no_such_event"), 0ul); EXPECT_EQ(table.GetEventById(1)->name, "foo"); EXPECT_EQ(table.GetEventById(3), nullptr); EXPECT_EQ(table.GetEventById(200), nullptr); EXPECT_EQ(table.GetEventById(0), nullptr); EXPECT_EQ(table.GetEventByName("foo")->ftrace_event_id, 1u); EXPECT_THAT(*table.GetEventsByGroup("group_one"), Contains(testing::Field(&Event::name, "foo"))); EXPECT_THAT(*table.GetEventsByGroup("group_one"), Contains(testing::Field(&Event::name, "bar"))); EXPECT_THAT(*table.GetEventsByGroup("group_two"), Contains(testing::Field(&Event::name, "baz"))); EXPECT_THAT(table.GetEventsByGroup("group_three"), IsNull()); } } // namespace } // namespace perfetto