blob: c72cdc65036e653458456bf99a938acbf228508b [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 "src/trace_redaction/redact_process_trees.h"
#include <string>
#include "perfetto/base/status.h"
#include "perfetto/protozero/field.h"
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "src/trace_processor/util/status_macros.h"
#include "src/trace_redaction/proto_util.h"
#include "src/trace_redaction/trace_redaction_framework.h"
#include "protos/perfetto/trace/ps/process_tree.pbzero.h"
#include "protos/perfetto/trace/trace_packet.pbzero.h"
namespace perfetto::trace_redaction {
ProcessTreeFilter::~ProcessTreeFilter() = default;
ProcessTreeModifier::~ProcessTreeModifier() = default;
bool ProcessTreeFilterAllowAll::Filter(const Context&,
uint64_t,
int32_t) const {
return true;
}
bool ProcessTreeFilterConnectedToPackage::Filter(const Context& context,
uint64_t ts,
int32_t pid) const {
PERFETTO_DCHECK(context.timeline);
PERFETTO_DCHECK(context.package_uid.has_value());
// Checking the uid directly is an option, but the timeline handles multiple
// instances of the same app.
return context.timeline->PidConnectsToUid(ts, pid, *context.package_uid);
}
base::Status ProcessTreeDoNothing::Modify(const Context&,
protos::pbzero::ProcessTree*) const {
return base::OkStatus();
}
base::Status ProcessTreeCreateSynthThreads::Modify(
const Context& context,
protos::pbzero::ProcessTree* message) const {
PERFETTO_DCHECK(message);
if (!context.synthetic_threads.has_value()) {
return base::ErrStatus(
"ProcessTreeCreateSynthThreads: missing synthetic thread group");
}
// There should be one thread per cpu. If there are no threads, it means there
// were no cpus. That wrong.
if (context.synthetic_threads->tids.empty()) {
return base::ErrStatus(
"ProcessTreeCreateSynthThreads: missing synthetic threads");
}
// uid 0 and ppid 2 means it is a system process.
auto* process = message->add_processes();
process->set_uid(0);
process->set_ppid(2);
process->set_pid(context.synthetic_threads->tgid);
process->add_cmdline("MergedThreads");
for (auto tid : context.synthetic_threads->tids) {
auto* thread = message->add_threads();
thread->set_tgid(context.synthetic_threads->tgid);
thread->set_tid(tid);
thread->set_name("CPU");
}
return base::OkStatus();
}
base::Status RedactProcessTrees::Transform(const Context& context,
std::string* packet) const {
PERFETTO_DCHECK(packet);
if (!context.package_uid.has_value()) {
return base::ErrStatus("RedactProcessTrees: missing package uid.");
}
if (!context.timeline) {
return base::ErrStatus("RedactProcessTrees: missing timeline.");
}
if (!context.synthetic_threads.has_value()) {
return base::ErrStatus("RedactProcessTrees: missing synthentic threads.");
}
protozero::ProtoDecoder decoder(*packet);
auto tree =
decoder.FindField(protos::pbzero::TracePacket::kProcessTreeFieldNumber);
if (!tree.valid()) {
return base::OkStatus();
}
// This has been verified by the verify primitive.
auto timestamp =
decoder.FindField(protos::pbzero::TracePacket::kTimestampFieldNumber);
protozero::HeapBuffered<protos::pbzero::TracePacket> message;
for (auto it = decoder.ReadField(); it.valid(); it = decoder.ReadField()) {
if (it.id() == tree.id()) {
RETURN_IF_ERROR(OnProcessTree(context, timestamp.as_uint64(),
it.as_bytes(),
message->set_process_tree()));
} else {
proto_util::AppendField(it, message.get());
}
}
packet->assign(message.SerializeAsString());
return base::OkStatus();
}
base::Status RedactProcessTrees::OnProcessTree(
const Context& context,
uint64_t ts,
protozero::ConstBytes bytes,
protos::pbzero::ProcessTree* message) const {
protozero::ProtoDecoder decoder(bytes);
for (auto it = decoder.ReadField(); it.valid(); it = decoder.ReadField()) {
switch (it.id()) {
case protos::pbzero::ProcessTree::kProcessesFieldNumber:
RETURN_IF_ERROR(OnProcess(context, ts, it, message));
break;
case protos::pbzero::ProcessTree::kThreadsFieldNumber:
RETURN_IF_ERROR(OnThread(context, ts, it, message));
break;
default:
proto_util::AppendField(it, message);
break;
}
}
PERFETTO_DCHECK(modifier_);
return modifier_->Modify(context, message);
}
base::Status RedactProcessTrees::OnProcess(
const Context& context,
uint64_t ts,
protozero::Field field,
protos::pbzero::ProcessTree* message) const {
protozero::ProtoDecoder decoder(field.as_bytes());
auto pid =
decoder.FindField(protos::pbzero::ProcessTree::Process::kPidFieldNumber);
if (!pid.valid()) {
return base::ErrStatus("RedactProcessTrees: process with no pid");
}
PERFETTO_DCHECK(filter_);
if (filter_->Filter(context, ts, pid.as_int32())) {
proto_util::AppendField(field, message);
}
return base::OkStatus();
}
base::Status RedactProcessTrees::OnThread(
const Context& context,
uint64_t ts,
protozero::Field field,
protos::pbzero::ProcessTree* message) const {
protozero::ProtoDecoder decoder(field.as_bytes());
auto tid =
decoder.FindField(protos::pbzero::ProcessTree::Thread::kTidFieldNumber);
if (!tid.valid()) {
return base::ErrStatus("RedactProcessTrees: thread with no tid");
}
PERFETTO_DCHECK(filter_);
if (filter_->Filter(context, ts, tid.as_int32())) {
proto_util::AppendField(field, message);
}
return base::OkStatus();
}
} // namespace perfetto::trace_redaction