blob: b2b07dd1d3a4f37b9d98110355526051ad0c309a [file] [log] [blame]
/*
* 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/traced/probes/ftrace/proto_translation_table.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "src/traced/probes/ftrace/event_info.h"
#include "src/traced/probes/ftrace/ftrace_procfs.h"
using testing::_;
using testing::Values;
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_METHOD0(ReadPageHeaderFormat, std::string());
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/traced/probes/ftrace/test/data/" + std::string(GetParam()) + "/";
FtraceProcfs ftrace_procfs(path);
table_ = ProtoTranslationTable::Create(&ftrace_procfs, GetStaticEventInfo(),
GetStaticCommonFieldsInfo());
PERFETTO_CHECK(table_);
}
std::unique_ptr<ProtoTranslationTable> table_;
};
class TranslationTableCreationTest : public TestWithParam<uint16_t> {};
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/traced/probes/ftrace/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_P(TranslationTableCreationTest, Create) {
MockFtraceProcfs ftrace;
std::vector<Field> common_fields;
std::vector<Event> events;
ON_CALL(ftrace, ReadPageHeaderFormat())
.WillByDefault(Return(
R"( field: u64 timestamp; offset:0; size:8; signed:0;
field: local_t commit; offset:8; size:)" +
std::to_string(GetParam()) + R"(; signed:1;
field: int overwrite; offset:8; size:1; signed:1;
field: char data; offset:16; size:4080; signed:0;)"));
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));
EXPECT_EQ(table->ftrace_page_header_spec().timestamp.size, 8);
EXPECT_EQ(table->ftrace_page_header_spec().size.size, GetParam());
EXPECT_EQ(table->ftrace_page_header_spec().overwrite.size, 1);
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);
}
INSTANTIATE_TEST_CASE_P(BySize, TranslationTableCreationTest, Values(4, 8));
TEST(TranslationTableTest, InferFtraceType) {
FtraceFieldType type;
ASSERT_TRUE(InferFtraceType("char foo[16]", 16, false, &type));
EXPECT_EQ(type, kFtraceFixedCString);
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);
ASSERT_TRUE(InferFtraceType("__data_loc char[] foo", 4, false, &type));
ASSERT_EQ(type, kFtraceDataLoc);
ASSERT_FALSE(InferFtraceType("__data_loc char[] foo", 8, false, &type));
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),
ProtoTranslationTable::DefaultPageHeaderSpecForTesting());
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