aboutsummaryrefslogtreecommitdiff
path: root/src/trace_redaction/remap_scheduling_events_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/trace_redaction/remap_scheduling_events_unittest.cc')
-rw-r--r--src/trace_redaction/remap_scheduling_events_unittest.cc441
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