blob: 179c50427bc5e7ade8a4f5ab71238cc6662d2489 [file] [log] [blame]
/*
* Copyright (C) 2024 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/trace_redaction/scrub_ftrace_events.h"
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "protos/perfetto/trace/ftrace/power.pbzero.h"
#include "src/base/test/status_matchers.h"
#include "test/gtest_and_gmock.h"
#include "protos/perfetto/trace/ftrace/ftrace_event.gen.h"
#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
#include "protos/perfetto/trace/ftrace/ftrace_event_bundle.gen.h"
#include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
#include "protos/perfetto/trace/ftrace/power.gen.h"
#include "protos/perfetto/trace/ftrace/task.gen.h"
#include "protos/perfetto/trace/ps/process_tree.gen.h"
#include "protos/perfetto/trace/trace_packet.gen.h"
namespace perfetto::trace_redaction {
namespace {
// task_rename should be in the allow-list.
void AddTaskRename(protos::gen::FtraceEventBundle* bundle,
int32_t pid,
const std::string& old_comm,
const std::string& new_comm) {
auto* e = bundle->add_event();
e->mutable_task_rename()->set_pid(pid);
e->mutable_task_rename()->set_oldcomm(old_comm);
e->mutable_task_rename()->set_newcomm(new_comm);
}
void AddClockSetRate(protos::gen::FtraceEventBundle* bundle,
uint64_t cpu,
const std::string& name,
uint64_t state) {
auto* e = bundle->add_event();
e->mutable_clock_set_rate()->set_cpu_id(cpu);
e->mutable_clock_set_rate()->set_name(name);
e->mutable_clock_set_rate()->set_state(state);
}
} // namespace
class ScrubFtraceEventsSerializationTest : public testing::Test {
public:
ScrubFtraceEventsSerializationTest() = default;
~ScrubFtraceEventsSerializationTest() override = default;
protected:
void SetUp() override {
std::array bytes = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
};
protozero::HeapBuffered<protozero::Message> msg;
msg->AppendFixed<uint32_t>(field_id_fixed_32, 7);
msg->AppendFixed<uint64_t>(field_id_fixed_64, 9);
msg->AppendVarInt(field_id_var_int, 723);
msg->AppendBytes(field_id_bytes, bytes.data(), bytes.size());
message_.assign(msg.SerializeAsString());
}
std::string message_;
static constexpr uint32_t field_id_fixed_32 = 0;
static constexpr uint32_t field_id_fixed_64 = 1;
static constexpr uint32_t field_id_var_int = 2;
static constexpr uint32_t field_id_bytes = 3;
};
TEST_F(ScrubFtraceEventsSerializationTest, AppendFixedUint32) {
protozero::ProtoDecoder decoder(message_);
const auto& field = decoder.FindField(field_id_fixed_32);
std::string expected = "";
field.SerializeAndAppendTo(&expected);
protozero::HeapBuffered<protozero::Message> actual;
ScrubFtraceEvents::AppendField(field, actual.get());
ASSERT_EQ(actual.SerializeAsString(), expected);
}
TEST_F(ScrubFtraceEventsSerializationTest, AppendFixedUint64) {
protozero::ProtoDecoder decoder(message_);
const auto& field = decoder.FindField(field_id_fixed_64);
std::string expected = "";
field.SerializeAndAppendTo(&expected);
protozero::HeapBuffered<protozero::Message> actual;
ScrubFtraceEvents::AppendField(field, actual.get());
ASSERT_EQ(actual.SerializeAsString(), expected);
}
TEST_F(ScrubFtraceEventsSerializationTest, AppendBytes) {
protozero::ProtoDecoder decoder(message_);
const auto& field = decoder.FindField(field_id_bytes);
std::string expected = "";
field.SerializeAndAppendTo(&expected);
protozero::HeapBuffered<protozero::Message> actual;
ScrubFtraceEvents::AppendField(field, actual.get());
ASSERT_EQ(actual.SerializeAsString(), expected);
}
TEST_F(ScrubFtraceEventsSerializationTest, AppendVarInt) {
protozero::ProtoDecoder decoder(message_);
const auto& field = decoder.FindField(field_id_var_int);
std::string expected = "";
field.SerializeAndAppendTo(&expected);
protozero::HeapBuffered<protozero::Message> actual;
ScrubFtraceEvents::AppendField(field, actual.get());
ASSERT_EQ(actual.SerializeAsString(), expected);
}
TEST(ScrubFtraceEventsTest, ReturnErrorForNullPacket) {
// Have something in the allow-list to avoid that error.
Context context;
context.ftrace_packet_allow_list = {
protos::pbzero::FtraceEvent::kTaskRenameFieldNumber};
ScrubFtraceEvents scrub;
ASSERT_FALSE(scrub.Transform(context, nullptr).ok());
}
TEST(ScrubFtraceEventsTest, ReturnErrorForEmptyPacket) {
// Have something in the allow-list to avoid that error.
Context context;
context.ftrace_packet_allow_list = {
protos::pbzero::FtraceEvent::kTaskRenameFieldNumber};
std::string packet_str = "";
ScrubFtraceEvents scrub;
ASSERT_FALSE(scrub.Transform(context, &packet_str).ok());
}
TEST(ScrubFtraceEventsTest, ReturnErrorForEmptyAllowList) {
// The context will have no allow-list entries. ScrubFtraceEvents should fail.
Context context;
protos::gen::TracePacket packet;
std::string packet_str = packet.SerializeAsString();
ScrubFtraceEvents scrub;
ASSERT_FALSE(scrub.Transform(context, &packet_str).ok());
}
TEST(ScrubFtraceEventsTest, IgnorePacketWithNoFtraceEvents) {
protos::gen::TracePacket trace_packet;
auto* tree = trace_packet.mutable_process_tree();
auto& process = tree->mutable_processes()->emplace_back();
process.set_pid(1);
process.set_ppid(2);
process.set_uid(3);
auto& thread = tree->mutable_threads()->emplace_back();
thread.set_name("hello world");
thread.set_tgid(1);
thread.set_tid(135);
auto original_packet = trace_packet.SerializeAsString();
auto packet = original_packet;
Context context;
context.ftrace_packet_allow_list = {
protos::pbzero::FtraceEvent::kTaskRenameFieldNumber};
ScrubFtraceEvents transform;
ASSERT_OK(transform.Transform(context, &packet));
// The packet doesn't have any ftrace events. It should not be affected by
// this transform.
ASSERT_EQ(original_packet, packet);
}
// There are some values in a ftrace event that sits behind the ftrace bundle.
// These values should be retained.
TEST(ScrubFtraceEventsTest, KeepsFtraceBundleSiblingValues) {
protos::gen::TracePacket trace_packet;
auto* ftrace_events = trace_packet.mutable_ftrace_events();
ftrace_events->set_cpu(7);
AddTaskRename(ftrace_events, 7, "old_comm", "new_comm_7");
AddClockSetRate(ftrace_events, 7, "cool cpu name", 1);
auto original_packet = trace_packet.SerializeAsString();
auto packet = original_packet;
Context context;
context.ftrace_packet_allow_list = {
protos::pbzero::FtraceEvent::kTaskRenameFieldNumber};
ScrubFtraceEvents transform;
ASSERT_OK(transform.Transform(context, &packet));
protos::gen::TracePacket gen_packet;
gen_packet.ParseFromString(packet);
ASSERT_TRUE(gen_packet.has_ftrace_events());
const auto& gen_events = gen_packet.ftrace_events();
// Because the CPU sits beside the event list, and not inside the event list,
// the CPU value should be retained.
ASSERT_TRUE(gen_events.has_cpu());
ASSERT_EQ(gen_events.cpu(), 7u);
// ClockSetRate should be dropped. Only TaskRename should remain.
ASSERT_EQ(gen_events.event_size(), 1);
ASSERT_FALSE(gen_events.event().front().has_clock_set_rate());
ASSERT_TRUE(gen_events.event().front().has_task_rename());
}
TEST(ScrubFtraceEventsTest, KeepsAllowedEvents) {
Context context;
context.ftrace_packet_allow_list = {
protos::pbzero::FtraceEvent::kTaskRenameFieldNumber,
};
protos::gen::TracePacket before;
AddTaskRename(before.mutable_ftrace_events(), 7, "old_comm", "new_comm_7");
AddTaskRename(before.mutable_ftrace_events(), 8, "old_comm", "new_comm_8");
AddTaskRename(before.mutable_ftrace_events(), 9, "old_comm", "new_comm_9");
auto before_str = before.SerializeAsString();
auto after_str = before_str;
ScrubFtraceEvents transform;
ASSERT_OK(transform.Transform(context, &after_str));
protos::gen::TracePacket after;
after.ParseFromString(after_str);
// Implementation detail: ScrubFtraceEvents may change entry order. The diff
// must be order independent. Sort the events by pid, this will make it easier
// to assert values.
auto events = after.ftrace_events().event();
std::sort(events.begin(), events.end(),
[](const auto& l, const auto& r) { return l.pid() < r.pid(); });
ASSERT_EQ(events.size(), 3u);
ASSERT_TRUE(events[0].has_task_rename());
ASSERT_EQ(events[0].task_rename().pid(), 7);
ASSERT_EQ(events[0].task_rename().oldcomm(), "old_comm");
ASSERT_EQ(events[0].task_rename().newcomm(), "new_comm_7");
ASSERT_TRUE(events[1].has_task_rename());
ASSERT_EQ(events[1].task_rename().pid(), 8);
ASSERT_EQ(events[1].task_rename().oldcomm(), "old_comm");
ASSERT_EQ(events[1].task_rename().newcomm(), "new_comm_8");
ASSERT_TRUE(events[2].has_task_rename());
ASSERT_EQ(events[2].task_rename().pid(), 9);
ASSERT_EQ(events[2].task_rename().oldcomm(), "old_comm");
ASSERT_EQ(events[2].task_rename().newcomm(), "new_comm_9");
}
// Only the specific non-allowed events should be removed from the event list.
TEST(ScrubFtraceEventsTest, OnlyDropsNotAllowedEvents) {
// AddTaskRename >> Keep
// AddClockSetRate >> Drop
protos::gen::TracePacket original_packet;
AddTaskRename(original_packet.mutable_ftrace_events(), 7, "old_comm",
"new_comm_7");
AddClockSetRate(original_packet.mutable_ftrace_events(), 0, "cool cpu name",
1);
AddTaskRename(original_packet.mutable_ftrace_events(), 8, "old_comm",
"new_comm_8");
AddTaskRename(original_packet.mutable_ftrace_events(), 9, "old_comm",
"new_comm_9");
auto packet = original_packet.SerializeAsString();
Context context;
context.ftrace_packet_allow_list = {
protos::pbzero::FtraceEvent::kTaskRenameFieldNumber};
ScrubFtraceEvents transform;
ASSERT_OK(transform.Transform(context, &packet));
protos::gen::TracePacket modified_packet;
ASSERT_TRUE(modified_packet.ParseFromString(packet));
// Only the clock set rate event should have been removed (drop 1 of the 4
// events).
ASSERT_TRUE(modified_packet.has_ftrace_events());
ASSERT_EQ(modified_packet.ftrace_events().event_size(), 3);
// All ftrace events should be rename events.
const auto& events = modified_packet.ftrace_events().event();
ASSERT_TRUE(events.at(0).has_task_rename());
ASSERT_EQ(events.at(0).task_rename().pid(), 7);
ASSERT_TRUE(events.at(1).has_task_rename());
ASSERT_EQ(events.at(1).task_rename().pid(), 8);
ASSERT_TRUE(events.at(2).has_task_rename());
ASSERT_EQ(events.at(2).task_rename().pid(), 9);
}
} // namespace perfetto::trace_redaction