Merge "tp: rewrite span join to remove dependency on SqliteTable" into main
diff --git a/Android.bp b/Android.bp
index dce7dbc..0c8ab42 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12850,6 +12850,8 @@
name: "perfetto_src_trace_redaction_trace_redaction",
srcs: [
"src/trace_redaction/build_timeline.cc",
+ "src/trace_redaction/filter_ftrace_using_allowlist.cc",
+ "src/trace_redaction/filter_sched_waking_events.cc",
"src/trace_redaction/find_package_uid.cc",
"src/trace_redaction/optimize_timeline.cc",
"src/trace_redaction/populate_allow_lists.cc",
@@ -12857,7 +12859,6 @@
"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",
@@ -12872,13 +12873,13 @@
name: "perfetto_src_trace_redaction_unittests",
srcs: [
"src/trace_redaction/build_timeline_unittest.cc",
+ "src/trace_redaction/filter_ftrace_using_allowlist_unittest.cc",
+ "src/trace_redaction/filter_sched_waking_events_unittest.cc",
"src/trace_redaction/find_package_uid_unittest.cc",
"src/trace_redaction/process_thread_timeline_unittest.cc",
"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/infra/ci/frontend/static/script.js b/infra/ci/frontend/static/script.js
index a2e9dc6..24591d4 100644
--- a/infra/ci/frontend/static/script.js
+++ b/infra/ci/frontend/static/script.js
@@ -231,7 +231,7 @@
return lastUpdateMins + ' mins ago';
if (lastUpdateMins < 60 * 24)
return Math.ceil(lastUpdateMins / 60) + ' hours ago';
- return lastUpdate.toLocaleDateString();
+ return lastUpdate.toISOString().substr(0, 10);
}
function renderCLRow(cl) {
diff --git a/src/trace_redaction/BUILD.gn b/src/trace_redaction/BUILD.gn
index 9f67428..f908e96 100644
--- a/src/trace_redaction/BUILD.gn
+++ b/src/trace_redaction/BUILD.gn
@@ -30,6 +30,10 @@
sources = [
"build_timeline.cc",
"build_timeline.h",
+ "filter_ftrace_using_allowlist.cc",
+ "filter_ftrace_using_allowlist.h",
+ "filter_sched_waking_events.cc",
+ "filter_sched_waking_events.h",
"find_package_uid.cc",
"find_package_uid.h",
"optimize_timeline.cc",
@@ -44,8 +48,6 @@
"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",
@@ -76,11 +78,14 @@
source_set("integrationtests") {
testonly = true
sources = [
+ "filter_ftrace_using_allowlist_integrationtest.cc",
+ "filter_sched_waking_events_integrationtest.cc",
"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",
+ "trace_redaction_integration_fixture.cc",
+ "trace_redaction_integration_fixture.h",
"trace_redactor_integrationtest.cc",
]
deps = [
@@ -100,13 +105,13 @@
testonly = true
sources = [
"build_timeline_unittest.cc",
+ "filter_ftrace_using_allowlist_unittest.cc",
+ "filter_sched_waking_events_unittest.cc",
"find_package_uid_unittest.cc",
"process_thread_timeline_unittest.cc",
"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/filter_ftrace_using_allowlist.cc b/src/trace_redaction/filter_ftrace_using_allowlist.cc
new file mode 100644
index 0000000..f32ddeb
--- /dev/null
+++ b/src/trace_redaction/filter_ftrace_using_allowlist.cc
@@ -0,0 +1,51 @@
+/*
+ * 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/filter_ftrace_using_allowlist.h"
+
+#include "perfetto/base/status.h"
+#include "perfetto/protozero/field.h"
+#include "perfetto/protozero/proto_decoder.h"
+
+namespace perfetto::trace_redaction {
+
+base::Status FilterFtraceUsingAllowlist::VerifyContext(
+ const Context& context) const {
+ if (context.ftrace_packet_allow_list.empty()) {
+ return base::ErrStatus(
+ "FilterFtraceUsingAllowlist: missing ftrace allowlist.");
+ }
+
+ return base::OkStatus();
+}
+
+bool FilterFtraceUsingAllowlist::KeepEvent(const Context& context,
+ protozero::ConstBytes bytes) const {
+ PERFETTO_DCHECK(!context.ftrace_packet_allow_list.empty());
+
+ protozero::ProtoDecoder event(bytes);
+
+ for (auto field = event.ReadField(); field.valid();
+ field = event.ReadField()) {
+ if (context.ftrace_packet_allow_list.count(field.id()) != 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/filter_ftrace_using_allowlist.h b/src/trace_redaction/filter_ftrace_using_allowlist.h
new file mode 100644
index 0000000..661da68
--- /dev/null
+++ b/src/trace_redaction/filter_ftrace_using_allowlist.h
@@ -0,0 +1,49 @@
+/*
+ * 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_FILTER_FTRACE_USING_ALLOWLIST_H_
+#define SRC_TRACE_REDACTION_FILTER_FTRACE_USING_ALLOWLIST_H_
+
+#include "perfetto/protozero/field.h"
+#include "src/trace_redaction/scrub_ftrace_events.h"
+#include "src/trace_redaction/trace_redaction_framework.h"
+
+namespace perfetto::trace_redaction {
+
+// event {
+// timestamp: 6702094168934980
+// pid: 7127
+// sched_waking { <-- event type
+// comm: "Job.worker 1"
+// pid: 7143
+// prio: 120
+// success: 1
+// target_cpu: 7
+// }
+// }
+//
+// Check if the event type appears in the ftrace allow-list. If it doesn't
+// appear there, then mark the event as "don't keep".
+class FilterFtraceUsingAllowlist : public FtraceEventFilter {
+ public:
+ base::Status VerifyContext(const Context& context) const override;
+ bool KeepEvent(const Context& context,
+ protozero::ConstBytes bytes) const override;
+};
+
+} // namespace perfetto::trace_redaction
+
+#endif // SRC_TRACE_REDACTION_FILTER_FTRACE_USING_ALLOWLIST_H_
diff --git a/src/trace_redaction/filter_ftrace_using_allowlist_integrationtest.cc b/src/trace_redaction/filter_ftrace_using_allowlist_integrationtest.cc
new file mode 100644
index 0000000..c0abf3f
--- /dev/null
+++ b/src/trace_redaction/filter_ftrace_using_allowlist_integrationtest.cc
@@ -0,0 +1,209 @@
+/*
+ * 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 "perfetto/ext/base/file_utils.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/filter_ftrace_using_allowlist.h"
+#include "src/trace_redaction/populate_allow_lists.h"
+#include "src/trace_redaction/scrub_ftrace_events.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/trace.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto::trace_redaction {
+
+class FilterFtraceUsingAllowlistTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ redactor_.emplace_build<PopulateAllowlists>();
+
+ auto* scrub_ftrace_events =
+ redactor_.emplace_transform<ScrubFtraceEvents>();
+ scrub_ftrace_events->emplace_back<FilterFtraceUsingAllowlist>();
+
+ src_trace_ =
+ base::GetTestDataPath("test/data/trace-redaction-general.pftrace");
+
+ dest_trace_ = tmp_dir_.AbsolutePath("dst.pftrace");
+ }
+
+ base::Status Redact() {
+ auto status = redactor_.Redact(src_trace_, dest_trace_, &context_);
+
+ // If redaction failed, the redactor should not have written the file to
+ // disk.
+ if (status.ok()) {
+ tmp_dir_.TrackFile("dst.pftrace");
+ }
+
+ return status;
+ }
+
+ base::StatusOr<std::string> LoadOriginal() const {
+ return ReadRawTrace(src_trace_);
+ }
+
+ base::StatusOr<std::string> LoadRedacted() const {
+ return ReadRawTrace(dest_trace_);
+ }
+
+ // Parse the given buffer and gather field ids from across all events. This
+ // will also include fields like timestamp.
+ base::FlatSet<uint32_t> ParseEvents(std::string trace_buffer) {
+ base::FlatSet<uint32_t> event_ids;
+
+ protos::pbzero::Trace::Decoder trace_decoder(trace_buffer);
+
+ for (auto packet = trace_decoder.packet(); packet; ++packet) {
+ protos::pbzero::TracePacket::Decoder packet_decoder(*packet);
+
+ if (!packet_decoder.has_ftrace_events()) {
+ continue;
+ }
+
+ protos::pbzero::FtraceEventBundle::Decoder bundle_decoder(
+ packet_decoder.ftrace_events());
+
+ for (auto event = bundle_decoder.event(); event; ++event) {
+ protozero::ProtoDecoder event_decoder(*event);
+
+ for (auto field = event_decoder.ReadField(); field.valid();
+ field = event_decoder.ReadField()) {
+ event_ids.insert(field.id());
+ }
+ }
+ }
+
+ return event_ids;
+ }
+
+ 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_;
+};
+
+// This is not a test of FilterFtraceUsingAllowlist, but instead verifies of the
+// sample trace used in the test.
+TEST_F(FilterFtraceUsingAllowlistTest, TraceHasAllEvents) {
+ auto trace = LoadOriginal();
+ ASSERT_OK(trace) << trace->c_str();
+
+ auto events = ParseEvents(std::move(trace.value()));
+ ASSERT_EQ(events.size(), 14u);
+
+ ASSERT_TRUE(
+ events.count(protos::pbzero::FtraceEvent::kCpuFrequencyFieldNumber));
+ ASSERT_TRUE(events.count(protos::pbzero::FtraceEvent::kCpuIdleFieldNumber));
+ ASSERT_TRUE(
+ events.count(protos::pbzero::FtraceEvent::kOomScoreAdjUpdateFieldNumber));
+ ASSERT_TRUE(events.count(protos::pbzero::FtraceEvent::kPidFieldNumber));
+ ASSERT_TRUE(events.count(protos::pbzero::FtraceEvent::kPrintFieldNumber));
+ ASSERT_TRUE(
+ events.count(protos::pbzero::FtraceEvent::kSchedProcessExitFieldNumber));
+ ASSERT_TRUE(
+ events.count(protos::pbzero::FtraceEvent::kSchedProcessFreeFieldNumber));
+ ASSERT_TRUE(
+ events.count(protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber));
+ ASSERT_TRUE(
+ events.count(protos::pbzero::FtraceEvent::kSchedWakeupFieldNumber));
+ ASSERT_TRUE(
+ events.count(protos::pbzero::FtraceEvent::kSchedWakeupNewFieldNumber));
+ ASSERT_TRUE(
+ events.count(protos::pbzero::FtraceEvent::kSchedWakingFieldNumber));
+ ASSERT_TRUE(
+ events.count(protos::pbzero::FtraceEvent::kTaskNewtaskFieldNumber));
+ ASSERT_TRUE(
+ events.count(protos::pbzero::FtraceEvent::kTaskRenameFieldNumber));
+ ASSERT_TRUE(events.count(protos::pbzero::FtraceEvent::kTimestampFieldNumber));
+}
+
+TEST_F(FilterFtraceUsingAllowlistTest, RetainsAllowedEvents) {
+ auto redacted = Redact();
+ ASSERT_OK(redacted) << redacted.c_message();
+
+ auto trace = LoadRedacted();
+ ASSERT_OK(trace) << trace.status().c_message();
+
+ auto events = ParseEvents(std::move(trace.value()));
+
+ // These are not events, they are fields that exist alongside the event.
+ ASSERT_TRUE(events.count(protos::pbzero::FtraceEvent::kPidFieldNumber));
+ ASSERT_TRUE(events.count(protos::pbzero::FtraceEvent::kTimestampFieldNumber));
+
+ // These are events.
+ ASSERT_TRUE(
+ events.count(protos::pbzero::FtraceEvent::kCpuFrequencyFieldNumber));
+ ASSERT_TRUE(events.count(protos::pbzero::FtraceEvent::kCpuIdleFieldNumber));
+ ASSERT_TRUE(
+ events.count(protos::pbzero::FtraceEvent::kSchedProcessFreeFieldNumber));
+ ASSERT_TRUE(
+ events.count(protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber));
+ ASSERT_TRUE(
+ events.count(protos::pbzero::FtraceEvent::kSchedWakingFieldNumber));
+ ASSERT_TRUE(
+ events.count(protos::pbzero::FtraceEvent::kTaskNewtaskFieldNumber));
+ ASSERT_TRUE(
+ events.count(protos::pbzero::FtraceEvent::kTaskRenameFieldNumber));
+}
+
+TEST_F(FilterFtraceUsingAllowlistTest, RemovesNotAllowedEvents) {
+ auto redacted = Redact();
+ ASSERT_OK(redacted) << redacted.c_message();
+
+ auto trace = LoadRedacted();
+ ASSERT_OK(trace) << trace.status().c_message();
+
+ auto events = ParseEvents(std::move(trace.value()));
+
+ // These are events.
+ ASSERT_FALSE(
+ events.count(protos::pbzero::FtraceEvent::kOomScoreAdjUpdateFieldNumber));
+ ASSERT_FALSE(events.count(protos::pbzero::FtraceEvent::kPrintFieldNumber));
+ ASSERT_FALSE(
+ events.count(protos::pbzero::FtraceEvent::kSchedProcessExitFieldNumber));
+ ASSERT_FALSE(
+ events.count(protos::pbzero::FtraceEvent::kSchedWakeupFieldNumber));
+ ASSERT_FALSE(
+ events.count(protos::pbzero::FtraceEvent::kSchedWakeupNewFieldNumber));
+}
+
+} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/scrub_ftrace_events_unittest.cc b/src/trace_redaction/filter_ftrace_using_allowlist_unittest.cc
similarity index 86%
rename from src/trace_redaction/scrub_ftrace_events_unittest.cc
rename to src/trace_redaction/filter_ftrace_using_allowlist_unittest.cc
index 5b5012f..6f6672a 100644
--- a/src/trace_redaction/scrub_ftrace_events_unittest.cc
+++ b/src/trace_redaction/filter_ftrace_using_allowlist_unittest.cc
@@ -15,6 +15,7 @@
*/
#include "src/trace_redaction/scrub_ftrace_events.h"
+#include "src/trace_redaction/filter_ftrace_using_allowlist.h"
#include "protos/perfetto/trace/ftrace/power.gen.h"
#include "src/base/test/status_matchers.h"
#include "test/gtest_and_gmock.h"
@@ -30,12 +31,12 @@
namespace perfetto::trace_redaction {
// Tests which nested messages and fields are removed.
-class ScrubFtraceEventsTest : public testing::Test {
- public:
- ScrubFtraceEventsTest() = default;
- ~ScrubFtraceEventsTest() override = default;
-
+class FilterFtraceUsingAllowlistTest : public testing::Test {
protected:
+ void SetUp() override {
+ transform_.emplace_back<FilterFtraceUsingAllowlist>();
+ }
+
// task_rename should be in the allow-list.
static void AddTaskRename(protos::gen::FtraceEventBundle* bundle,
int32_t pid,
@@ -56,19 +57,20 @@
e->mutable_clock_set_rate()->set_name(name);
e->mutable_clock_set_rate()->set_state(state);
}
+
+ ScrubFtraceEvents transform_;
};
-TEST_F(ScrubFtraceEventsTest, ReturnErrorForNullPacket) {
+TEST_F(FilterFtraceUsingAllowlistTest, 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());
+ ASSERT_FALSE(transform_.Transform(context, nullptr).ok());
}
-TEST_F(ScrubFtraceEventsTest, ReturnErrorForEmptyPacket) {
+TEST_F(FilterFtraceUsingAllowlistTest, ReturnErrorForEmptyPacket) {
// Have something in the allow-list to avoid that error.
Context context;
context.ftrace_packet_allow_list = {
@@ -76,22 +78,20 @@
std::string packet_str = "";
- ScrubFtraceEvents scrub;
- ASSERT_FALSE(scrub.Transform(context, &packet_str).ok());
+ ASSERT_FALSE(transform_.Transform(context, &packet_str).ok());
}
-TEST_F(ScrubFtraceEventsTest, ReturnErrorForEmptyAllowList) {
+TEST_F(FilterFtraceUsingAllowlistTest, 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());
+ ASSERT_FALSE(transform_.Transform(context, &packet_str).ok());
}
-TEST_F(ScrubFtraceEventsTest, IgnorePacketWithNoFtraceEvents) {
+TEST_F(FilterFtraceUsingAllowlistTest, IgnorePacketWithNoFtraceEvents) {
protos::gen::TracePacket trace_packet;
auto* tree = trace_packet.mutable_process_tree();
@@ -112,8 +112,8 @@
context.ftrace_packet_allow_list = {
protos::pbzero::FtraceEvent::kTaskRenameFieldNumber};
- ScrubFtraceEvents transform;
- ASSERT_OK(transform.Transform(context, &packet));
+ auto transform_status = transform_.Transform(context, &packet);
+ ASSERT_OK(transform_status) << transform_status.c_message();
// The packet doesn't have any ftrace events. It should not be affected by
// this transform.
@@ -122,7 +122,7 @@
// There are some values in a ftrace event that sits behind the ftrace bundle.
// These values should be retained.
-TEST_F(ScrubFtraceEventsTest, KeepsFtraceBundleSiblingValues) {
+TEST_F(FilterFtraceUsingAllowlistTest, KeepsFtraceBundleSiblingValues) {
protos::gen::TracePacket trace_packet;
auto* ftrace_events = trace_packet.mutable_ftrace_events();
@@ -137,8 +137,7 @@
context.ftrace_packet_allow_list = {
protos::pbzero::FtraceEvent::kTaskRenameFieldNumber};
- ScrubFtraceEvents transform;
- ASSERT_OK(transform.Transform(context, &packet));
+ ASSERT_OK(transform_.Transform(context, &packet));
protos::gen::TracePacket gen_packet;
gen_packet.ParseFromString(packet);
@@ -157,7 +156,7 @@
ASSERT_TRUE(gen_events.event().front().has_task_rename());
}
-TEST_F(ScrubFtraceEventsTest, KeepsAllowedEvents) {
+TEST_F(FilterFtraceUsingAllowlistTest, KeepsAllowedEvents) {
Context context;
context.ftrace_packet_allow_list = {
protos::pbzero::FtraceEvent::kTaskRenameFieldNumber,
@@ -171,8 +170,7 @@
auto before_str = before.SerializeAsString();
auto after_str = before_str;
- ScrubFtraceEvents transform;
- ASSERT_OK(transform.Transform(context, &after_str));
+ ASSERT_OK(transform_.Transform(context, &after_str));
protos::gen::TracePacket after;
after.ParseFromString(after_str);
@@ -203,7 +201,7 @@
}
// Only the specific non-allowed events should be removed from the event list.
-TEST_F(ScrubFtraceEventsTest, OnlyDropsNotAllowedEvents) {
+TEST_F(FilterFtraceUsingAllowlistTest, OnlyDropsNotAllowedEvents) {
// AddTaskRename >> Keep
// AddClockSetRate >> Drop
protos::gen::TracePacket original_packet;
@@ -221,8 +219,7 @@
context.ftrace_packet_allow_list = {
protos::pbzero::FtraceEvent::kTaskRenameFieldNumber};
- ScrubFtraceEvents transform;
- ASSERT_OK(transform.Transform(context, &packet));
+ ASSERT_OK(transform_.Transform(context, &packet));
protos::gen::TracePacket modified_packet;
ASSERT_TRUE(modified_packet.ParseFromString(packet));
diff --git a/src/trace_redaction/filter_sched_waking_events.cc b/src/trace_redaction/filter_sched_waking_events.cc
new file mode 100644
index 0000000..aded8fc
--- /dev/null
+++ b/src/trace_redaction/filter_sched_waking_events.cc
@@ -0,0 +1,88 @@
+/*
+ * 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/filter_sched_waking_events.h"
+
+#include "protos/perfetto/trace/ftrace/sched.pbzero.h"
+
+#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
+#include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
+
+namespace perfetto::trace_redaction {
+
+base::Status FilterSchedWakingEvents::VerifyContext(
+ const Context& context) const {
+ if (!context.package_uid.has_value()) {
+ return base::ErrStatus("FilterSchedWakingEvents: missing packet uid.");
+ }
+
+ if (!context.timeline) {
+ return base::ErrStatus("FilterSchedWakingEvents: missing timeline.");
+ }
+
+ return base::OkStatus();
+}
+
+bool FilterSchedWakingEvents::KeepEvent(const Context& context,
+ protozero::ConstBytes bytes) const {
+ PERFETTO_DCHECK(context.package_uid.has_value());
+ PERFETTO_DCHECK(context.timeline);
+
+ protozero::ProtoDecoder event_decoder(bytes);
+
+ auto sched_waking = event_decoder.FindField(
+ protos::pbzero::FtraceEvent::kSchedWakingFieldNumber);
+
+ if (!sched_waking.valid()) {
+ return true; // Keep
+ }
+
+ auto timestamp = event_decoder.FindField(
+ protos::pbzero::FtraceEvent::kTimestampFieldNumber);
+
+ if (!timestamp.valid()) {
+ return false; // Remove
+ }
+
+ auto outer_pid =
+ event_decoder.FindField(protos::pbzero::FtraceEvent::kPidFieldNumber);
+
+ if (!outer_pid.valid()) {
+ return false; // Remove
+ }
+
+ 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; // Remove
+ }
+
+ protozero::ProtoDecoder waking_decoder(sched_waking.as_bytes());
+
+ auto inner_pid = waking_decoder.FindField(
+ protos::pbzero::SchedWakingFtraceEvent::kPidFieldNumber);
+
+ if (!inner_pid.valid()) {
+ return false; // Remove
+ }
+
+ auto inner_slice =
+ context.timeline->Search(timestamp.as_uint64(), inner_pid.as_int32());
+ return inner_slice.uid == context.package_uid.value();
+}
+
+} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/filter_sched_waking_events.h b/src/trace_redaction/filter_sched_waking_events.h
new file mode 100644
index 0000000..5d33ed6
--- /dev/null
+++ b/src/trace_redaction/filter_sched_waking_events.h
@@ -0,0 +1,61 @@
+/*
+ * 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_FILTER_SCHED_WAKING_EVENTS_H_
+#define SRC_TRACE_REDACTION_FILTER_SCHED_WAKING_EVENTS_H_
+
+#include "perfetto/protozero/field.h"
+#include "src/trace_redaction/scrub_ftrace_events.h"
+#include "src/trace_redaction/trace_redaction_framework.h"
+
+namespace perfetto::trace_redaction {
+
+// Redact sched waking trace events in a ftrace event bundle:
+//
+// event {
+// timestamp: 6702093787823849
+// pid: 814 <-- waker
+// sched_waking {
+// comm: "surfaceflinger"
+// pid: 756 <-- target
+// 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.
+class FilterSchedWakingEvents : public FtraceEventFilter {
+ public:
+ base::Status VerifyContext(const Context& context) const override;
+ bool KeepEvent(const Context& context,
+ protozero::ConstBytes bytes) const override;
+};
+
+} // namespace perfetto::trace_redaction
+
+#endif // SRC_TRACE_REDACTION_FILTER_SCHED_WAKING_EVENTS_H_
diff --git a/src/trace_redaction/redact_sched_waking_integrationtest.cc b/src/trace_redaction/filter_sched_waking_events_integrationtest.cc
similarity index 81%
rename from src/trace_redaction/redact_sched_waking_integrationtest.cc
rename to src/trace_redaction/filter_sched_waking_events_integrationtest.cc
index b83f7e9..08bc991 100644
--- a/src/trace_redaction/redact_sched_waking_integrationtest.cc
+++ b/src/trace_redaction/filter_sched_waking_events_integrationtest.cc
@@ -26,10 +26,12 @@
#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/filter_sched_waking_events.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/scrub_ftrace_events.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"
@@ -42,57 +44,24 @@
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 {
+class RedactSchedWakingIntegrationTest
+ : public testing::Test,
+ protected TraceRedactionIntegrationFixure {
protected:
void SetUp() override {
- redactor_.emplace_collect<FindPackageUid>();
- redactor_.emplace_collect<BuildTimeline>();
- redactor_.emplace_build<OptimizeTimeline>();
- redactor_.emplace_transform<RedactSchedWaking>();
+ trace_redactor()->emplace_collect<FindPackageUid>();
+ trace_redactor()->emplace_collect<BuildTimeline>();
+ trace_redactor()->emplace_build<OptimizeTimeline>();
- context_.package_name = kPackageName;
+ auto* ftrace_filter =
+ trace_redactor()->emplace_transform<ScrubFtraceEvents>();
+ ftrace_filter->emplace_back<FilterSchedWakingEvents>();
- src_trace_ = base::GetTestDataPath(std::string(kTracePath));
-
- dest_trace_ = tmp_dir_.AbsolutePath("dst.pftrace");
- tmp_dir_.TrackFile("dst.pftrace");
+ context()->package_name = kPackageName;
}
-
- 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
diff --git a/src/trace_redaction/redact_sched_waking_unittest.cc b/src/trace_redaction/filter_sched_waking_events_unittest.cc
similarity index 91%
rename from src/trace_redaction/redact_sched_waking_unittest.cc
rename to src/trace_redaction/filter_sched_waking_events_unittest.cc
index a196b09..891a86a 100644
--- a/src/trace_redaction/redact_sched_waking_unittest.cc
+++ b/src/trace_redaction/filter_sched_waking_events_unittest.cc
@@ -14,22 +14,24 @@
* 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/ftrace_event.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"
+#include "protos/perfetto/trace/trace.gen.h"
+#include "src/trace_redaction/filter_sched_waking_events.h"
+#include "src/trace_redaction/scrub_ftrace_events.h"
+#include "test/gtest_and_gmock.h"
namespace perfetto::trace_redaction {
namespace {
constexpr int32_t kPackageUid = 1;
} // namespace
-class RedactSchedWakingTest : public testing::Test {
+class FilterSchedWakingEventsTest : public testing::Test {
protected:
+ void SetUp() override { transform_.emplace_back<FilterSchedWakingEvents>(); }
+
void BeginBundle() { ftrace_bundle_ = trace_packet_.mutable_ftrace_events(); }
void AddWaking(uint64_t ts, int32_t pid, std::string_view comm) {
@@ -43,7 +45,7 @@
sched_waking->set_comm(std::string(comm));
}
- const RedactSchedWaking& transform() const { return transform_; }
+ const ScrubFtraceEvents& transform() const { return transform_; }
// event {
// timestamp: 6702093757720043
@@ -105,10 +107,10 @@
protos::gen::TracePacket trace_packet_;
protos::gen::FtraceEventBundle* ftrace_bundle_;
- RedactSchedWaking transform_;
+ ScrubFtraceEvents transform_;
};
-TEST_F(RedactSchedWakingTest, ReturnsErrorForNullPacket) {
+TEST_F(FilterSchedWakingEventsTest, ReturnsErrorForNullPacket) {
// Don't use context_. These tests will use invalid contexts.
Context context;
context.package_uid = kPackageUid;
@@ -117,7 +119,7 @@
ASSERT_FALSE(transform().Transform(context, nullptr).ok());
}
-TEST_F(RedactSchedWakingTest, ReturnsErrorForEmptyPacket) {
+TEST_F(FilterSchedWakingEventsTest, ReturnsErrorForEmptyPacket) {
// Don't use context_. These tests will use invalid contexts.
Context context;
context.package_uid = kPackageUid;
@@ -128,7 +130,7 @@
ASSERT_FALSE(transform().Transform(context, &packet_str).ok());
}
-TEST_F(RedactSchedWakingTest, ReturnsErrorForNoTimeline) {
+TEST_F(FilterSchedWakingEventsTest, ReturnsErrorForNoTimeline) {
// Don't use context_. These tests will use invalid contexts.
Context context;
context.package_uid = kPackageUid;
@@ -139,7 +141,7 @@
ASSERT_FALSE(transform().Transform(context, &packet_str).ok());
}
-TEST_F(RedactSchedWakingTest, ReturnsErrorForMissingPackage) {
+TEST_F(FilterSchedWakingEventsTest, ReturnsErrorForMissingPackage) {
// Don't use context_. These tests will use invalid contexts.
Context context;
context.timeline = std::make_unique<ProcessThreadTimeline>();
@@ -182,7 +184,7 @@
// KeepsWakingWhenBothPidsConnectToPackage for more information on how). Because
// this transform only affects waking events, the sched switch event should be
// retain.
-TEST_F(RedactSchedWakingTest, RetainsNonWakingEvents) {
+TEST_F(FilterSchedWakingEventsTest, RetainsNonWakingEvents) {
std::string packet_str;
{
@@ -255,7 +257,7 @@
//
// 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) {
+TEST_F(FilterSchedWakingEventsTest, KeepsWhenBothPidsConnectToPackage) {
std::string packet_str;
{
@@ -322,10 +324,10 @@
// }
// }
//
-// Because the only one of the sched waking events pid's appears in the
+// Because 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) {
+// even should be removed.
+TEST_F(FilterSchedWakingEventsTest, DropWhenOnlyWakerConnectsToPackage) {
std::string packet_str;
{
@@ -385,7 +387,7 @@
// 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) {
+TEST_F(FilterSchedWakingEventsTest, DropWhenOnlyTargetConnectsToPackage) {
std::string packet_str;
{
diff --git a/src/trace_redaction/main.cc b/src/trace_redaction/main.cc
index ea728bc..c0061b6 100644
--- a/src/trace_redaction/main.cc
+++ b/src/trace_redaction/main.cc
@@ -17,12 +17,13 @@
#include "perfetto/base/logging.h"
#include "perfetto/base/status.h"
#include "src/trace_redaction/build_timeline.h"
+#include "src/trace_redaction/filter_ftrace_using_allowlist.h"
+#include "src/trace_redaction/filter_sched_waking_events.h"
#include "src/trace_redaction/find_package_uid.h"
#include "src/trace_redaction/optimize_timeline.h"
#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"
@@ -49,11 +50,16 @@
// Add all transforms.
redactor.emplace_transform<PrunePackageList>();
redactor.emplace_transform<ScrubTracePacket>();
- redactor.emplace_transform<ScrubFtraceEvents>();
+
+ // Scrub ftrace events before other ftrace events in order to reduce the
+ // number of events they need to iterate over.
+ auto scrub_ftrace_events = redactor.emplace_transform<ScrubFtraceEvents>();
+ scrub_ftrace_events->emplace_back<FilterFtraceUsingAllowlist>();
+ scrub_ftrace_events->emplace_back<FilterSchedWakingEvents>();
+
redactor.emplace_transform<ScrubProcessTrees>();
redactor.emplace_transform<ScrubTaskRename>();
redactor.emplace_transform<RedactSchedSwitch>();
- redactor.emplace_transform<RedactSchedWaking>();
Context context;
context.package_name = package_name;
diff --git a/src/trace_redaction/populate_allow_lists.cc b/src/trace_redaction/populate_allow_lists.cc
index fbfe279..2ba81b6 100644
--- a/src/trace_redaction/populate_allow_lists.cc
+++ b/src/trace_redaction/populate_allow_lists.cc
@@ -29,6 +29,15 @@
return base::ErrStatus("Trace packet allow-list should be empty.");
}
+ // TRACE PACKET NOTES
+ //
+ // protos::pbzero::TracePacket::kAndroidSystemPropertyFieldNumber
+ //
+ // AndroidSystemProperty exposes a key-value pair structure with no
+ // constraints around keys or values, making fine-grain redaction
+ // difficult. Because this packet's value has no measurable, the safest
+ // option to drop the whole packet.
+
context->trace_packet_allow_list = {
protos::pbzero::TracePacket::kProcessTreeFieldNumber,
protos::pbzero::TracePacket::kProcessStatsFieldNumber,
@@ -42,7 +51,6 @@
protos::pbzero::TracePacket::kServiceEventFieldNumber,
protos::pbzero::TracePacket::kInitialDisplayStateFieldNumber,
protos::pbzero::TracePacket::kFrameTimelineEventFieldNumber,
- protos::pbzero::TracePacket::kAndroidSystemPropertyFieldNumber,
protos::pbzero::TracePacket::kSynchronizationMarkerFieldNumber,
protos::pbzero::TracePacket::kFtraceEventsFieldNumber,
@@ -75,11 +83,12 @@
// without additional redaction. This list should be configured in a build
// primitive so that they can be optionally included.
//
+ // protos::pbzero::FtraceEvent::kPrintFieldNumber,
+ //
// TODO: Some fields will create new packets (e.g. binder calls may create
// new spans. This is currently not supported (generated packets still
// need to be redacted).
//
- // protos::pbzero::FtraceEvent::kPrintFieldNumber,
// protos::pbzero::FtraceEvent::kBinderTransactionFieldNumber,
// protos::pbzero::FtraceEvent::kBinderTransactionReceivedFieldNumber,
// protos::pbzero::FtraceEvent::kBinderSetPriorityFieldNumber,
diff --git a/src/trace_redaction/redact_sched_switch_integrationtest.cc b/src/trace_redaction/redact_sched_switch_integrationtest.cc
index 26dd400..9480977 100644
--- a/src/trace_redaction/redact_sched_switch_integrationtest.cc
+++ b/src/trace_redaction/redact_sched_switch_integrationtest.cc
@@ -16,20 +16,16 @@
#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_switch.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"
@@ -40,59 +36,19 @@
#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 RedactSchedSwitchIntegrationTest : public testing::Test {
+class RedactSchedSwitchIntegrationTest
+ : public testing::Test,
+ protected TraceRedactionIntegrationFixure {
protected:
void SetUp() override {
- redactor_.emplace_collect<FindPackageUid>();
- redactor_.emplace_collect<BuildTimeline>();
- redactor_.emplace_build<OptimizeTimeline>();
- redactor_.emplace_transform<RedactSchedSwitch>();
+ trace_redactor()->emplace_collect<FindPackageUid>();
+ trace_redactor()->emplace_collect<BuildTimeline>();
+ trace_redactor()->emplace_build<OptimizeTimeline>();
+ trace_redactor()->emplace_transform<RedactSchedSwitch>();
- context_.package_name = kPackageName;
-
- src_trace_ = base::GetTestDataPath(std::string(kTracePath));
-
- dest_trace_ = tmp_dir_.AbsolutePath("dst.pftrace");
- tmp_dir_.TrackFile("dst.pftrace");
+ context()->package_name = "com.Unity.com.unity.multiplayer.samples.coop";
}
-
- 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
@@ -233,5 +189,4 @@
}
}
-} // namespace
} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/redact_sched_waking.cc b/src/trace_redaction/redact_sched_waking.cc
deleted file mode 100644
index ce5c26d..0000000
--- a/src/trace_redaction/redact_sched_waking.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * 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
deleted file mode 100644
index adb1ec8..0000000
--- a/src/trace_redaction/redact_sched_waking.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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/scrub_ftrace_events.cc b/src/trace_redaction/scrub_ftrace_events.cc
index b41fc6e..3260c71 100644
--- a/src/trace_redaction/scrub_ftrace_events.cc
+++ b/src/trace_redaction/scrub_ftrace_events.cc
@@ -25,67 +25,15 @@
#include "protos/perfetto/trace/trace.pbzero.h"
namespace perfetto::trace_redaction {
-namespace {
-constexpr auto kFtraceEventsFieldNumber =
- protos::pbzero::TracePacket::kFtraceEventsFieldNumber;
-
-constexpr auto kEventFieldNumber =
- protos::pbzero::FtraceEventBundle::kEventFieldNumber;
-
-enum class Redact : uint8_t {
- // Some resources in the target need to be redacted.
- kSomething = 0,
-
- // No resources in the target need to be redacted.
- kNothing = 1,
-};
-
-// Return kSomething if an event will change after redaction . If a packet
-// will not change, then the packet should skip redaction and be appended
-// to the output.
-//
-// Event packets have few packets (e.g. timestamp, pid, the event payload).
-// because of this, it is relatively cheap to test a packet.
-//
-// event {
-// timestamp: 6702095044306682
-// pid: 0
-// sched_switch {
-// prev_comm: "swapper/2"
-// prev_pid: 0
-// prev_prio: 120
-// prev_state: 0
-// next_comm: "surfaceflinger"
-// next_pid: 819
-// next_prio: 120
-// }
-// }
-Redact ProbeEvent(const Context& context, const protozero::Field& event) {
- if (event.id() != kEventFieldNumber) {
- PERFETTO_FATAL("Invalid proto field. Expected kEventFieldNumber.");
- }
-
- protozero::ProtoDecoder decoder(event.data(), event.size());
-
- for (auto field = decoder.ReadField(); field.valid();
- field = decoder.ReadField()) {
- if (context.ftrace_packet_allow_list.count(field.id()) != 0) {
- return Redact::kNothing;
- }
- }
-
- return Redact::kSomething;
-}
-
-} // namespace
+FtraceEventFilter::~FtraceEventFilter() = default;
// packet {
// ftrace_events {
// event { <-- This is where we test the allow-list
// timestamp: 6702095044299807
// pid: 0
-// cpu_idle { <-- This is the event data (allow-list)
+// cpu_idle { <-- This is the event type
// state: 4294967295
// cpu_id: 2
// }
@@ -95,58 +43,63 @@
base::Status ScrubFtraceEvents::Transform(const Context& context,
std::string* packet) const {
if (packet == nullptr || packet->empty()) {
- return base::ErrStatus("Cannot scrub null or empty trace packet.");
+ return base::ErrStatus("FilterPrintEvents: null or empty packet.");
}
- if (context.ftrace_packet_allow_list.empty()) {
- return base::ErrStatus("Cannot scrub ftrace packets, missing allow-list.");
+ for (const auto& filter : filters_) {
+ auto status = filter->VerifyContext(context);
+
+ if (!status.ok()) {
+ return status;
+ }
}
- // If the packet has no ftrace events, skip it, leaving it unmodified.
- protos::pbzero::TracePacket::Decoder query(*packet);
- if (!query.has_ftrace_events()) {
+ protozero::ProtoDecoder packet_decoder(*packet);
+
+ if (!packet_decoder
+ .FindField(protos::pbzero::TracePacket::kFtraceEventsFieldNumber)
+ .valid()) {
return base::OkStatus();
}
- protozero::HeapBuffered<protos::pbzero::TracePacket> packet_msg;
+ protozero::HeapBuffered<protos::pbzero::TracePacket> packet_message;
- // packet.foreach_child.foreach( ... )
- protozero::ProtoDecoder d_packet(*packet);
- for (auto packet_child_it = d_packet.ReadField(); packet_child_it.valid();
- packet_child_it = d_packet.ReadField()) {
- // packet.child_not<ftrace_events>( ).do ( ... )
- if (packet_child_it.id() != kFtraceEventsFieldNumber) {
- proto_util::AppendField(packet_child_it, packet_msg.get());
+ for (auto field = packet_decoder.ReadField(); field.valid();
+ field = packet_decoder.ReadField()) {
+ if (field.id() != protos::pbzero::TracePacket::kFtraceEventsFieldNumber) {
+ proto_util::AppendField(field, packet_message.get());
continue;
}
- // To clarify, "ftrace_events" is the field name and "FtraceEventBundle" is
- // the field type. The terms are often used interchangeably.
- auto* ftrace_events_msg = packet_msg->set_ftrace_events();
+ auto* bundle_message = packet_message->set_ftrace_events();
- // packet.child<ftrace_events>( ).foreach_child( ... )
- protozero::ProtoDecoder ftrace_events(packet_child_it.as_bytes());
- for (auto ftrace_events_it = ftrace_events.ReadField();
- ftrace_events_it.valid();
- ftrace_events_it = ftrace_events.ReadField()) {
- // packet.child<ftrace_events>( ).child_not<event>( ).do ( ... )
- if (ftrace_events_it.id() != kEventFieldNumber) {
- proto_util::AppendField(ftrace_events_it, ftrace_events_msg);
- continue;
+ protozero::ProtoDecoder bundle(field.as_bytes());
+
+ for (auto event_it = bundle.ReadField(); event_it.valid();
+ event_it = bundle.ReadField()) {
+ if (event_it.id() !=
+ protos::pbzero::FtraceEventBundle::kEventFieldNumber ||
+ KeepEvent(context, event_it.as_bytes())) {
+ proto_util::AppendField(event_it, bundle_message);
}
-
- // packet.child<ftrace_events>( ).child_is<event>( ).do ( ... )
- if (ProbeEvent(context, ftrace_events_it) == Redact::kNothing) {
- proto_util::AppendField(ftrace_events_it, ftrace_events_msg);
- continue;
- }
-
- // Dropping packet = "is event" and "is redacted"
}
}
- packet->assign(packet_msg.SerializeAsString());
+ packet->assign(packet_message.SerializeAsString());
+
return base::OkStatus();
}
+// Logical AND of all filters.
+bool ScrubFtraceEvents::KeepEvent(const Context& context,
+ protozero::ConstBytes bytes) const {
+ for (const auto& filter : filters_) {
+ if (!filter->KeepEvent(context, bytes)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/scrub_ftrace_events.h b/src/trace_redaction/scrub_ftrace_events.h
index 77e6835..35c12bf 100644
--- a/src/trace_redaction/scrub_ftrace_events.h
+++ b/src/trace_redaction/scrub_ftrace_events.h
@@ -19,10 +19,22 @@
#include <string>
+#include "perfetto/protozero/field.h"
#include "src/trace_redaction/trace_redaction_framework.h"
namespace perfetto::trace_redaction {
+class FtraceEventFilter {
+ public:
+ virtual ~FtraceEventFilter();
+
+ // Checks if the context contains all neccessary parameters.
+ virtual base::Status VerifyContext(const Context& context) const = 0;
+
+ virtual bool KeepEvent(const Context& context,
+ protozero::ConstBytes bytes) const = 0;
+};
+
// Assumptions:
// 1. This is a hot path (a lot of ftrace packets)
// 2. Allocations are slower than CPU cycles.
@@ -46,11 +58,21 @@
// Block C is different, there is a block D that falls within block C.
// Block D contains sensitive information and should be dropped. When C
// is probed, it will come back saying that C needs additional redaction.
-
-class ScrubFtraceEvents final : public TransformPrimitive {
+class ScrubFtraceEvents : public TransformPrimitive {
public:
base::Status Transform(const Context& context,
std::string* packet) const override;
+
+ // Add a new filter. T must extend FtraceEventFilter.
+ template <typename T>
+ void emplace_back() {
+ filters_.push_back(std::make_unique<T>());
+ }
+
+ private:
+ bool KeepEvent(const Context& context, protozero::ConstBytes bytes) const;
+
+ std::vector<std::unique_ptr<FtraceEventFilter>> filters_;
};
} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/scrub_ftrace_events_integrationtest.cc b/src/trace_redaction/scrub_ftrace_events_integrationtest.cc
index 7584b0f..ec026d1 100644
--- a/src/trace_redaction/scrub_ftrace_events_integrationtest.cc
+++ b/src/trace_redaction/scrub_ftrace_events_integrationtest.cc
@@ -22,6 +22,7 @@
#include "perfetto/ext/base/file_utils.h"
#include "src/base/test/status_matchers.h"
#include "src/base/test/utils.h"
+#include "src/trace_redaction/filter_ftrace_using_allowlist.h"
#include "src/trace_redaction/scrub_ftrace_events.h"
#include "src/trace_redaction/trace_redaction_framework.h"
#include "test/gtest_and_gmock.h"
diff --git a/src/trace_redaction/scrub_process_trees_integrationtest.cc b/src/trace_redaction/scrub_process_trees_integrationtest.cc
index 61a7bcd..7d13721 100644
--- a/src/trace_redaction/scrub_process_trees_integrationtest.cc
+++ b/src/trace_redaction/scrub_process_trees_integrationtest.cc
@@ -30,6 +30,7 @@
#include "src/trace_redaction/optimize_timeline.h"
#include "src/trace_redaction/scrub_process_trees.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"
@@ -37,14 +38,14 @@
namespace {
-constexpr std::string_view kTracePath =
- "test/data/trace-redaction-general.pftrace";
constexpr std::string_view kProcessName =
"com.Unity.com.unity.multiplayer.samples.coop";
} // namespace
-class ScrubProcessTreesIntegrationTest : public testing::Test {
+class ScrubProcessTreesIntegrationTest
+ : public testing::Test,
+ protected TraceRedactionIntegrationFixure {
protected:
void SetUp() override {
// ScrubProcessTrees depends on:
@@ -58,30 +59,13 @@
// BuildTimeline depends on.... nothing
// FindPackageUid depends on... nothing
- redactor_.emplace_collect<FindPackageUid>();
- redactor_.emplace_collect<BuildTimeline>();
- redactor_.emplace_build<OptimizeTimeline>();
- redactor_.emplace_transform<ScrubProcessTrees>();
+ trace_redactor()->emplace_collect<FindPackageUid>();
+ trace_redactor()->emplace_collect<BuildTimeline>();
+ trace_redactor()->emplace_build<OptimizeTimeline>();
+ trace_redactor()->emplace_transform<ScrubProcessTrees>();
// In this case, the process and package have the same name.
- context_.package_name = kProcessName;
-
- 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_);
+ context()->package_name = kProcessName;
}
std::vector<std::string> CollectProcessNames(
@@ -109,25 +93,6 @@
return names;
}
-
- 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_;
};
TEST_F(ScrubProcessTreesIntegrationTest, RemovesProcessNamesFromProcessTrees) {
diff --git a/src/trace_redaction/scrub_task_rename_integrationtest.cc b/src/trace_redaction/scrub_task_rename_integrationtest.cc
index dee77e6..82903c0 100644
--- a/src/trace_redaction/scrub_task_rename_integrationtest.cc
+++ b/src/trace_redaction/scrub_task_rename_integrationtest.cc
@@ -29,6 +29,7 @@
#include "src/trace_redaction/optimize_timeline.h"
#include "src/trace_redaction/scrub_task_rename.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"
@@ -40,44 +41,29 @@
namespace perfetto::trace_redaction {
namespace {
-using FtraceEvent = protos::pbzero::FtraceEvent;
-constexpr std::string_view kTracePath =
- "test/data/trace-redaction-general.pftrace";
+using FtraceEvent = protos::pbzero::FtraceEvent;
// Set the package name to "just some package name". If a specific package name
// is needed, the test it should overwrite this value.
constexpr std::string_view kPackageName =
"com.Unity.com.unity.multiplayer.samples.coop";
-class RenameEventsTraceRedactorIntegrationTest : public testing::Test {
+} // namespace
+
+class RenameEventsTraceRedactorIntegrationTest
+ : public testing::Test,
+ protected TraceRedactionIntegrationFixure {
protected:
void SetUp() override {
// In order for ScrubTaskRename to work, it needs the timeline. All
// registered primitives are there to generate the timeline.
- redactor_.emplace_collect<FindPackageUid>();
- redactor_.emplace_collect<BuildTimeline>();
- redactor_.emplace_build<OptimizeTimeline>();
- redactor_.emplace_transform<ScrubTaskRename>();
+ trace_redactor()->emplace_collect<FindPackageUid>();
+ trace_redactor()->emplace_collect<BuildTimeline>();
+ trace_redactor()->emplace_build<OptimizeTimeline>();
+ trace_redactor()->emplace_transform<ScrubTaskRename>();
- 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_);
+ context()->package_name = kPackageName;
}
std::vector<uint32_t> GetAllRenamedPids(
@@ -105,25 +91,6 @@
return renamed_pids;
}
-
- 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_;
};
TEST_F(RenameEventsTraceRedactorIntegrationTest, RemovesUnwantedRenameTasks) {
@@ -153,5 +120,4 @@
ASSERT_TRUE(redacted_rename_pids.empty());
}
-} // namespace
} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/trace_redaction_integration_fixture.cc b/src/trace_redaction/trace_redaction_integration_fixture.cc
new file mode 100644
index 0000000..85555f3
--- /dev/null
+++ b/src/trace_redaction/trace_redaction_integration_fixture.cc
@@ -0,0 +1,61 @@
+/*
+ * 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/trace_redaction_integration_fixture.h"
+
+#include "perfetto/ext/base/file_utils.h"
+#include "src/base/test/utils.h"
+
+namespace perfetto::trace_redaction {
+
+TraceRedactionIntegrationFixure::TraceRedactionIntegrationFixure() {
+ src_trace_ =
+ base::GetTestDataPath("test/data/trace-redaction-general.pftrace");
+ dest_trace_ = tmp_dir_.AbsolutePath("dst.pftrace");
+}
+
+base::Status TraceRedactionIntegrationFixure::Redact() {
+ auto status = trace_redactor_.Redact(src_trace_, dest_trace_, &context_);
+
+ if (status.ok()) {
+ tmp_dir_.TrackFile("dst.pftrace");
+ }
+
+ return status;
+}
+
+base::StatusOr<std::string> TraceRedactionIntegrationFixure::LoadOriginal()
+ const {
+ return ReadRawTrace(src_trace_);
+}
+
+base::StatusOr<std::string> TraceRedactionIntegrationFixure::LoadRedacted()
+ const {
+ return ReadRawTrace(dest_trace_);
+}
+
+base::StatusOr<std::string> TraceRedactionIntegrationFixure::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());
+}
+
+} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/trace_redaction_integration_fixture.h b/src/trace_redaction/trace_redaction_integration_fixture.h
new file mode 100644
index 0000000..55060f9
--- /dev/null
+++ b/src/trace_redaction/trace_redaction_integration_fixture.h
@@ -0,0 +1,64 @@
+/*
+ * 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_TRACE_REDACTION_INTEGRATION_FIXTURE_H_
+#define SRC_TRACE_REDACTION_TRACE_REDACTION_INTEGRATION_FIXTURE_H_
+
+#include <string>
+
+#include "perfetto/ext/base/status_or.h"
+#include "src/base/test/tmp_dir_tree.h"
+#include "src/trace_redaction/trace_redaction_framework.h"
+#include "src/trace_redaction/trace_redactor.h"
+
+namespace perfetto::trace_redaction {
+
+class TraceRedactionIntegrationFixure {
+ protected:
+ TraceRedactionIntegrationFixure();
+
+ // Redact the source file and write it to the destination file. The contents
+ // of each file can be read using LoadOriginal() and LoadRedacted().
+ base::Status Redact();
+
+ base::StatusOr<std::string> LoadOriginal() const;
+
+ base::StatusOr<std::string> LoadRedacted() const;
+
+ Context* context() { return &context_; }
+
+ TraceRedactor* trace_redactor() { return &trace_redactor_; }
+
+ private:
+ base::StatusOr<std::string> ReadRawTrace(const std::string& path) const;
+
+ Context context_;
+
+ TraceRedactor trace_redactor_;
+
+ base::TmpDirTree tmp_dir_;
+
+ std::string src_trace_;
+
+ // Path to the redacted trace. This will only be valid after Redact()
+ // completely. If redaction was successful, this file will be tracked by
+ // tmp_dir_.
+ std::string dest_trace_;
+};
+
+} // namespace perfetto::trace_redaction
+
+#endif // SRC_TRACE_REDACTION_TRACE_REDACTION_INTEGRATION_FIXTURE_H_
diff --git a/src/trace_redaction/trace_redactor_integrationtest.cc b/src/trace_redaction/trace_redactor_integrationtest.cc
index db3e23f..a62359f 100644
--- a/src/trace_redaction/trace_redactor_integrationtest.cc
+++ b/src/trace_redaction/trace_redactor_integrationtest.cc
@@ -25,10 +25,7 @@
#include "src/base/test/tmp_dir_tree.h"
#include "src/base/test/utils.h"
#include "src/trace_redaction/find_package_uid.h"
-#include "src/trace_redaction/populate_allow_lists.h"
#include "src/trace_redaction/prune_package_list.h"
-#include "src/trace_redaction/scrub_ftrace_events.h"
-#include "src/trace_redaction/scrub_trace_packet.h"
#include "src/trace_redaction/trace_redaction_framework.h"
#include "src/trace_redaction/trace_redactor.h"
#include "test/gtest_and_gmock.h"
@@ -197,170 +194,5 @@
ASSERT_EQ(package_names[7], "com.google.android.networkstack.tethering");
}
-// Broadphase redactions are meant to remove large pieces of information (e.g.
-// whole packets). They need to be tested on their own because finer-grain
-// redactions can make it harder to verify the results. For example, broadphase
-// redactions should not remove rename events. There is a finer grain redaction
-// that takes care of those events. However, it the "scrub rename events"
-// redaction removes all the rename events, it could look like the broadphase
-// primitive did it.
-class BroadphaseTraceRedactorIntegrationTest
- : public TraceRedactorIntegrationTest {
- protected:
- void SetUp() override {
- TraceRedactorIntegrationTest::SetUp();
-
- redactor_.emplace_collect<FindPackageUid>();
- redactor_.emplace_build<PopulateAllowlists>();
- redactor_.emplace_transform<ScrubTracePacket>();
- redactor_.emplace_transform<ScrubFtraceEvents>();
- }
-
- static base::StatusOr<protozero::ConstBytes> FindFirstFtraceEvents(
- const protos::pbzero::Trace::Decoder& trace) {
- for (auto packet_it = trace.packet(); packet_it; ++packet_it) {
- protos::pbzero::TracePacket::Decoder packet(*packet_it);
-
- if (packet.has_ftrace_events()) {
- return packet.ftrace_events();
- }
- }
-
- return base::ErrStatus("Failed to find ftrace events");
- }
-
- // NOTE - this will include fields like "timestamp" and "pid".
- static void GetEventFields(const protos::pbzero::Trace::Decoder& trace,
- base::FlatSet<uint32_t>* set) {
- for (auto packet_it = trace.packet(); packet_it; ++packet_it) {
- protos::pbzero::TracePacket::Decoder packet(*packet_it);
-
- if (!packet.has_ftrace_events()) {
- continue;
- }
-
- protos::pbzero::FtraceEventBundle::Decoder bundle(packet.ftrace_events());
-
- if (!bundle.has_event()) {
- continue;
- }
-
- for (auto events_it = bundle.event(); events_it; ++events_it) {
- protozero::ProtoDecoder event(*events_it);
-
- for (auto event_it = event.ReadField(); event_it.valid();
- event_it = event.ReadField()) {
- set->insert(event_it.id());
- }
- }
- }
- }
-};
-
-// Makes sure all not-allowed ftrace event is removed from a trace.
-TEST_F(BroadphaseTraceRedactorIntegrationTest, RemovesFtraceEvents) {
- auto pre_redaction_file = src_trace();
- auto post_redaction_file = tmp_dir_.AbsolutePath("dst.pftrace");
-
- // We know that there are two oom score updates in the test trace. These
- // events are not in the allowlist and should be dropped.
- auto pre_redaction_buffer = ReadRawTrace(pre_redaction_file);
- ASSERT_OK(pre_redaction_buffer) << pre_redaction_buffer.status().message();
- protos::pbzero::Trace::Decoder pre_redaction_trace(*pre_redaction_buffer);
-
- base::FlatSet<uint32_t> pre_redaction_event_types;
- GetEventFields(pre_redaction_trace, &pre_redaction_event_types);
- ASSERT_GT(pre_redaction_event_types.count(
- FtraceEvent::kOomScoreAdjUpdateFieldNumber),
- 0u);
-
- auto result =
- redactor_.Redact(pre_redaction_file, post_redaction_file, &context_);
- tmp_dir_.TrackFile("dst.pftrace");
- ASSERT_OK(result) << result.message();
-
- auto post_redaction_buffer = ReadRawTrace(post_redaction_file);
- ASSERT_OK(post_redaction_buffer) << post_redaction_buffer.status().message();
- protos::pbzero::Trace::Decoder post_redaction_trace(*post_redaction_buffer);
-
- base::FlatSet<uint32_t> post_redaction_event_types;
- GetEventFields(post_redaction_trace, &post_redaction_event_types);
- ASSERT_EQ(post_redaction_event_types.count(
- FtraceEvent::kOomScoreAdjUpdateFieldNumber),
- 0u);
-}
-
-// When a event is dropped from ftrace_events, only that event should be droped,
-// the other events in the ftrace_events should be retained.
-TEST_F(BroadphaseTraceRedactorIntegrationTest,
- RetainsFtraceEventsWhenRemovingFtraceEvent) {
- auto pre_redaction_file = src_trace();
- auto post_redaction_file = tmp_dir_.AbsolutePath("dst.pftrace");
-
- auto pre_redaction_buffer = ReadRawTrace(pre_redaction_file);
- ASSERT_OK(pre_redaction_buffer) << pre_redaction_buffer.status().message();
-
- protos::pbzero::Trace::Decoder pre_redaction_trace(*pre_redaction_buffer);
-
- auto pre_redaction_first_events = FindFirstFtraceEvents(pre_redaction_trace);
- ASSERT_OK(pre_redaction_first_events)
- << pre_redaction_first_events.status().message();
-
- auto result =
- redactor_.Redact(pre_redaction_file, post_redaction_file, &context_);
- tmp_dir_.TrackFile("dst.pftrace");
- ASSERT_OK(result) << result.message();
-
- auto post_redaction_buffer = ReadRawTrace(post_redaction_file);
- ASSERT_OK(post_redaction_buffer) << post_redaction_buffer.status().message();
-
- protos::pbzero::Trace::Decoder post_redaction_trace(*post_redaction_buffer);
-
- auto post_redaction_ftrace_events =
- FindFirstFtraceEvents(post_redaction_trace);
- ASSERT_OK(post_redaction_ftrace_events)
- << post_redaction_ftrace_events.status().message();
-
- base::FlatSet<uint32_t> events_before;
- GetEventFields(pre_redaction_trace, &events_before);
- ASSERT_EQ(events_before.size(), 14u);
- ASSERT_TRUE(events_before.count(FtraceEvent::kTimestampFieldNumber));
- ASSERT_TRUE(events_before.count(FtraceEvent::kPidFieldNumber));
- ASSERT_TRUE(events_before.count(FtraceEvent::kPrintFieldNumber));
- ASSERT_TRUE(events_before.count(FtraceEvent::kSchedSwitchFieldNumber));
- ASSERT_TRUE(events_before.count(FtraceEvent::kCpuFrequencyFieldNumber));
- ASSERT_TRUE(events_before.count(FtraceEvent::kCpuIdleFieldNumber));
- ASSERT_TRUE(events_before.count(FtraceEvent::kSchedWakeupFieldNumber));
- ASSERT_TRUE(events_before.count(FtraceEvent::kSchedWakingFieldNumber));
- ASSERT_TRUE(events_before.count(FtraceEvent::kSchedWakeupNewFieldNumber));
- ASSERT_TRUE(events_before.count(FtraceEvent::kTaskNewtaskFieldNumber));
- ASSERT_TRUE(events_before.count(FtraceEvent::kTaskRenameFieldNumber));
- ASSERT_TRUE(events_before.count(FtraceEvent::kSchedProcessExitFieldNumber));
- ASSERT_TRUE(events_before.count(FtraceEvent::kSchedProcessFreeFieldNumber));
- ASSERT_TRUE(events_before.count(FtraceEvent::kOomScoreAdjUpdateFieldNumber));
-
- base::FlatSet<uint32_t> events_after;
- GetEventFields(post_redaction_trace, &events_after);
- ASSERT_EQ(events_after.size(), 9u);
-
- // Retained.
- ASSERT_TRUE(events_after.count(FtraceEvent::kTimestampFieldNumber));
- ASSERT_TRUE(events_after.count(FtraceEvent::kPidFieldNumber));
- ASSERT_TRUE(events_after.count(FtraceEvent::kSchedSwitchFieldNumber));
- ASSERT_TRUE(events_after.count(FtraceEvent::kCpuFrequencyFieldNumber));
- ASSERT_TRUE(events_after.count(FtraceEvent::kCpuIdleFieldNumber));
- ASSERT_TRUE(events_after.count(FtraceEvent::kSchedWakingFieldNumber));
- ASSERT_TRUE(events_after.count(FtraceEvent::kTaskNewtaskFieldNumber));
- ASSERT_TRUE(events_after.count(FtraceEvent::kTaskRenameFieldNumber));
- ASSERT_TRUE(events_after.count(FtraceEvent::kSchedProcessFreeFieldNumber));
-
- // Dropped.
- ASSERT_FALSE(events_after.count(FtraceEvent::kPrintFieldNumber));
- ASSERT_FALSE(events_after.count(FtraceEvent::kSchedWakeupFieldNumber));
- ASSERT_FALSE(events_after.count(FtraceEvent::kSchedWakeupNewFieldNumber));
- ASSERT_FALSE(events_after.count(FtraceEvent::kSchedProcessExitFieldNumber));
- ASSERT_FALSE(events_after.count(FtraceEvent::kOomScoreAdjUpdateFieldNumber));
-}
-
} // namespace
} // namespace perfetto::trace_redaction
diff --git a/tools/delete_permalink b/tools/delete_permalink
new file mode 100755
index 0000000..9963b03
--- /dev/null
+++ b/tools/delete_permalink
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+
+"""A tool to delete permalinks and traces behind them.
+
+To be used by the Perfetto team, who has write access to the GCS bucket.
+"""
+
+import json
+import logging
+import re
+import subprocess
+import sys
+
+from typing import List
+
+GCS_BUCKET = 'perfetto-ui-data'
+GCS_HTTP = 'https://storage.googleapis.com/%s/' % GCS_BUCKET
+
+
+def delete_gcs_obj(url: str, gcs_delete_list: List[str]):
+ if not url.startswith(GCS_HTTP):
+ logging.error('The URL %s should start with %s', url, GCS_HTTP)
+ return
+ gs_url = 'gs://%s/%s' % (GCS_BUCKET, url[len(GCS_HTTP):])
+ gcs_delete_list.append(gs_url)
+
+
+def delete_permalink_uuid(uuid: str, gcs_delete_list: List[str]):
+ state_url = GCS_HTTP + uuid
+ delete_gcs_obj(state_url, gcs_delete_list)
+ state_json = subprocess.check_output(['curl', '-Ls', state_url])
+ state = json.loads(state_json)
+ trace_url = state['engine']['source']['url']
+ delete_gcs_obj(trace_url, gcs_delete_list)
+
+
+def main():
+ gcs_delete_list = []
+ if sys.stdin.isatty():
+ logging.warn('This tool expects a list of uuids or https://ui.perfetto.dev/#!#?s=deadbeef')
+
+ for line in sys.stdin.readlines():
+ line = line.strip()
+ m = re.match(r'.*?\b([a-f0-9]{64})', line)
+ if not m:
+ logging.error('Could not find a 64 hex UUID from %s', line)
+ continue
+ uuid = m.group(1)
+ delete_permalink_uuid(uuid, gcs_delete_list)
+
+ if len(gcs_delete_list) == 0:
+ logging.info('No object to delete, quitting without taking any action')
+ return 0
+
+ print('Removing the following objects:')
+ for gs_uri in gcs_delete_list:
+ print(' ', gs_uri)
+ subprocess.check_call(['gsutil', '-m', 'rm', '-f', '-a'] + gcs_delete_list)
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/ui/src/common/plugins.ts b/ui/src/common/plugins.ts
index 80e9ca7..dfb93d7 100644
--- a/ui/src/common/plugins.ts
+++ b/ui/src/common/plugins.ts
@@ -504,7 +504,10 @@
return this._plugins.get(pluginId);
}
- async onTraceLoad(engine: Engine): Promise<void> {
+ async onTraceLoad(
+ engine: Engine,
+ beforeEach?: (id: string) => void,
+ ): Promise<void> {
this.engine = engine;
const plugins = Array.from(this._plugins.entries());
// Awaiting all plugins in parallel will skew timing data as later plugins
@@ -513,6 +516,7 @@
// most plugins use the same engine, which can only process one query at a
// time.
for (const [id, pluginDetails] of plugins) {
+ beforeEach?.(id);
await doPluginTraceLoad(pluginDetails, engine, id);
}
}
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index e5df631..de8fde3 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -528,8 +528,9 @@
await defineMaxLayoutDepthSqlFunction(engine);
- this.updateStatus('Loading plugins');
- await pluginManager.onTraceLoad(engine);
+ await pluginManager.onTraceLoad(engine, (id) => {
+ this.updateStatus(`Running plugin: ${id}`);
+ });
{
// When we reload from a permalink don't create extra tracks: