blob: 123e122b76b04255350a9c4a8e75018682a29a92 [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_ftrace_events.h"
#include <string>
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "src/trace_processor/util/status_macros.h"
#include "src/trace_redaction/proto_util.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/power.pbzero.h"
#include "protos/perfetto/trace/trace.pbzero.h"
namespace perfetto::trace_redaction {
FtraceEventFilter::~FtraceEventFilter() = default;
bool FilterFtraceUsingSuspendResume::Includes(const Context&,
protozero::Field event) const {
// Values are taken from "suspend_period.textproto". These values would
// ideally be provided via the context, but until there are multiple sources,
// they can be here.
constexpr std::string_view kSyscoreSuspend = "syscore_suspend";
constexpr std::string_view kSyscoreResume = "syscore_resume";
constexpr std::string_view kTimekeepingFreeze = "timekeeping_freeze";
protozero::ProtoDecoder event_decoder(event.as_bytes());
// It's not a suspend-resume event, defer the decision to another filter.
auto suspend_resume = event_decoder.FindField(
protos::pbzero::FtraceEvent::kSuspendResumeFieldNumber);
if (!suspend_resume.valid()) {
return true;
}
protozero::ProtoDecoder suspend_resume_decoder(suspend_resume.as_bytes());
auto action = suspend_resume_decoder.FindField(
protos::pbzero::SuspendResumeFtraceEvent::kActionFieldNumber);
// If a suspend-resume has no action, there is nothing to redact, so it is
// safe to passthrough.
if (!action.valid()) {
return true;
}
std::string_view action_str(action.as_string().data, action.size());
return kSyscoreSuspend == action_str || kSyscoreResume == action_str ||
kTimekeepingFreeze == action_str;
}
bool FilterRss::Includes(const Context& context, protozero::Field event) const {
protos::pbzero::FtraceEvent::Decoder event_decoder(event.as_bytes());
if (event_decoder.has_rss_stat_throttled() || event_decoder.has_rss_stat()) {
// The event's pid is unsigned, but tids are always signed.
auto pid = static_cast<int32_t>(event_decoder.pid());
return context.timeline->PidConnectsToUid(event_decoder.timestamp(), pid,
*context.package_uid);
}
return true;
}
base::Status RedactFtraceEvents::Transform(const Context& context,
std::string* packet) const {
if (packet == nullptr || packet->empty()) {
return base::ErrStatus("RedactFtraceEvents: null or empty packet.");
}
protozero::ProtoDecoder packet_decoder(*packet);
auto ftrace_events = packet_decoder.FindField(
protos::pbzero::TracePacket::kFtraceEventsFieldNumber);
if (!ftrace_events.valid()) {
return base::OkStatus();
}
protozero::ProtoDecoder decoder(*packet);
protozero::HeapBuffered<protos::pbzero::TracePacket> message;
for (auto it = decoder.ReadField(); it.valid(); it = decoder.ReadField()) {
if (it.id() == protos::pbzero::TracePacket::kFtraceEventsFieldNumber) {
RETURN_IF_ERROR(
OnFtraceEvents(context, it, message->set_ftrace_events()));
} else {
proto_util::AppendField(it, message.get());
}
}
packet->assign(message.SerializeAsString());
return base::OkStatus();
}
base::Status RedactFtraceEvents::OnFtraceEvents(
const Context& context,
protozero::Field ftrace_events,
protos::pbzero::FtraceEventBundle* message) const {
// If there are N ftrace events, and all N events are passed to the modifier,
// it is far better to have the bundle fully decoded ahead of time.
protos::pbzero::FtraceEventBundle::Decoder bundle(ftrace_events.as_bytes());
if (!bundle.has_cpu()) {
return base::ErrStatus(
"RedactFtraceEvents: missing field FtraceEventBundle::kCpu.");
}
protozero::ProtoDecoder decoder(ftrace_events.as_bytes());
for (auto it = decoder.ReadField(); it.valid(); it = decoder.ReadField()) {
if (it.id() == protos::pbzero::FtraceEventBundle::kEventFieldNumber) {
OnFtraceEvent(context, bundle, it, message);
} else {
proto_util::AppendField(it, message);
}
}
return base::OkStatus();
}
void RedactFtraceEvents::OnFtraceEvent(
const Context& context,
const protos::pbzero::FtraceEventBundle::Decoder& bundle,
protozero::Field event,
protos::pbzero::FtraceEventBundle* parent_message) const {
PERFETTO_DCHECK(filter_);
PERFETTO_DCHECK(modifier_);
if (event.id() != protos::pbzero::FtraceEventBundle::kEventFieldNumber) {
proto_util::AppendField(event, parent_message);
return;
}
protozero::ProtoDecoder decoder(event.as_bytes());
auto ts_field =
decoder.FindField(protos::pbzero::FtraceEvent::kTimestampFieldNumber);
PERFETTO_DCHECK(ts_field.valid());
auto pid_field =
decoder.FindField(protos::pbzero::FtraceEvent::kPidFieldNumber);
PERFETTO_DCHECK(pid_field.valid());
if (!filter_->Includes(context, event)) {
return;
}
auto cpu = static_cast<int32_t>(bundle.cpu());
auto pid = pid_field.as_int32();
modifier_->Modify(context, ts_field.as_uint64(), cpu, &pid, nullptr);
auto* message = parent_message->add_event();
for (auto it = decoder.ReadField(); it.valid(); it = decoder.ReadField()) {
if (it.id() == protos::pbzero::FtraceEvent::kPidFieldNumber) {
message->set_pid(static_cast<uint32_t>(pid));
} else {
proto_util::AppendField(it, message);
}
}
}
} // namespace perfetto::trace_redaction