/* * 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/base/test/status_matchers.h" #include "src/trace_redaction/collect_system_info.h" #include "src/trace_redaction/collect_timeline_events.h" #include "src/trace_redaction/find_package_uid.h" #include "src/trace_redaction/redact_ftrace_event.h" #include "src/trace_redaction/remap_scheduling_events.h" #include "src/trace_redaction/trace_redaction_integration_fixture.h" #include "test/gtest_and_gmock.h" #include "protos/perfetto/trace/ftrace/sched.pbzero.h" #include "protos/perfetto/trace/ftrace/task.pbzero.h" namespace perfetto::trace_redaction { // Runs ThreadMergeRemapFtraceEventPid, ThreadMergeRemapSchedSwitchPid, // ThreadMergeRemapSchedWakingPid, and ThreadMergeDropField to replace pids with // synthetic pids (for all threads outside of the target package); class RemapSchedulingEventsIntegrationTest : public testing::Test, protected TraceRedactionIntegrationFixure { public: static constexpr auto kPackageName = "com.Unity.com.unity.multiplayer.samples.coop"; static constexpr uint64_t kPackageId = 10252; static constexpr int32_t kPid = 7105; // Threads belonging to pid 7105. Collected using trace processors. static constexpr auto kTids = { 0, // pid 0 will always be included because CPU idle uses it. 7105, 7111, 7112, 7113, 7114, 7115, 7116, 7117, 7118, 7119, 7120, 7124, 7125, 7127, 7129, 7130, 7131, 7132, 7133, 7134, 7135, 7136, 7137, 7139, 7141, 7142, 7143, 7144, 7145, 7146, 7147, 7148, 7149, 7150, 7151, 7152, 7153, 7154, 7155, 7156, 7157, 7158, 7159, 7160, 7161, 7162, 7163, 7164, 7165, 7166, 7167, 7171, 7172, 7174, 7178, 7180, 7184, 7200, 7945, 7946, 7947, 7948, 7950, 7969, }; protected: void SetUp() override { trace_redactor()->emplace_collect(); // In order to remap threads, we need to have synth threads. trace_redactor()->emplace_collect(); trace_redactor()->emplace_build(); // Timeline information is needed to know if a pid belongs to a package. trace_redactor()->emplace_collect(); auto* redactions = trace_redactor()->emplace_transform(); redactions->emplace_back(); redactions->emplace_back(); redactions->emplace_back(); redactions->emplace_back(); redactions->emplace_back(); context()->package_name = kPackageName; } struct Index { // List of FtraceEvent std::vector events; // List of SchedSwitchFtraceEvent std::vector events_sched_switch; // List of SchedWakingFtraceEvent std::vector events_sched_waking; // List of SchedProcessFreeFtraceEvent std::vector events_sched_process_free; // List of TaskNewtaskFtraceEvent std::vector events_task_newtask; }; void UpdateFtraceIndex(protozero::ConstBytes bytes, Index* index) { protos::pbzero::FtraceEventBundle::Decoder bundle(bytes); for (auto event = bundle.event(); event; ++event) { index->events.push_back(event->as_bytes()); // protos::pbzero::FtraceEvent protozero::ProtoDecoder ftrace_event(event->as_bytes()); auto sched_switch = ftrace_event.FindField( protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber); if (sched_switch.valid()) { index->events_sched_switch.push_back(sched_switch.as_bytes()); } auto sched_waking = ftrace_event.FindField( protos::pbzero::FtraceEvent::kSchedWakingFieldNumber); if (sched_waking.valid()) { index->events_sched_waking.push_back(sched_waking.as_bytes()); } auto sched_process_free = ftrace_event.FindField( protos::pbzero::FtraceEvent::kSchedProcessFreeFieldNumber); if (sched_process_free.valid()) { index->events_sched_process_free.push_back( sched_process_free.as_bytes()); } auto task_newtask = ftrace_event.FindField( protos::pbzero::FtraceEvent::kTaskNewtaskFieldNumber); if (task_newtask.valid()) { index->events_task_newtask.push_back(task_newtask.as_bytes()); } } } // Bytes should be TracePacket Index CreateFtraceIndex(const std::string& bytes) { Index index; protozero::ProtoDecoder packet_decoder(bytes); for (auto packet = packet_decoder.ReadField(); packet.valid(); packet = packet_decoder.ReadField()) { auto events = packet_decoder.FindField( protos::pbzero::TracePacket::kFtraceEventsFieldNumber); if (events.valid()) { UpdateFtraceIndex(events.as_bytes(), &index); } } return index; } base::StatusOr LoadAndRedactTrace() { auto source = LoadOriginal(); if (!source.ok()) { return source.status(); } auto redact = Redact(); if (!redact.ok()) { return redact; } // Double-check the package id with the one from trace processor. If this // was wrong and this check was missing, finding the problem would be much // harder. if (!context()->package_uid.has_value()) { return base::ErrStatus("Missing package uid."); } if (context()->package_uid.value() != kPackageId) { return base::ErrStatus("Unexpected package uid found."); } auto redacted = LoadRedacted(); if (redacted.ok()) { return redacted; } // System info is used to initialize the synth threads. If these are wrong, // then the synth threads will be wrong. if (!context()->system_info.has_value()) { return base::ErrStatus("Missing system info."); } if (context()->system_info->last_cpu() != 7u) { return base::ErrStatus("Unexpected cpu count."); } // The synth threads should have been initialized. They will be used here to // verify which threads exist in the redacted trace. if (!context()->synthetic_threads.has_value()) { return base::ErrStatus("Missing synthetic threads."); } if (context()->synthetic_threads->tids.size() != 8u) { return base::ErrStatus("Unexpected synthentic thread count."); } return redacted; } // Should be called after redaction since it requires data from the context. std::unordered_set CopyAllowedTids(const Context& context) const { std::unordered_set tids(kTids.begin(), kTids.end()); tids.insert(context.synthetic_threads->tgid); tids.insert(context.synthetic_threads->tids.begin(), context.synthetic_threads->tids.end()); return tids; } private: std::unordered_set allowed_tids_; }; TEST_F(RemapSchedulingEventsIntegrationTest, FilterFtraceEventPid) { auto redacted = LoadAndRedactTrace(); ASSERT_OK(redacted); auto allowlist = CopyAllowedTids(*context()); auto index = CreateFtraceIndex(*redacted); for (const auto& event : index.events) { protos::pbzero::FtraceEvent::Decoder decoder(event); auto pid = static_cast(decoder.pid()); ASSERT_TRUE(allowlist.count(pid)); } } TEST_F(RemapSchedulingEventsIntegrationTest, FiltersSchedSwitch) { auto redacted = LoadAndRedactTrace(); ASSERT_OK(redacted); auto allowlist = CopyAllowedTids(*context()); auto index = CreateFtraceIndex(*redacted); for (const auto& event : index.events_sched_switch) { protos::pbzero::SchedSwitchFtraceEvent::Decoder decoder(event); ASSERT_TRUE(allowlist.count(decoder.prev_pid())); ASSERT_TRUE(allowlist.count(decoder.next_pid())); } } TEST_F(RemapSchedulingEventsIntegrationTest, FiltersSchedWaking) { auto redacted = LoadAndRedactTrace(); ASSERT_OK(redacted); auto allowlist = CopyAllowedTids(*context()); auto index = CreateFtraceIndex(*redacted); for (const auto& event : index.events_sched_waking) { protos::pbzero::SchedWakingFtraceEvent::Decoder decoder(event); ASSERT_TRUE(allowlist.count(decoder.pid())); } } TEST_F(RemapSchedulingEventsIntegrationTest, FiltersProcessFree) { auto redacted = LoadAndRedactTrace(); ASSERT_OK(redacted); auto allowlist = CopyAllowedTids(*context()); auto index = CreateFtraceIndex(*redacted); for (const auto& event : index.events_sched_process_free) { protos::pbzero::SchedProcessFreeFtraceEvent::Decoder decoder(event); ASSERT_TRUE(allowlist.count(decoder.pid())); } } TEST_F(RemapSchedulingEventsIntegrationTest, FiltersNewTask) { auto redacted = LoadAndRedactTrace(); ASSERT_OK(redacted); auto allowlist = CopyAllowedTids(*context()); auto index = CreateFtraceIndex(*redacted); for (const auto& event : index.events_task_newtask) { protos::pbzero::TaskNewtaskFtraceEvent::Decoder decoder(event); ASSERT_TRUE(allowlist.count(decoder.pid())); } } } // namespace perfetto::trace_redaction