blob: b0056b60839776d082437b8207236c60dd1ecb5c [file] [edit]
/*
* Copyright (C) 2026 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/merge_process_tree.h"
#include "perfetto/base/compiler.h"
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "protos/perfetto/trace/ps/process_tree.pbzero.h"
#include "protos/perfetto/trace/trace.pbzero.h"
#include "src/trace_redaction/proto_util.h"
#include "src/trace_redaction/trace_redaction_framework.h"
namespace perfetto::trace_redaction {
using protos::pbzero::ProcessTree;
using protos::pbzero::TracePacket;
base::Status CollectProcessTrees::Collect(const TracePacket::Decoder& packet,
Context* context) const {
if (!packet.has_process_tree()) {
return base::OkStatus();
}
if (!context->merged_process_tree.has_value()) {
context->merged_process_tree = Context::MergedProcessTree();
}
auto& merged_tree = context->merged_process_tree.value();
// Collect unique processes
ProcessTree::Decoder process_tree_decoder(packet.process_tree());
for (auto it = process_tree_decoder.processes(); it; ++it) {
if (PERFETTO_UNLIKELY(!merged_tree.collected_global_packet_fields)) {
// Collect global packet fields only from the first process_tree packet.
merged_tree.collected_global_packet_fields = true;
merged_tree.timestamp = packet.timestamp();
merged_tree.trusted_uid = packet.trusted_uid();
}
ProcessTree::Process::Decoder process_decoder(*it);
Context::ProcessTreeProcess process;
process.pid = process_decoder.pid();
if (merged_tree.processes_by_pid.find(process.pid) !=
merged_tree.processes_by_pid.end()) {
continue;
}
for (auto cmdline_it = process_decoder.cmdline(); cmdline_it;
++cmdline_it) {
process.cmdline.push_back(cmdline_it->as_std_string());
}
process.is_kthread = process_decoder.is_kthread();
process.ppid = process_decoder.ppid();
process.uid = process_decoder.uid();
merged_tree.processes_by_pid[process.pid] = process;
}
// Collect unique threads
for (auto it = process_tree_decoder.threads(); it; ++it) {
ProcessTree::Thread::Decoder thread_decoder(*it);
if (merged_tree.threads_by_tid.find(thread_decoder.tid()) !=
merged_tree.threads_by_tid.end()) {
continue;
}
Context::ProcessTreeThread thread;
thread.tgid = thread_decoder.tgid();
thread.tid = thread_decoder.tid();
thread.name = thread_decoder.name().ToStdString();
merged_tree.threads_by_tid[thread.tid] = thread;
}
return base::OkStatus();
}
base::Status ReduceProcessTrees::Transform(const Context&,
std::string* packet) const {
TracePacket::Decoder packet_decoder(*packet);
if (packet_decoder.has_process_tree()) {
// Drop every process tree, it will be added back during Augment phase.
packet->clear();
}
return base::OkStatus();
}
base::Status AugmentProcessTrees::Augment(const Context& context,
std::string* packet) {
PERFETTO_DCHECK(packet);
if (emitted_ || !context.merged_process_tree.has_value()) {
return base::OkStatus();
}
const auto& merged_tree = context.merged_process_tree.value();
if (merged_tree.processes_by_pid.empty() &&
merged_tree.threads_by_tid.empty()) {
return base::OkStatus();
}
emitted_ = true;
protozero::HeapBuffered<protos::pbzero::TracePacket> message;
message->set_timestamp(merged_tree.timestamp);
message->set_trusted_uid(merged_tree.trusted_uid);
// Add the merged process tree (processes and threads).
auto* tree = message->set_process_tree();
for (const auto& it : merged_tree.processes_by_pid) {
const Context::ProcessTreeProcess& process = it.second;
auto* process_field = tree->add_processes();
process_field->set_pid(process.pid);
process_field->set_ppid(process.ppid);
process_field->set_uid(process.uid);
process_field->set_is_kthread(process.is_kthread);
for (const std::string& cmdline : process.cmdline) {
process_field->add_cmdline(cmdline);
}
}
for (const auto& it : merged_tree.threads_by_tid) {
const Context::ProcessTreeThread& thread = it.second;
auto* thread_field = tree->add_threads();
thread_field->set_tgid(thread.tgid);
thread_field->set_tid(thread.tid);
if (!thread.name.empty()) {
thread_field->set_name(thread.name);
}
}
proto_util::AppendMessage(message, packet);
return base::OkStatus();
}
} // namespace perfetto::trace_redaction