Trace Redaction - Drop process stats
Process stats don't expose sensitive information but they are
connected to processes. The counters exposed by them don't provide
value to redacted traces, so we drop them.
Aside from removing data "just in case", removing process stats makes
future process/thread merging (super-redaction) a lot easier because
counters don't need to be merged.
Bug: 318576499
Change-Id: Iabd80537c7ff4375b9fc6cc4104b79de242c7011
diff --git a/Android.bp b/Android.bp
index 0c8ab42..16a5a2c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12860,6 +12860,7 @@
"src/trace_redaction/prune_package_list.cc",
"src/trace_redaction/redact_sched_switch.cc",
"src/trace_redaction/scrub_ftrace_events.cc",
+ "src/trace_redaction/scrub_process_stats.cc",
"src/trace_redaction/scrub_process_trees.cc",
"src/trace_redaction/scrub_task_rename.cc",
"src/trace_redaction/scrub_trace_packet.cc",
diff --git a/src/trace_redaction/BUILD.gn b/src/trace_redaction/BUILD.gn
index f908e96..ca33a4b 100644
--- a/src/trace_redaction/BUILD.gn
+++ b/src/trace_redaction/BUILD.gn
@@ -50,6 +50,8 @@
"redact_sched_switch.h",
"scrub_ftrace_events.cc",
"scrub_ftrace_events.h",
+ "scrub_process_stats.cc",
+ "scrub_process_stats.h",
"scrub_process_trees.cc",
"scrub_process_trees.h",
"scrub_task_rename.cc",
@@ -82,6 +84,7 @@
"filter_sched_waking_events_integrationtest.cc",
"redact_sched_switch_integrationtest.cc",
"scrub_ftrace_events_integrationtest.cc",
+ "scrub_process_stats_integrationtest.cc",
"scrub_process_trees_integrationtest.cc",
"scrub_task_rename_integrationtest.cc",
"trace_redaction_integration_fixture.cc",
diff --git a/src/trace_redaction/main.cc b/src/trace_redaction/main.cc
index c0061b6..d623e63 100644
--- a/src/trace_redaction/main.cc
+++ b/src/trace_redaction/main.cc
@@ -25,6 +25,7 @@
#include "src/trace_redaction/prune_package_list.h"
#include "src/trace_redaction/redact_sched_switch.h"
#include "src/trace_redaction/scrub_ftrace_events.h"
+#include "src/trace_redaction/scrub_process_stats.h"
#include "src/trace_redaction/scrub_process_trees.h"
#include "src/trace_redaction/scrub_task_rename.h"
#include "src/trace_redaction/scrub_trace_packet.h"
@@ -60,6 +61,7 @@
redactor.emplace_transform<ScrubProcessTrees>();
redactor.emplace_transform<ScrubTaskRename>();
redactor.emplace_transform<RedactSchedSwitch>();
+ redactor.emplace_transform<ScrubProcessStats>();
Context context;
context.package_name = package_name;
diff --git a/src/trace_redaction/scrub_process_stats.cc b/src/trace_redaction/scrub_process_stats.cc
new file mode 100644
index 0000000..991c02f
--- /dev/null
+++ b/src/trace_redaction/scrub_process_stats.cc
@@ -0,0 +1,102 @@
+/*
+ * 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_process_stats.h"
+
+#include <string>
+
+#include "perfetto/base/status.h"
+#include "perfetto/protozero/field.h"
+#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "protos/perfetto/trace/ps/process_stats.pbzero.h"
+#include "src/trace_redaction/proto_util.h"
+#include "src/trace_redaction/trace_redaction_framework.h"
+
+namespace perfetto::trace_redaction {
+
+base::Status ScrubProcessStats::Transform(const Context& context,
+ std::string* packet) const {
+ if (!context.package_uid.has_value()) {
+ return base::ErrStatus("FilterProcessStats: missing package uid.");
+ }
+
+ if (!context.timeline) {
+ return base::ErrStatus("FilterProcessStats: missing timeline.");
+ }
+
+ protozero::ProtoDecoder packet_decoder(*packet);
+
+ // Very few packets will have process stats. It's best to avoid
+ // reserialization whenever possible.
+ if (!packet_decoder
+ .FindField(protos::pbzero::TracePacket::kProcessStatsFieldNumber)
+ .valid()) {
+ return base::OkStatus();
+ }
+
+ protozero::HeapBuffered<protos::pbzero::TracePacket> message;
+
+ // TODO(vaage): Add primitive to drop all packets that don't have a
+ // timestamp, allowing all other packets assume there are timestamps.
+ auto time_field = packet_decoder.FindField(
+ protos::pbzero::TracePacket::kTimestampFieldNumber);
+ PERFETTO_DCHECK(time_field.valid());
+ auto time = time_field.as_uint64();
+
+ auto* timeline = context.timeline.get();
+ auto uid = context.package_uid.value();
+
+ for (auto packet_field = packet_decoder.ReadField(); packet_field.valid();
+ packet_field = packet_decoder.ReadField()) {
+ if (packet_field.id() !=
+ protos::pbzero::TracePacket::kProcessStatsFieldNumber) {
+ proto_util::AppendField(packet_field, message.get());
+ continue;
+ }
+
+ auto process_stats = std::move(packet_field);
+ protozero::ProtoDecoder process_stats_decoder(process_stats.as_bytes());
+
+ auto* process_stats_message = message->set_process_stats();
+
+ for (auto process_stats_field = process_stats_decoder.ReadField();
+ process_stats_field.valid();
+ process_stats_field = process_stats_decoder.ReadField()) {
+ bool keep_field;
+
+ if (process_stats_field.id() ==
+ protos::pbzero::ProcessStats::kProcessesFieldNumber) {
+ protozero::ProtoDecoder process_decoder(process_stats_field.as_bytes());
+ auto pid = process_decoder.FindField(
+ protos::pbzero::ProcessStats::Process::kPidFieldNumber);
+ keep_field =
+ pid.valid() && timeline->Search(time, pid.as_int32()).uid == uid;
+ } else {
+ keep_field = true;
+ }
+
+ if (keep_field) {
+ proto_util::AppendField(process_stats_field, process_stats_message);
+ }
+ }
+ }
+
+ packet->assign(message.SerializeAsString());
+
+ return base::OkStatus();
+}
+
+} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/scrub_process_stats.h b/src/trace_redaction/scrub_process_stats.h
new file mode 100644
index 0000000..99b6697
--- /dev/null
+++ b/src/trace_redaction/scrub_process_stats.h
@@ -0,0 +1,32 @@
+/*
+ * 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_SCRUB_PROCESS_STATS_H_
+#define SRC_TRACE_REDACTION_SCRUB_PROCESS_STATS_H_
+
+#include "src/trace_redaction/trace_redaction_framework.h"
+
+namespace perfetto::trace_redaction {
+
+class ScrubProcessStats : public TransformPrimitive {
+ public:
+ base::Status Transform(const Context& context,
+ std::string* packet) const override;
+};
+
+} // namespace perfetto::trace_redaction
+
+#endif // SRC_TRACE_REDACTION_SCRUB_PROCESS_STATS_H_
diff --git a/src/trace_redaction/scrub_process_stats_integrationtest.cc b/src/trace_redaction/scrub_process_stats_integrationtest.cc
new file mode 100644
index 0000000..01c61b1
--- /dev/null
+++ b/src/trace_redaction/scrub_process_stats_integrationtest.cc
@@ -0,0 +1,142 @@
+/*
+ * 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 "perfetto/base/status.h"
+#include "src/base/test/status_matchers.h"
+#include "src/trace_redaction/build_timeline.h"
+#include "src/trace_redaction/optimize_timeline.h"
+#include "src/trace_redaction/scrub_process_stats.h"
+#include "src/trace_redaction/trace_redaction_framework.h"
+#include "src/trace_redaction/trace_redaction_integration_fixture.h"
+#include "src/trace_redaction/trace_redactor.h"
+#include "test/gtest_and_gmock.h"
+
+#include "protos/perfetto/trace/ps/process_stats.pbzero.h"
+#include "protos/perfetto/trace/trace.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto::trace_redaction {
+
+class ScrubProcessStatsTest : public testing::Test,
+ protected TraceRedactionIntegrationFixure {
+ protected:
+ void SetUp() override {
+ trace_redactor()->emplace_collect<BuildTimeline>();
+ trace_redactor()->emplace_build<OptimizeTimeline>();
+ trace_redactor()->emplace_transform<ScrubProcessStats>();
+
+ // Package "com.Unity.com.unity.multiplayer.samples.coop";
+ context()->package_uid = 10252;
+ }
+
+ // Gets pids from all process_stats messages in the trace (bytes).
+ base::FlatSet<int32_t> GetAllPids(const std::string& bytes) const {
+ base::FlatSet<int32_t> pids;
+
+ protos::pbzero::Trace::Decoder decoder(bytes);
+
+ for (auto packet = decoder.packet(); packet; ++packet) {
+ protos::pbzero::TracePacket::Decoder trace_packet(packet->as_bytes());
+
+ if (!trace_packet.has_process_stats()) {
+ continue;
+ }
+
+ protos::pbzero::ProcessStats::Decoder process_stats(
+ trace_packet.process_stats());
+
+ for (auto process = process_stats.processes(); process; ++process) {
+ protos::pbzero::ProcessStats::Process::Decoder p(process->as_bytes());
+ PERFETTO_DCHECK(p.has_pid());
+ pids.insert(p.pid());
+ }
+ }
+
+ return pids;
+ }
+};
+
+// This test is a canary for changes to the test data. If the test data was to
+// change, every test in this file would fail.
+//
+// SELECT DISTINCT pid
+// FROM process
+// WHERE upid IN (
+// SELECT DISTINCT upid
+// FROM counter
+// JOIN process_counter_track ON counter.track_id=process_counter_track.id
+// WHERE name!='oom_score_adj'
+// )
+// ORDER BY pid
+//
+// NOTE: WHERE name!='oom_score_adj' is used because there are two sources for
+// oom_score_adj values and we only want process stats here.
+TEST_F(ScrubProcessStatsTest, VerifyTraceStats) {
+ base::FlatSet<int32_t> expected = {
+ 1, 578, 581, 696, 697, 698, 699, 700, 701, 704,
+ 709, 710, 718, 728, 749, 750, 751, 752, 756, 760,
+ 761, 762, 873, 874, 892, 1046, 1047, 1073, 1074, 1091,
+ 1092, 1093, 1101, 1103, 1104, 1105, 1106, 1107, 1110, 1111,
+ 1112, 1113, 1115, 1116, 1118, 1119, 1120, 1121, 1123, 1124,
+ 1125, 1126, 1127, 1129, 1130, 1131, 1133, 1140, 1145, 1146,
+ 1147, 1151, 1159, 1163, 1164, 1165, 1166, 1167, 1168, 1175,
+ 1177, 1205, 1206, 1235, 1237, 1238, 1248, 1251, 1254, 1255,
+ 1295, 1296, 1298, 1300, 1301, 1303, 1304, 1312, 1317, 1325,
+ 1339, 1340, 1363, 1374, 1379, 1383, 1388, 1392, 1408, 1409,
+ 1410, 1413, 1422, 1426, 1427, 1428, 1429, 1433, 1436, 1448,
+ 1450, 1451, 1744, 1774, 1781, 1814, 2262, 2268, 2286, 2392,
+ 2456, 2502, 2510, 2518, 2528, 2569, 3171, 3195, 3262, 3286,
+ 3310, 3338, 3442, 3955, 4386, 4759, 5935, 6034, 6062, 6167,
+ 6547, 6573, 6720, 6721, 6725, 6944, 6984, 7105, 7207, 7557,
+ 7636, 7786, 7874, 7958, 7960, 7967, 15449, 15685, 15697, 16453,
+ 19683, 21124, 21839, 23150, 23307, 23876, 24317, 25017, 25126, 25450,
+ 25474, 27271, 30604, 32289,
+ };
+
+ auto original = LoadOriginal();
+ ASSERT_OK(original) << original.status().c_message();
+
+ auto actual = GetAllPids(*original);
+
+ for (auto pid : expected) {
+ ASSERT_TRUE(actual.count(pid))
+ << "pid " << pid << " was not found in the trace";
+ }
+
+ for (auto pid : actual) {
+ ASSERT_TRUE(expected.count(pid))
+ << "pid " << pid << " was found in the trace";
+ }
+}
+
+// Package name: "com.Unity.com.unity.multiplayer.samples.coop"
+// Package pid: 7105
+TEST_F(ScrubProcessStatsTest, OnlyKeepsStatsForPackage) {
+ auto result = Redact();
+ ASSERT_OK(result) << result.c_message();
+
+ auto redacted = LoadRedacted();
+ ASSERT_OK(redacted) << redacted.status().c_message();
+
+ auto actual = GetAllPids(*redacted);
+ ASSERT_EQ(actual.size(), 1u);
+ ASSERT_TRUE(actual.count(7105));
+}
+
+} // namespace perfetto::trace_redaction