| /* |
| * 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 |