diff options
Diffstat (limited to 'src/trace_redaction/remap_scheduling_events_unittest.cc')
-rw-r--r-- | src/trace_redaction/remap_scheduling_events_unittest.cc | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/src/trace_redaction/remap_scheduling_events_unittest.cc b/src/trace_redaction/remap_scheduling_events_unittest.cc new file mode 100644 index 000000000..5cdc75373 --- /dev/null +++ b/src/trace_redaction/remap_scheduling_events_unittest.cc @@ -0,0 +1,441 @@ +/* + * 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/remap_scheduling_events.h" + +#include "perfetto/protozero/scattered_heap_buffer.h" +#include "src/base/test/status_matchers.h" +#include "src/trace_redaction/trace_redaction_framework.h" +#include "test/gtest_and_gmock.h" + +#include "protos/perfetto/trace/ftrace/ftrace_event.gen.h" +#include "protos/perfetto/trace/ftrace/ftrace_event_bundle.gen.h" +#include "protos/perfetto/trace/ftrace/sched.gen.h" + +namespace perfetto::trace_redaction { + +template <class T> +class ThreadMergeTest { + protected: + struct Process { + uint64_t uid; + int32_t ppid; + int32_t pid; + }; + + base::Status Redact(protos::pbzero::FtraceEvent* event_message) { + T redact; + + auto bundle_str = bundle_.SerializeAsString(); + protos::pbzero::FtraceEventBundle::Decoder bundle_decoder(bundle_str); + + auto event_str = bundle_.event().back().SerializeAsString(); + protos::pbzero::FtraceEvent::Decoder event_decoder(event_str); + + return redact.Redact(context_, bundle_decoder, event_decoder, + event_message); + } + + Context context_; + protos::gen::FtraceEventBundle bundle_; +}; + +// All ftrace events have a timestamp and a pid. This test focuses on the +// event's pid value. When that pid doesn't belong to the target package, it +// should be replaced with a synthetic thread id. +// +// event { +// timestamp: 6702093743539938 +// pid: 0 +// sched_switch { ... } +// } +class ThreadMergeRemapFtraceEventPidTest + : public testing::Test, + protected ThreadMergeTest<ThreadMergeRemapFtraceEventPid> { + protected: + static constexpr uint32_t kCpu = 3; + + static constexpr auto kTimestamp = 123456789; + + // This process will be connected to the target package. + static constexpr Process kProcess = {12, 5, 7}; + + // This process will not be connected to the target package. + static constexpr Process kOtherProcess = {120, 50, 70}; + + void SetUp() override { + bundle_.add_event(); + + context_.package_uid = kProcess.uid; + + context_.timeline = std::make_unique<ProcessThreadTimeline>(); + context_.timeline->Append(ProcessThreadTimeline::Event::Open( + 0, kProcess.pid, kProcess.ppid, kProcess.uid)); + context_.timeline->Append(ProcessThreadTimeline::Event::Open( + 0, kOtherProcess.pid, kOtherProcess.ppid, kOtherProcess.uid)); + context_.timeline->Sort(); + + // Because kCpu is 3, it means that there are four CPUs (id 0, id 1, ...). + context_.synthetic_threads.emplace(); + context_.synthetic_threads->tids.assign({100, 101, 102, 103}); + } +}; + +// This should never happen, a bundle should always have a cpu. If it doesn't +// have a CPU, the event field should be dropped (safest option). +// +// TODO(vaage): This will create an invalid trace. It can also leak information +// if other primitives don't strip the remaining information. To be safe, these +// cases should be replaced with errors. +TEST_F(ThreadMergeRemapFtraceEventPidTest, MissingCpuReturnsError) { + // Do not call set_cpu(uint32_t value). There should be no cpu for this case. + bundle_.mutable_event()->back().set_timestamp(kTimestamp); + bundle_.mutable_event()->back().set_pid(kProcess.pid); + + protozero::HeapBuffered<protos::pbzero::FtraceEvent> event_message; + ASSERT_FALSE(Redact(event_message.get()).ok()); +} + +// This should never happen, an event should always have a timestamp. If it +// doesn't have a timestamp, the event field should be dropped (safest option). +// +// TODO(vaage): This will create an invalid trace. It can also leak information +// if other primitives don't strip the remaining information. To be safe, these +// cases should be replaced with errors. +TEST_F(ThreadMergeRemapFtraceEventPidTest, MissingTimestampReturnsError) { + bundle_.set_cpu(kCpu); + // Do not call set_timestamp(uint64_t value). There should be no timestamp for + // this case. + bundle_.mutable_event()->back().set_pid(kProcess.pid); + + protozero::HeapBuffered<protos::pbzero::FtraceEvent> event_message; + ASSERT_FALSE(Redact(event_message.get()).ok()); +} + +TEST_F(ThreadMergeRemapFtraceEventPidTest, NoopWhenPidIsInPackage) { + bundle_.set_cpu(kCpu); + bundle_.mutable_event()->back().set_timestamp(kTimestamp); + bundle_.mutable_event()->back().set_pid(kProcess.pid); + + protozero::HeapBuffered<protos::pbzero::FtraceEvent> event_message; + ASSERT_OK(Redact(event_message.get())); + + protos::gen::FtraceEvent event; + event.ParseFromString(event_message.SerializeAsString()); + + ASSERT_TRUE(event.has_pid()); + ASSERT_EQ(static_cast<int32_t>(event.pid()), kProcess.pid); +} + +TEST_F(ThreadMergeRemapFtraceEventPidTest, ChangesPidWhenPidIsOutsidePackage) { + bundle_.set_cpu(kCpu); // The CPU is used to select the pid. + bundle_.mutable_event()->back().set_timestamp(kTimestamp); + bundle_.mutable_event()->back().set_pid(kOtherProcess.pid); + + protozero::HeapBuffered<protos::pbzero::FtraceEvent> event_message; + ASSERT_OK(Redact(event_message.get())); + + protos::gen::FtraceEvent event; + event.ParseFromString(event_message.SerializeAsString()); + + ASSERT_TRUE(event.has_pid()); + ASSERT_EQ(static_cast<int32_t>(event.pid()), + context_.synthetic_threads->tids[kCpu]); +} + +// When creating a sched_switch event, the event pid and the previous pid should +// be the same pid. +// +// event { +// timestamp: 6702093743539938 +// pid: 0 +// sched_switch { +// prev_comm: "swapper/7" +// prev_pid: 0 +// prev_prio: 120 +// prev_state: 0 +// next_comm: "FMOD stream thr" +// next_pid: 7174 +// next_prio: 104 +// } +// } +class ThreadMergeRemapSchedSwitchPidTest + : public testing::Test, + protected ThreadMergeTest<ThreadMergeRemapSchedSwitchPid> { + protected: + static constexpr uint32_t kCpu = 3; + + static constexpr auto kTimestamp = 123456789; + + // This process will be connected to the target package. + static constexpr Process kPrevProcess = {12, 5, 7}; + static constexpr Process kNextProcess = {12, 5, 8}; + + // This process will not be connected to the target package. + static constexpr Process kOtherProcess = {120, 50, 70}; + + void SetUp() override { + bundle_.add_event(); + + context_.package_uid = kPrevProcess.uid; + + context_.timeline = std::make_unique<ProcessThreadTimeline>(); + context_.timeline->Append(ProcessThreadTimeline::Event::Open( + 0, kPrevProcess.pid, kPrevProcess.ppid, kPrevProcess.uid)); + context_.timeline->Append(ProcessThreadTimeline::Event::Open( + 0, kNextProcess.pid, kNextProcess.ppid, kNextProcess.uid)); + context_.timeline->Append(ProcessThreadTimeline::Event::Open( + 0, kOtherProcess.pid, kOtherProcess.ppid, kOtherProcess.uid)); + + context_.timeline->Sort(); + + // Because kCpu is 3, it means that there are four CPUs (id 0, id 1, ...). + context_.synthetic_threads.emplace(); + context_.synthetic_threads->tids.assign({100, 101, 102, 103}); + } +}; + +// This should never happen, a bundle should always have a cpu. If it doesn't +// have a CPU, the event field should be dropped (safest option). +// +// TODO(vaage): This will create an invalid trace. It can also leak information +// if other primitives don't strip the remaining information. To be safe, these +// cases should be replaced with errors. +TEST_F(ThreadMergeRemapSchedSwitchPidTest, MissingCpuReturnsError) { + // Do not call set_cpu(uint32_t value). There should be no cpu for this case. + bundle_.mutable_event()->back().set_timestamp(kTimestamp); + bundle_.mutable_event()->back().set_pid(kPrevProcess.pid); + + protozero::HeapBuffered<protos::pbzero::FtraceEvent> event_message; + ASSERT_FALSE(Redact(event_message.get()).ok()); +} + +// This should never happen, an event should always have a timestamp. If it +// doesn't have a timestamp, the event field should be dropped (safest option). +// +// TODO(vaage): This will create an invalid trace. It can also leak information +// if other primitives don't strip the remaining information. To be safe, these +// cases should be replaced with errors. +TEST_F(ThreadMergeRemapSchedSwitchPidTest, MissingTimestampReturnsError) { + bundle_.set_cpu(kCpu); + // Do not call set_timestamp(uint64_t value). There should be no timestamp for + // this case. + bundle_.mutable_event()->back().set_pid(kPrevProcess.pid); + + protozero::HeapBuffered<protos::pbzero::FtraceEvent> event_message; + ASSERT_FALSE(Redact(event_message.get()).ok()); +} + +TEST_F(ThreadMergeRemapSchedSwitchPidTest, NoopWhenPidIsInPackage) { + bundle_.set_cpu(kCpu); + bundle_.mutable_event()->back().set_timestamp(kTimestamp); + bundle_.mutable_event()->back().set_pid(kPrevProcess.pid); + + auto* sched_switch = bundle_.mutable_event()->back().mutable_sched_switch(); + sched_switch->set_prev_pid(kPrevProcess.pid); + sched_switch->set_next_pid(kNextProcess.pid); + + protozero::HeapBuffered<protos::pbzero::FtraceEvent> event_message; + ASSERT_OK(Redact(event_message.get())); + + protos::gen::FtraceEvent event; + event.ParseFromString(event_message.SerializeAsString()); + + ASSERT_TRUE(event.has_sched_switch()); + + ASSERT_TRUE(event.sched_switch().has_prev_pid()); + ASSERT_EQ(static_cast<int32_t>(event.sched_switch().prev_pid()), + kPrevProcess.pid); + + ASSERT_TRUE(event.sched_switch().has_next_pid()); + ASSERT_EQ(static_cast<int32_t>(event.sched_switch().next_pid()), + kNextProcess.pid); +} + +TEST_F(ThreadMergeRemapSchedSwitchPidTest, + ChangesPrevPidWhenPidIsOutsidePackage) { + bundle_.set_cpu(kCpu); + bundle_.mutable_event()->back().set_timestamp(kTimestamp); + bundle_.mutable_event()->back().set_pid(kPrevProcess.pid); + + auto* sched_switch = bundle_.mutable_event()->back().mutable_sched_switch(); + sched_switch->set_prev_pid(kOtherProcess.pid); + sched_switch->set_next_pid(kNextProcess.pid); + + protozero::HeapBuffered<protos::pbzero::FtraceEvent> event_message; + ASSERT_OK(Redact(event_message.get())); + + protos::gen::FtraceEvent event; + event.ParseFromString(event_message.SerializeAsString()); + + ASSERT_TRUE(event.has_sched_switch()); + + ASSERT_TRUE(event.sched_switch().has_prev_pid()); + ASSERT_EQ(static_cast<int32_t>(event.sched_switch().prev_pid()), + context_.synthetic_threads->tids[kCpu]); + + ASSERT_TRUE(event.sched_switch().has_next_pid()); + ASSERT_EQ(static_cast<int32_t>(event.sched_switch().next_pid()), + kNextProcess.pid); +} + +TEST_F(ThreadMergeRemapSchedSwitchPidTest, + ChangesNextPidWhenPidIsOutsidePackage) { + bundle_.set_cpu(kCpu); + bundle_.mutable_event()->back().set_timestamp(kTimestamp); + bundle_.mutable_event()->back().set_pid(kPrevProcess.pid); + + auto* sched_switch = bundle_.mutable_event()->back().mutable_sched_switch(); + sched_switch->set_prev_pid(kPrevProcess.pid); + sched_switch->set_next_pid(kOtherProcess.pid); + + protozero::HeapBuffered<protos::pbzero::FtraceEvent> event_message; + ASSERT_OK(Redact(event_message.get())); + + protos::gen::FtraceEvent event; + event.ParseFromString(event_message.SerializeAsString()); + + ASSERT_TRUE(event.has_sched_switch()); + + ASSERT_TRUE(event.sched_switch().has_prev_pid()); + ASSERT_EQ(static_cast<int32_t>(event.sched_switch().prev_pid()), + kPrevProcess.pid); + + ASSERT_TRUE(event.sched_switch().has_next_pid()); + ASSERT_EQ(static_cast<int32_t>(event.sched_switch().next_pid()), + context_.synthetic_threads->tids[kCpu]); +} + +// event { +// timestamp: 6702093743527386 +// pid: 0 +// sched_waking { +// comm: "FMOD stream thr" +// pid: 7174 +// prio: 104 +// success: 1 +// target_cpu: 7 +// } +// } +class ThreadMergeRemapSchedWakingPidTest + : public testing::Test, + protected ThreadMergeTest<ThreadMergeRemapSchedWakingPid> { + protected: + static constexpr uint32_t kCpu = 3; + + static constexpr auto kTimestamp = 123456789; + + // This process will be connected to the target package. + static constexpr Process kWakerProcess = {12, 5, 7}; + static constexpr Process kWakeTarget = {12, 5, 8}; + + // This process will not be connected to the target package. + static constexpr Process kOtherProcess = {120, 50, 70}; + + void SetUp() override { + bundle_.add_event(); + + context_.package_uid = kWakerProcess.uid; + + context_.timeline = std::make_unique<ProcessThreadTimeline>(); + context_.timeline->Append(ProcessThreadTimeline::Event::Open( + 0, kWakerProcess.pid, kWakerProcess.ppid, kWakerProcess.uid)); + context_.timeline->Append(ProcessThreadTimeline::Event::Open( + 0, kWakeTarget.pid, kWakeTarget.ppid, kWakeTarget.uid)); + context_.timeline->Append(ProcessThreadTimeline::Event::Open( + 0, kOtherProcess.pid, kOtherProcess.ppid, kOtherProcess.uid)); + + context_.timeline->Sort(); + + // Because kCpu is 3, it means that there are four CPUs (id 0, id 1, ...). + context_.synthetic_threads.emplace(); + context_.synthetic_threads->tids.assign({100, 101, 102, 103}); + } +}; + +// This should never happen, a bundle should always have a cpu. If it doesn't +// have a CPU, the event field should be dropped (safest option). +// +// TODO(vaage): This will create an invalid trace. It can also leak information +// if other primitives don't strip the remaining information. To be safe, these +// cases should be replaced with errors. +TEST_F(ThreadMergeRemapSchedWakingPidTest, MissingCpuReturnsError) { + // Do not call set_cpu(uint32_t value). There should be no cpu for this case. + bundle_.mutable_event()->back().set_timestamp(kTimestamp); + bundle_.mutable_event()->back().set_pid(kWakerProcess.pid); + + protozero::HeapBuffered<protos::pbzero::FtraceEvent> event_message; + ASSERT_FALSE(Redact(event_message.get()).ok()); +} + +// This should never happen, an event should always have a timestamp. If it +// doesn't have a timestamp, the event field should be dropped (safest option). +// +// TODO(vaage): This will create an invalid trace. It can also leak information +// if other primitives don't strip the remaining information. To be safe, these +// cases should be replaced with errors. +TEST_F(ThreadMergeRemapSchedWakingPidTest, MissingTimestampReturnsError) { + bundle_.set_cpu(kCpu); + // Do not call set_timestamp(uint64_t value). There should be no timestamp for + // this case. + bundle_.mutable_event()->back().set_pid(kWakerProcess.pid); + + protozero::HeapBuffered<protos::pbzero::FtraceEvent> event_message; + ASSERT_FALSE(Redact(event_message.get()).ok()); +} + +TEST_F(ThreadMergeRemapSchedWakingPidTest, NoopWhenPidIsInPackage) { + bundle_.set_cpu(kCpu); + bundle_.mutable_event()->back().set_timestamp(kTimestamp); + bundle_.mutable_event()->back().set_pid(kWakerProcess.pid); + + auto* sched_waking = bundle_.mutable_event()->back().mutable_sched_waking(); + sched_waking->set_pid(kWakeTarget.pid); + + protozero::HeapBuffered<protos::pbzero::FtraceEvent> event_message; + ASSERT_OK(Redact(event_message.get())); + + protos::gen::FtraceEvent event; + event.ParseFromString(event_message.SerializeAsString()); + + ASSERT_TRUE(event.has_sched_waking()); + + ASSERT_TRUE(event.sched_waking().has_pid()); + ASSERT_EQ(static_cast<int32_t>(event.sched_waking().pid()), kWakeTarget.pid); +} + +TEST_F(ThreadMergeRemapSchedWakingPidTest, ChangesPidWhenPidIsOutsidePackage) { + bundle_.set_cpu(kCpu); + bundle_.mutable_event()->back().set_timestamp(kTimestamp); + bundle_.mutable_event()->back().set_pid(kWakerProcess.pid); + + auto* sched_switch = bundle_.mutable_event()->back().mutable_sched_waking(); + sched_switch->set_pid(kOtherProcess.pid); + + protozero::HeapBuffered<protos::pbzero::FtraceEvent> event_message; + ASSERT_OK(Redact(event_message.get())); + + protos::gen::FtraceEvent event; + event.ParseFromString(event_message.SerializeAsString()); + + ASSERT_TRUE(event.has_sched_waking()); + + ASSERT_TRUE(event.sched_waking().has_pid()); + ASSERT_EQ(static_cast<int32_t>(event.sched_waking().pid()), + context_.synthetic_threads->tids[kCpu]); +} + +} // namespace perfetto::trace_redaction |