blob: 716cabc044496db9fe71a5a89f3f553be629883e [file] [log] [blame]
/*
* 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 <string>
#include <string_view>
#include <vector>
#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_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"
#include "protos/perfetto/trace/ps/process_tree.pbzero.h"
#include "protos/perfetto/trace/trace.pbzero.h"
namespace perfetto::trace_redaction {
namespace {
constexpr std::string_view kProcessName =
"com.Unity.com.unity.multiplayer.samples.coop";
} // namespace
class RedactProcessTreesIntegrationTest
: public testing::Test,
protected TraceRedactionIntegrationFixure {
protected:
void SetUp() override {
trace_redactor_.emplace_collect<CollectSystemInfo>();
trace_redactor_.emplace_build<BuildSyntheticThreads>();
trace_redactor_.emplace_collect<FindPackageUid>();
trace_redactor_.emplace_collect<CollectTimelineEvents>();
// Filter the process tree based on whether or not a process is part of the
// target package.
auto* process_tree =
trace_redactor_.emplace_transform<RedactProcessTrees>();
process_tree->emplace_modifier<ProcessTreeDoNothing>();
process_tree->emplace_filter<ConnectedToPackage>();
// In this case, the process and package have the same name.
context_.package_name = kProcessName;
}
std::unordered_set<int32_t> GetPids(const std::string& bytes) const {
std::unordered_set<int32_t> pids;
protos::pbzero::Trace::Decoder decoder(bytes);
for (auto it = decoder.packet(); it; ++it) {
protos::pbzero::TracePacket::Decoder packet(*it);
if (packet.has_process_tree()) {
GetPids(packet.process_tree(), &pids);
}
}
return pids;
}
std::unordered_set<int32_t> GetTids(const std::string& bytes) const {
std::unordered_set<int32_t> tids;
protos::pbzero::Trace::Decoder decoder(bytes);
for (auto it = decoder.packet(); it; ++it) {
protos::pbzero::TracePacket::Decoder packet(*it);
if (packet.has_process_tree()) {
GetTids(packet.process_tree(), &tids);
}
}
return tids;
}
Context context_;
TraceRedactor trace_redactor_;
private:
void GetPids(protozero::ConstBytes bytes,
std::unordered_set<int32_t>* pids) const {
protos::pbzero::ProcessTree::Decoder process_tree(bytes);
for (auto it = process_tree.processes(); it; ++it) {
protos::pbzero::ProcessTree::Process::Decoder process(*it);
pids->insert(process.ppid());
pids->insert(process.pid());
}
}
void GetTids(protozero::ConstBytes bytes,
std::unordered_set<int32_t>* tids) const {
protos::pbzero::ProcessTree::Decoder process_tree(bytes);
for (auto it = process_tree.threads(); it; ++it) {
protos::pbzero::ProcessTree::Thread::Decoder thread(*it);
tids->insert(thread.tgid());
tids->insert(thread.tid());
}
}
};
TEST_F(RedactProcessTreesIntegrationTest, FilterProcesses) {
ASSERT_OK(Redact(trace_redactor_, &context_));
auto original_trace_str = LoadOriginal();
ASSERT_OK(original_trace_str);
auto redacted_trace_str = LoadRedacted();
ASSERT_OK(redacted_trace_str);
auto original_pids = GetPids(*original_trace_str);
auto redacted_pids = GetPids(*redacted_trace_str);
// There are 902 unique pids across all process trees:
// grep 'processes {' -A 1 src.pftrace.txt | grep 'pid: ' | grep -Po "\d+"
// | sort | uniq | wc -l
//
// But if ppids are included, there are 903 pids in the process tree:
// grep 'processes {' -A 2 src.pftrace.txt | grep 'pid: ' | grep -Po "\d+"
// | sort | uniq | wc -l
//
// The above grep statements use a stringified version of the trace. Using "-A
// 1" will return the pid line. Using "-A 2" will include both pid and ppid.
//
// The original process count aligns with trace processor. However, the
// redacted count does not. The final tree has one process but trace processor
// reports 4 processes.
ASSERT_EQ(original_pids.size(), 903u);
ASSERT_EQ(redacted_pids.size(), 2u);
ASSERT_TRUE(redacted_pids.count(7105));
}
TEST_F(RedactProcessTreesIntegrationTest, FilterThreads) {
ASSERT_OK(Redact(trace_redactor_, &context_));
auto original_trace_str = LoadOriginal();
ASSERT_OK(original_trace_str);
auto redacted_trace_str = LoadRedacted();
ASSERT_OK(redacted_trace_str);
auto original_tids = GetTids(*original_trace_str);
auto redacted_tids = GetTids(*redacted_trace_str);
// There are 2761 unique tids across all process trees:
// grep 'threads {' -A 1 src.pftrace.txt | grep 'tid: ' | grep -Po "\d+" |
// sort | uniq | wc -l
//
// There are 2896 unique tids/tgis across all process trees:
// grep 'threads {' -A 2 src.pftrace.txt | grep -P '(tid|tgid): ' | grep
// -Po '\d+' | sort | uniq | wc -l
//
// The original tid count does NOT align with what trace processor returns.
// Trace processor reports 3666 threads. The assumption is trace processor is
// fulling thread information from additional.
//
// The redacted tid+tgid count does NOT align with what trace processor
// returns. Trace processor reports 199 tids where are there are only 63 tids
// found in process tree. This suggests that trace processor is pulling tid
// data from other locations.
ASSERT_EQ(original_tids.size(), 2896u);
ASSERT_EQ(redacted_tids.size(), 64u);
}
TEST_F(RedactProcessTreesIntegrationTest, AddSynthProcess) {
// Append another primitive that won't filter, but will add new threads. This
// will be compatible with the other instanced in SetUp().
auto* process_tree = trace_redactor_.emplace_transform<RedactProcessTrees>();
process_tree->emplace_modifier<ProcessTreeCreateSynthThreads>();
process_tree->emplace_filter<AllowAll>();
ASSERT_OK(Redact(trace_redactor_, &context_));
auto redacted_trace_str = LoadRedacted();
ASSERT_OK(redacted_trace_str);
auto redacted_pids = GetPids(*redacted_trace_str);
const auto* synth_process = context_.synthetic_process.get();
ASSERT_TRUE(synth_process);
ASSERT_NE(std::find(redacted_pids.begin(), redacted_pids.end(),
synth_process->tgid()),
redacted_pids.end());
}
TEST_F(RedactProcessTreesIntegrationTest, AddSynthThreads) {
// Append another primitive that won't filter, but will add new threads. This
// will be compatible with the other instanced in SetUp().
auto* process_tree = trace_redactor_.emplace_transform<RedactProcessTrees>();
process_tree->emplace_modifier<ProcessTreeCreateSynthThreads>();
process_tree->emplace_filter<AllowAll>();
ASSERT_OK(Redact(trace_redactor_, &context_));
const auto* synth_process = context_.synthetic_process.get();
ASSERT_TRUE(synth_process);
ASSERT_FALSE(synth_process->tids().empty());
auto original_trace_str = LoadOriginal();
ASSERT_OK(original_trace_str);
auto original_tids = GetTids(*original_trace_str);
// The synth threads should not be found in the original trace.
for (auto tid : synth_process->tids()) {
ASSERT_FALSE(original_tids.count(tid));
}
auto redacted_trace_str = LoadRedacted();
ASSERT_OK(redacted_trace_str);
auto redacted_tids = GetTids(*redacted_trace_str);
// The synth threads should be found in the redacted trace.
for (auto tid : synth_process->tids()) {
ASSERT_TRUE(redacted_tids.count(tid));
}
}
} // namespace perfetto::trace_redaction