Merge "Trace Processor - Redact Sched Waking" into main
diff --git a/Android.bp b/Android.bp
index 05b6bfb..0524616 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12823,6 +12823,7 @@
"src/trace_redaction/proto_util.cc",
"src/trace_redaction/prune_package_list.cc",
"src/trace_redaction/redact_sched_switch.cc",
+ "src/trace_redaction/redact_sched_waking.cc",
"src/trace_redaction/scrub_ftrace_events.cc",
"src/trace_redaction/scrub_process_trees.cc",
"src/trace_redaction/scrub_task_rename.cc",
@@ -12842,6 +12843,7 @@
"src/trace_redaction/proto_util_unittest.cc",
"src/trace_redaction/prune_package_list_unittest.cc",
"src/trace_redaction/redact_sched_switch_unittest.cc",
+ "src/trace_redaction/redact_sched_waking_unittest.cc",
"src/trace_redaction/scrub_ftrace_events_unittest.cc",
"src/trace_redaction/scrub_task_rename_unittest.cc",
"src/trace_redaction/scrub_trace_packet_unittest.cc",
diff --git a/src/trace_redaction/BUILD.gn b/src/trace_redaction/BUILD.gn
index ed04178..9f67428 100644
--- a/src/trace_redaction/BUILD.gn
+++ b/src/trace_redaction/BUILD.gn
@@ -44,6 +44,8 @@
"prune_package_list.h",
"redact_sched_switch.cc",
"redact_sched_switch.h",
+ "redact_sched_waking.cc",
+ "redact_sched_waking.h",
"scrub_ftrace_events.cc",
"scrub_ftrace_events.h",
"scrub_process_trees.cc",
@@ -75,6 +77,7 @@
testonly = true
sources = [
"redact_sched_switch_integrationtest.cc",
+ "redact_sched_waking_integrationtest.cc",
"scrub_ftrace_events_integrationtest.cc",
"scrub_process_trees_integrationtest.cc",
"scrub_task_rename_integrationtest.cc",
@@ -102,6 +105,7 @@
"proto_util_unittest.cc",
"prune_package_list_unittest.cc",
"redact_sched_switch_unittest.cc",
+ "redact_sched_waking_unittest.cc",
"scrub_ftrace_events_unittest.cc",
"scrub_task_rename_unittest.cc",
"scrub_trace_packet_unittest.cc",
diff --git a/src/trace_redaction/main.cc b/src/trace_redaction/main.cc
index 0b9aafe..767e90e 100644
--- a/src/trace_redaction/main.cc
+++ b/src/trace_redaction/main.cc
@@ -22,6 +22,7 @@
#include "src/trace_redaction/populate_allow_lists.h"
#include "src/trace_redaction/prune_package_list.h"
#include "src/trace_redaction/redact_sched_switch.h"
+#include "src/trace_redaction/redact_sched_waking.h"
#include "src/trace_redaction/scrub_ftrace_events.h"
#include "src/trace_redaction/scrub_process_trees.h"
#include "src/trace_redaction/scrub_task_rename.h"
@@ -52,6 +53,7 @@
redactor.transformers()->emplace_back(new ScrubProcessTrees());
redactor.transformers()->emplace_back(new ScrubTaskRename());
redactor.transformers()->emplace_back(new RedactSchedSwitch());
+ redactor.transformers()->emplace_back(new RedactSchedWaking());
Context context;
context.package_name = package_name;
diff --git a/src/trace_redaction/redact_sched_waking.cc b/src/trace_redaction/redact_sched_waking.cc
new file mode 100644
index 0000000..ce5c26d
--- /dev/null
+++ b/src/trace_redaction/redact_sched_waking.cc
@@ -0,0 +1,164 @@
+/*
+ * 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/redact_sched_waking.h"
+
+#include <string>
+
+#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
+#include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
+#include "protos/perfetto/trace/ftrace/sched.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "src/trace_redaction/proto_util.h"
+
+namespace perfetto::trace_redaction {
+
+namespace {
+
+// Redact sched waking trace events in a ftrace event bundle:
+//
+// event {
+// timestamp: 6702093787823849
+// pid: 814
+// sched_waking {
+// comm: "surfaceflinger"
+// pid: 756
+// prio: 97
+// success: 1
+// target_cpu: 2
+// }
+// }
+//
+// The three values needed are:
+//
+// 1. event.pid
+// 2. event.timestamp
+// 3. event.sched_waking.pid
+//
+// The two checks that are executed are:
+//
+// 1. package(event.pid).at(event.timestamp).is(target)
+// 2. package(event.sched_waking.pid).at(event.timestamp).is(target)
+//
+// Both must be true in order to keep an event.
+bool KeepEvent(const Context& context, protozero::Field bundle_field) {
+ PERFETTO_DCHECK(context.timeline);
+ PERFETTO_DCHECK(context.package_uid.has_value());
+
+ PERFETTO_DCHECK(bundle_field.valid());
+ PERFETTO_DCHECK(bundle_field.id() ==
+ protos::pbzero::FtraceEventBundle::kEventFieldNumber);
+
+ protozero::ProtoDecoder event_decoder(bundle_field.as_bytes());
+
+ auto sched_waking = event_decoder.FindField(
+ protos::pbzero::FtraceEvent::kSchedWakingFieldNumber);
+
+ if (!sched_waking.valid()) {
+ return true;
+ }
+
+ auto timestamp = event_decoder.FindField(
+ protos::pbzero::FtraceEvent::kTimestampFieldNumber);
+
+ if (!timestamp.valid()) {
+ return false;
+ }
+
+ auto outer_pid =
+ event_decoder.FindField(protos::pbzero::FtraceEvent::kPidFieldNumber);
+
+ if (!outer_pid.valid()) {
+ return false;
+ }
+
+ auto outer_slice = context.timeline->Search(
+ timestamp.as_uint64(), static_cast<int32_t>(outer_pid.as_uint32()));
+
+ if (outer_slice.uid != context.package_uid.value()) {
+ return false;
+ }
+
+ protozero::ProtoDecoder waking_decoder(sched_waking.as_bytes());
+
+ auto inner_pid = waking_decoder.FindField(
+ protos::pbzero::SchedWakingFtraceEvent::kPidFieldNumber);
+
+ if (!inner_pid.valid()) {
+ return false;
+ }
+
+ auto inner_slice =
+ context.timeline->Search(timestamp.as_uint64(), inner_pid.as_int32());
+ return inner_slice.uid == context.package_uid.value();
+}
+
+} // namespace
+
+base::Status RedactSchedWaking::Transform(const Context& context,
+ std::string* packet) const {
+ if (packet == nullptr || packet->empty()) {
+ return base::ErrStatus("RedactSchedWaking: null or empty packet.");
+ }
+
+ if (!context.package_uid.has_value()) {
+ return base::ErrStatus("RedactSchedWaking: missing packet uid.");
+ }
+
+ if (!context.timeline) {
+ return base::ErrStatus("RedactSchedWaking: missing timeline.");
+ }
+
+ protozero::ProtoDecoder packet_decoder(*packet);
+
+ auto trace_event_bundle = packet_decoder.FindField(
+ protos::pbzero::TracePacket::kFtraceEventsFieldNumber);
+
+ if (!trace_event_bundle.valid()) {
+ return base::OkStatus();
+ }
+
+ protozero::HeapBuffered<protos::pbzero::TracePacket> packet_message;
+ packet_message.Reset();
+
+ for (auto packet_field = packet_decoder.ReadField(); packet_field.valid();
+ packet_field = packet_decoder.ReadField()) {
+ if (packet_field.id() !=
+ protos::pbzero::TracePacket::kFtraceEventsFieldNumber) {
+ proto_util::AppendField(packet_field, packet_message.get());
+ continue;
+ }
+
+ protozero::ProtoDecoder bundle_decoder(packet_field.as_bytes());
+
+ auto* bundle_message = packet_message->set_ftrace_events();
+
+ for (auto field = bundle_decoder.ReadField(); field.valid();
+ field = bundle_decoder.ReadField()) {
+ if (field.id() != protos::pbzero::FtraceEventBundle::kEventFieldNumber ||
+ KeepEvent(context, field)) {
+ proto_util::AppendField(field, bundle_message);
+ }
+ }
+ }
+
+ *packet = packet_message.SerializeAsString();
+
+ return base::OkStatus();
+}
+
+} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/redact_sched_waking.h b/src/trace_redaction/redact_sched_waking.h
new file mode 100644
index 0000000..adb1ec8
--- /dev/null
+++ b/src/trace_redaction/redact_sched_waking.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#ifndef SRC_TRACE_REDACTION_REDACT_SCHED_WAKING_H_
+#define SRC_TRACE_REDACTION_REDACT_SCHED_WAKING_H_
+
+#include <string>
+
+#include "src/trace_redaction/trace_redaction_framework.h"
+
+namespace perfetto::trace_redaction {
+
+class RedactSchedWaking final : public TransformPrimitive {
+ public:
+ base::Status Transform(const Context& context,
+ std::string* packet) const override;
+};
+
+} // namespace perfetto::trace_redaction
+
+#endif // SRC_TRACE_REDACTION_REDACT_SCHED_WAKING_H_
diff --git a/src/trace_redaction/redact_sched_waking_integrationtest.cc b/src/trace_redaction/redact_sched_waking_integrationtest.cc
new file mode 100644
index 0000000..6b6a990
--- /dev/null
+++ b/src/trace_redaction/redact_sched_waking_integrationtest.cc
@@ -0,0 +1,219 @@
+/*
+ * 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 <cstdint>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "src/base/test/status_matchers.h"
+#include "src/base/test/tmp_dir_tree.h"
+#include "src/base/test/utils.h"
+#include "src/trace_redaction/build_timeline.h"
+#include "src/trace_redaction/find_package_uid.h"
+#include "src/trace_redaction/optimize_timeline.h"
+#include "src/trace_redaction/redact_sched_waking.h"
+#include "src/trace_redaction/trace_redaction_framework.h"
+#include "src/trace_redaction/trace_redactor.h"
+#include "test/gtest_and_gmock.h"
+
+#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
+#include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
+#include "protos/perfetto/trace/ftrace/sched.pbzero.h"
+#include "protos/perfetto/trace/trace.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto::trace_redaction {
+namespace {
+
+constexpr std::string_view kTracePath =
+ "test/data/trace-redaction-general.pftrace";
+constexpr std::string_view kPackageName =
+ "com.Unity.com.unity.multiplayer.samples.coop";
+
+class RedactSchedWakingIntegrationTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ redactor_.collectors()->emplace_back(new FindPackageUid());
+ redactor_.collectors()->emplace_back(new BuildTimeline());
+ redactor_.builders()->emplace_back(new OptimizeTimeline());
+ redactor_.transformers()->emplace_back(new RedactSchedWaking());
+
+ context_.package_name = kPackageName;
+
+ src_trace_ = base::GetTestDataPath(std::string(kTracePath));
+
+ dest_trace_ = tmp_dir_.AbsolutePath("dst.pftrace");
+ tmp_dir_.TrackFile("dst.pftrace");
+ }
+
+ base::Status Redact() {
+ return redactor_.Redact(src_trace_, dest_trace_, &context_);
+ }
+
+ base::StatusOr<std::string> LoadOriginal() const {
+ return ReadRawTrace(src_trace_);
+ }
+
+ base::StatusOr<std::string> LoadRedacted() const {
+ return ReadRawTrace(dest_trace_);
+ }
+
+ private:
+ base::StatusOr<std::string> ReadRawTrace(const std::string& path) const {
+ std::string redacted_buffer;
+
+ if (base::ReadFile(path, &redacted_buffer)) {
+ return redacted_buffer;
+ }
+
+ return base::ErrStatus("Failed to read %s", path.c_str());
+ }
+
+ Context context_;
+ TraceRedactor redactor_;
+
+ base::TmpDirTree tmp_dir_;
+
+ std::string src_trace_;
+ std::string dest_trace_;
+};
+
+// >>> SELECT uid
+// >>> FROM package_list
+// >>> WHERE package_name='com.Unity.com.unity.multiplayer.samples.coop'
+//
+// +-------+
+// | uid |
+// +-------+
+// | 10252 |
+// +-------+
+//
+// >>> SELECT uid, upid, name
+// >>> FROM process
+// >>> WHERE uid=10252
+//
+// +-------+------+----------------------------------------------+
+// | uid | upid | name |
+// +-------+------+----------------------------------------------+
+// | 10252 | 843 | com.Unity.com.unity.multiplayer.samples.coop |
+// +-------+------+----------------------------------------------+
+//
+// >>> SELECT tid, name
+// >>> FROM thread
+// >>> WHERE upid=843 AND name IS NOT NULL
+//
+// +------+-----------------+
+// | tid | name |
+// +------+-----------------+
+// | 7120 | Binder:7105_2 |
+// | 7127 | UnityMain |
+// | 7142 | Job.worker 0 |
+// | 7143 | Job.worker 1 |
+// | 7144 | Job.worker 2 |
+// | 7145 | Job.worker 3 |
+// | 7146 | Job.worker 4 |
+// | 7147 | Job.worker 5 |
+// | 7148 | Job.worker 6 |
+// | 7150 | Background Job. |
+// | 7151 | Background Job. |
+// | 7167 | UnityGfxDeviceW |
+// | 7172 | AudioTrack |
+// | 7174 | FMOD stream thr |
+// | 7180 | Binder:7105_3 |
+// | 7184 | UnityChoreograp |
+// | 7945 | Filter0 |
+// | 7946 | Filter1 |
+// | 7947 | Thread-7 |
+// | 7948 | FMOD mixer thre |
+// | 7950 | UnityGfxDeviceW |
+// | 7969 | UnityGfxDeviceW |
+// +------+-----------------+
+
+TEST_F(RedactSchedWakingIntegrationTest, OnlyKeepsPackageEvents) {
+ auto result = Redact();
+ ASSERT_OK(result) << result.c_message();
+
+ auto original = LoadOriginal();
+ ASSERT_OK(original) << original.status().c_message();
+
+ auto redacted = LoadRedacted();
+ ASSERT_OK(redacted) << redacted.status().c_message();
+
+ base::FlatHashMap<int32_t, std::string> expected_names;
+ expected_names.Insert(7120, "Binder:7105_2");
+ expected_names.Insert(7127, "UnityMain");
+ expected_names.Insert(7142, "Job.worker 0");
+ expected_names.Insert(7143, "Job.worker 1");
+ expected_names.Insert(7144, "Job.worker 2");
+ expected_names.Insert(7145, "Job.worker 3");
+ expected_names.Insert(7146, "Job.worker 4");
+ expected_names.Insert(7147, "Job.worker 5");
+ expected_names.Insert(7148, "Job.worker 6");
+ expected_names.Insert(7150, "Background Job.");
+ expected_names.Insert(7151, "Background Job.");
+ expected_names.Insert(7167, "UnityGfxDeviceW");
+ expected_names.Insert(7172, "AudioTrack");
+ expected_names.Insert(7174, "FMOD stream thr");
+ expected_names.Insert(7180, "Binder:7105_3");
+ expected_names.Insert(7184, "UnityChoreograp");
+ expected_names.Insert(7945, "Filter0");
+ expected_names.Insert(7946, "Filter1");
+ expected_names.Insert(7947, "Thread-7");
+ expected_names.Insert(7948, "FMOD mixer thre");
+ expected_names.Insert(7950, "UnityGfxDeviceW");
+ expected_names.Insert(7969, "UnityGfxDeviceW");
+
+ auto redacted_trace_data = LoadRedacted();
+ ASSERT_OK(redacted_trace_data) << redacted.status().c_message();
+
+ protos::pbzero::Trace::Decoder decoder(redacted_trace_data.value());
+
+ for (auto packet = decoder.packet(); packet; ++packet) {
+ protos::pbzero::TracePacket::Decoder packet_decoder(*packet);
+
+ if (!packet_decoder.has_ftrace_events()) {
+ continue;
+ }
+
+ protos::pbzero::FtraceEventBundle::Decoder ftrace_events_decoder(
+ packet_decoder.ftrace_events());
+
+ for (auto event = ftrace_events_decoder.event(); event; ++event) {
+ protos::pbzero::FtraceEvent::Decoder event_decoder(*event);
+
+ if (!event_decoder.has_sched_waking()) {
+ continue;
+ }
+
+ ASSERT_TRUE(event_decoder.has_pid());
+ ASSERT_TRUE(
+ expected_names.Find(static_cast<int32_t>(event_decoder.pid())));
+
+ protos::pbzero::SchedWakingFtraceEvent::Decoder waking_decoder(
+ event_decoder.sched_waking());
+
+ ASSERT_TRUE(waking_decoder.has_pid());
+ ASSERT_TRUE(expected_names.Find(waking_decoder.pid()));
+ }
+ }
+}
+
+} // namespace
+} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/redact_sched_waking_unittest.cc b/src/trace_redaction/redact_sched_waking_unittest.cc
new file mode 100644
index 0000000..a196b09
--- /dev/null
+++ b/src/trace_redaction/redact_sched_waking_unittest.cc
@@ -0,0 +1,429 @@
+/*
+ * 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/redact_sched_waking.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"
+#include "protos/perfetto/trace/trace.gen.h"
+#include "protos/perfetto/trace/trace_packet.gen.h"
+
+namespace perfetto::trace_redaction {
+namespace {
+constexpr int32_t kPackageUid = 1;
+} // namespace
+
+class RedactSchedWakingTest : public testing::Test {
+ protected:
+ void BeginBundle() { ftrace_bundle_ = trace_packet_.mutable_ftrace_events(); }
+
+ void AddWaking(uint64_t ts, int32_t pid, std::string_view comm) {
+ ASSERT_NE(ftrace_bundle_, nullptr);
+
+ auto* event = ftrace_bundle_->add_event();
+ event->set_timestamp(ts);
+
+ auto* sched_waking = event->mutable_sched_waking();
+ sched_waking->set_pid(pid);
+ sched_waking->set_comm(std::string(comm));
+ }
+
+ const RedactSchedWaking& transform() const { return transform_; }
+
+ // event {
+ // timestamp: 6702093757720043
+ // pid: 0
+ // sched_switch {
+ // prev_comm: "swapper/0"
+ // prev_pid: 0
+ // prev_prio: 120
+ // prev_state: 0
+ // next_comm: "Job.worker 5"
+ // next_pid: 7147
+ // next_prio: 120
+ // }
+ // }
+ protos::gen::FtraceEvent* CreateSchedSwitchEvent(
+ protos::gen::FtraceEvent* event) {
+ event->set_timestamp(6702093757720043);
+ event->set_pid(0);
+
+ auto* sched_switch = event->mutable_sched_switch();
+ sched_switch->set_prev_comm("swapper/0");
+ sched_switch->set_prev_pid(0);
+ sched_switch->set_prev_prio(120);
+ sched_switch->set_prev_state(0);
+ sched_switch->set_next_comm("Job.worker 6");
+ sched_switch->set_next_pid(7147);
+ sched_switch->set_next_prio(120);
+
+ return event;
+ }
+
+ // event {
+ // timestamp: 6702093757727075
+ // pid: 7147 <- This pid woke up...
+ // sched_waking {
+ // comm: "Job.worker 6"
+ // pid: 7148 <- ... this pid
+ // prio: 120
+ // success: 1
+ // target_cpu: 6
+ // }
+ // }
+ protos::gen::FtraceEvent* CreateSchedWakingEvent(
+ protos::gen::FtraceEvent* event) {
+ event->set_timestamp(6702093757727075);
+ event->set_pid(7147);
+
+ auto* sched_waking = event->mutable_sched_waking();
+ sched_waking->set_comm("Job.worker 6");
+ sched_waking->set_pid(7148);
+ sched_waking->set_prio(120);
+ sched_waking->set_success(1);
+ sched_waking->set_target_cpu(6);
+
+ return event;
+ }
+
+ private:
+ protos::gen::TracePacket trace_packet_;
+ protos::gen::FtraceEventBundle* ftrace_bundle_;
+
+ RedactSchedWaking transform_;
+};
+
+TEST_F(RedactSchedWakingTest, ReturnsErrorForNullPacket) {
+ // Don't use context_. These tests will use invalid contexts.
+ Context context;
+ context.package_uid = kPackageUid;
+ context.timeline = std::make_unique<ProcessThreadTimeline>();
+
+ ASSERT_FALSE(transform().Transform(context, nullptr).ok());
+}
+
+TEST_F(RedactSchedWakingTest, ReturnsErrorForEmptyPacket) {
+ // Don't use context_. These tests will use invalid contexts.
+ Context context;
+ context.package_uid = kPackageUid;
+ context.timeline = std::make_unique<ProcessThreadTimeline>();
+
+ std::string packet_str = "";
+
+ ASSERT_FALSE(transform().Transform(context, &packet_str).ok());
+}
+
+TEST_F(RedactSchedWakingTest, ReturnsErrorForNoTimeline) {
+ // Don't use context_. These tests will use invalid contexts.
+ Context context;
+ context.package_uid = kPackageUid;
+
+ protos::gen::TracePacket packet;
+ std::string packet_str = packet.SerializeAsString();
+
+ ASSERT_FALSE(transform().Transform(context, &packet_str).ok());
+}
+
+TEST_F(RedactSchedWakingTest, ReturnsErrorForMissingPackage) {
+ // Don't use context_. These tests will use invalid contexts.
+ Context context;
+ context.timeline = std::make_unique<ProcessThreadTimeline>();
+
+ protos::gen::TracePacket packet;
+ std::string packet_str = packet.SerializeAsString();
+
+ ASSERT_FALSE(transform().Transform(context, &packet_str).ok());
+}
+
+// Assume that the traces has a series of events like the events below. All
+// constants will come from these packets:
+//
+// event {
+// timestamp: 6702093757720043
+// pid: 0
+// sched_switch {
+// prev_comm: "swapper/0"
+// prev_pid: 0
+// prev_prio: 120
+// prev_state: 0
+// next_comm: "Job.worker 5"
+// next_pid: 7147
+// next_prio: 120
+// }
+// }
+// event {
+// timestamp: 6702093757727075
+// pid: 7147 <- This pid woke up...
+// sched_waking {
+// comm: "Job.worker 6"
+// pid: 7148 <- ... this pid
+// prio: 120
+// success: 1
+// target_cpu: 6
+// }
+// }
+//
+// The waking event is configured to be retained (see
+// KeepsWakingWhenBothPidsConnectToPackage for more information on how). Because
+// this transform only affects waking events, the sched switch event should be
+// retain.
+TEST_F(RedactSchedWakingTest, RetainsNonWakingEvents) {
+ std::string packet_str;
+
+ {
+ protos::gen::TracePacket packet;
+ auto* events = packet.mutable_ftrace_events();
+ events->set_cpu(0);
+
+ CreateSchedSwitchEvent(events->add_event());
+ CreateSchedWakingEvent(events->add_event());
+
+ packet_str = packet.SerializeAsString();
+ }
+
+ // Create a timeline where the wake-target (7147 & 7148) is connected to the
+ // target package.
+ Context context;
+ context.timeline = std::make_unique<ProcessThreadTimeline>();
+ context.package_uid = kPackageUid;
+ context.timeline->Append(ProcessThreadTimeline::Event::Open(
+ 6702093757720043, 7147, 0, kPackageUid));
+ context.timeline->Append(ProcessThreadTimeline::Event::Open(
+ 6702093757720043, 7148, 0, kPackageUid));
+ context.timeline->Sort();
+
+ ASSERT_TRUE(transform().Transform(context, &packet_str).ok());
+
+ {
+ protos::gen::TracePacket packet;
+ packet.ParseFromString(packet_str);
+
+ ASSERT_TRUE(packet.has_ftrace_events());
+
+ const protos::gen::FtraceEvent* switch_it = nullptr;
+ const protos::gen::FtraceEvent* waking_it = nullptr;
+
+ for (const auto& event : packet.ftrace_events().event()) {
+ if (event.has_sched_switch()) {
+ switch_it = &event;
+ }
+
+ if (event.has_sched_waking()) {
+ waking_it = &event;
+ }
+ }
+
+ // The sched switch event should be here because this primitive should not
+ // affect it.
+ //
+ // The sched waking event should be here because the waker and target
+ // connect to the target package.
+ ASSERT_TRUE(switch_it);
+ ASSERT_TRUE(waking_it);
+ }
+}
+
+// Assume that the traces has a series of events like the events below. All
+// constants will come from these packets:
+//
+// event {
+// timestamp: 6702093757727075
+// pid: 7147 <- This pid woke up...
+// sched_waking {
+// comm: "Job.worker 6"
+// pid: 7148 <- ... this pid
+// prio: 120
+// success: 1
+// target_cpu: 6
+// }
+// }
+//
+// Because the sched waking event pid's appears in the timeline and is connected
+// to the target package (kPackageUid), the waking even should remain.
+TEST_F(RedactSchedWakingTest, KeepsWakingWhenBothPidsConnectToPackage) {
+ std::string packet_str;
+
+ {
+ protos::gen::TracePacket packet;
+ auto* events = packet.mutable_ftrace_events();
+ events->set_cpu(0);
+
+ CreateSchedWakingEvent(events->add_event());
+
+ packet_str = packet.SerializeAsString();
+ }
+
+ // Create a timeline where the wake-target (7147 & 7148) is connected to the
+ // target package.
+ Context context;
+ context.timeline = std::make_unique<ProcessThreadTimeline>();
+ context.package_uid = kPackageUid;
+ context.timeline->Append(ProcessThreadTimeline::Event::Open(
+ 6702093757720043, 7147, 0, kPackageUid));
+ context.timeline->Append(ProcessThreadTimeline::Event::Open(
+ 6702093757720043, 7148, 0, kPackageUid));
+ context.timeline->Sort();
+
+ ASSERT_TRUE(transform().Transform(context, &packet_str).ok());
+
+ {
+ protos::gen::TracePacket packet;
+ packet.ParseFromString(packet_str);
+
+ ASSERT_TRUE(packet.has_ftrace_events());
+
+ const protos::gen::FtraceEvent* waking_it = nullptr;
+
+ for (const auto& event : packet.ftrace_events().event()) {
+ if (event.has_sched_waking()) {
+ waking_it = &event;
+ }
+ }
+
+ ASSERT_TRUE(waking_it);
+
+ const auto& waking = waking_it->sched_waking();
+
+ ASSERT_EQ(waking.comm(), "Job.worker 6");
+ ASSERT_EQ(waking.pid(), 7148);
+ ASSERT_EQ(waking.prio(), 120);
+ ASSERT_EQ(waking.success(), 1);
+ ASSERT_EQ(waking.target_cpu(), 6);
+ }
+}
+
+// Assume that the traces has a series of events like the events below. All
+// constants will come from these packets:
+//
+// event {
+// timestamp: 6702093757727075
+// pid: 7147 <- This pid woke up...
+// sched_waking {
+// comm: "Job.worker 6"
+// pid: 7148 <- ... this pid
+// prio: 120
+// success: 1
+// target_cpu: 6
+// }
+// }
+//
+// Because the only one of the sched waking events pid's appears in the
+// timeline and is connected to the target package (kPackageUid), the waking
+// even should remain.
+TEST_F(RedactSchedWakingTest, DropsWakingWhenOnlyWakerPidsConnectToPackage) {
+ std::string packet_str;
+
+ {
+ protos::gen::TracePacket packet;
+ auto* events = packet.mutable_ftrace_events();
+ events->set_cpu(0);
+
+ CreateSchedWakingEvent(events->add_event());
+
+ packet_str = packet.SerializeAsString();
+ }
+
+ // Because 7147 is not added to the timeline, the waking event should not be
+ // retained.
+ Context context;
+ context.timeline = std::make_unique<ProcessThreadTimeline>();
+ context.package_uid = kPackageUid;
+ context.timeline->Append(ProcessThreadTimeline::Event::Open(
+ 6702093757720043, 7148, 0, kPackageUid));
+ context.timeline->Sort();
+
+ ASSERT_TRUE(transform().Transform(context, &packet_str).ok());
+
+ {
+ protos::gen::TracePacket packet;
+ packet.ParseFromString(packet_str);
+
+ ASSERT_TRUE(packet.has_ftrace_events());
+
+ const protos::gen::FtraceEvent* waking_it = nullptr;
+
+ for (const auto& event : packet.ftrace_events().event()) {
+ if (event.has_sched_waking()) {
+ waking_it = &event;
+ }
+ }
+
+ ASSERT_FALSE(waking_it);
+ }
+}
+
+// Assume that the traces has a series of events like the events below. All
+// constants will come from these packets:
+//
+// event {
+// timestamp: 6702093757727075
+// pid: 7147 <- This pid woke up...
+// sched_waking {
+// comm: "Job.worker 6"
+// pid: 7148 <- ... this pid
+// prio: 120
+// success: 1
+// target_cpu: 6
+// }
+// }
+//
+// Because the only one of the sched waking events pid's appears in the
+// timeline and is connected to the target package (kPackageUid), the waking
+// even should remain.
+TEST_F(RedactSchedWakingTest, DropsWakingWhenOnlyTargetPidsConnectToPackage) {
+ std::string packet_str;
+
+ {
+ protos::gen::TracePacket packet;
+ auto* events = packet.mutable_ftrace_events();
+ events->set_cpu(0);
+
+ CreateSchedWakingEvent(events->add_event());
+
+ packet_str = packet.SerializeAsString();
+ }
+
+ // Because 7147 is not added to the timeline, the waking event should not be
+ // retained.
+ Context context;
+ context.timeline = std::make_unique<ProcessThreadTimeline>();
+ context.package_uid = kPackageUid;
+ context.timeline->Append(ProcessThreadTimeline::Event::Open(
+ 6702093757720043, 7147, 0, kPackageUid));
+ context.timeline->Sort();
+
+ ASSERT_TRUE(transform().Transform(context, &packet_str).ok());
+
+ {
+ protos::gen::TracePacket packet;
+ packet.ParseFromString(packet_str);
+
+ ASSERT_TRUE(packet.has_ftrace_events());
+
+ const protos::gen::FtraceEvent* waking_it = nullptr;
+
+ for (const auto& event : packet.ftrace_events().event()) {
+ if (event.has_sched_waking()) {
+ waking_it = &event;
+ }
+ }
+
+ ASSERT_FALSE(waking_it);
+ }
+}
+} // namespace perfetto::trace_redaction