xref: /aosp_15_r20/external/perfetto/src/trace_redaction/redact_ftrace_events.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "src/trace_redaction/redact_ftrace_events.h"
18 
19 #include <string>
20 
21 #include "perfetto/protozero/scattered_heap_buffer.h"
22 #include "src/trace_processor/util/status_macros.h"
23 #include "src/trace_redaction/proto_util.h"
24 
25 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
26 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
27 #include "protos/perfetto/trace/ftrace/power.pbzero.h"
28 #include "protos/perfetto/trace/trace.pbzero.h"
29 
30 namespace perfetto::trace_redaction {
31 
32 FtraceEventFilter::~FtraceEventFilter() = default;
33 
Includes(const Context &,protozero::Field event) const34 bool FilterFtraceUsingSuspendResume::Includes(const Context&,
35                                               protozero::Field event) const {
36   // Values are taken from "suspend_period.textproto". These values would
37   // ideally be provided via the context, but until there are multiple sources,
38   // they can be here.
39   constexpr std::string_view kSyscoreSuspend = "syscore_suspend";
40   constexpr std::string_view kSyscoreResume = "syscore_resume";
41   constexpr std::string_view kTimekeepingFreeze = "timekeeping_freeze";
42 
43   protozero::ProtoDecoder event_decoder(event.as_bytes());
44 
45   // It's not a suspend-resume event, defer the decision to another filter.
46   auto suspend_resume = event_decoder.FindField(
47       protos::pbzero::FtraceEvent::kSuspendResumeFieldNumber);
48 
49   if (!suspend_resume.valid()) {
50     return true;
51   }
52 
53   protozero::ProtoDecoder suspend_resume_decoder(suspend_resume.as_bytes());
54 
55   auto action = suspend_resume_decoder.FindField(
56       protos::pbzero::SuspendResumeFtraceEvent::kActionFieldNumber);
57 
58   // If a suspend-resume has no action, there is nothing to redact, so it is
59   // safe to passthrough.
60   if (!action.valid()) {
61     return true;
62   }
63 
64   std::string_view action_str(action.as_string().data, action.size());
65 
66   return kSyscoreSuspend == action_str || kSyscoreResume == action_str ||
67          kTimekeepingFreeze == action_str;
68 }
69 
Includes(const Context & context,protozero::Field event) const70 bool FilterRss::Includes(const Context& context, protozero::Field event) const {
71   protos::pbzero::FtraceEvent::Decoder event_decoder(event.as_bytes());
72 
73   if (event_decoder.has_rss_stat_throttled() || event_decoder.has_rss_stat()) {
74     // The event's pid is unsigned, but tids are always signed.
75     auto pid = static_cast<int32_t>(event_decoder.pid());
76     return context.timeline->PidConnectsToUid(event_decoder.timestamp(), pid,
77                                               *context.package_uid);
78   }
79 
80   return true;
81 }
82 
Transform(const Context & context,std::string * packet) const83 base::Status RedactFtraceEvents::Transform(const Context& context,
84                                            std::string* packet) const {
85   if (packet == nullptr || packet->empty()) {
86     return base::ErrStatus("RedactFtraceEvents: null or empty packet.");
87   }
88 
89   protozero::ProtoDecoder packet_decoder(*packet);
90   auto ftrace_events = packet_decoder.FindField(
91       protos::pbzero::TracePacket::kFtraceEventsFieldNumber);
92 
93   if (!ftrace_events.valid()) {
94     return base::OkStatus();
95   }
96 
97   protozero::ProtoDecoder decoder(*packet);
98 
99   protozero::HeapBuffered<protos::pbzero::TracePacket> message;
100 
101   for (auto it = decoder.ReadField(); it.valid(); it = decoder.ReadField()) {
102     if (it.id() == protos::pbzero::TracePacket::kFtraceEventsFieldNumber) {
103       RETURN_IF_ERROR(
104           OnFtraceEvents(context, it, message->set_ftrace_events()));
105     } else {
106       proto_util::AppendField(it, message.get());
107     }
108   }
109 
110   packet->assign(message.SerializeAsString());
111 
112   return base::OkStatus();
113 }
114 
OnFtraceEvents(const Context & context,protozero::Field ftrace_events,protos::pbzero::FtraceEventBundle * message) const115 base::Status RedactFtraceEvents::OnFtraceEvents(
116     const Context& context,
117     protozero::Field ftrace_events,
118     protos::pbzero::FtraceEventBundle* message) const {
119   // If there are N ftrace events, and all N events are passed to the modifier,
120   // it is far better to have the bundle fully decoded ahead of time.
121   protos::pbzero::FtraceEventBundle::Decoder bundle(ftrace_events.as_bytes());
122 
123   if (!bundle.has_cpu()) {
124     return base::ErrStatus(
125         "RedactFtraceEvents: missing field FtraceEventBundle::kCpu.");
126   }
127 
128   protozero::ProtoDecoder decoder(ftrace_events.as_bytes());
129 
130   for (auto it = decoder.ReadField(); it.valid(); it = decoder.ReadField()) {
131     if (it.id() == protos::pbzero::FtraceEventBundle::kEventFieldNumber) {
132       OnFtraceEvent(context, bundle, it, message);
133     } else {
134       proto_util::AppendField(it, message);
135     }
136   }
137 
138   return base::OkStatus();
139 }
140 
OnFtraceEvent(const Context & context,const protos::pbzero::FtraceEventBundle::Decoder & bundle,protozero::Field event,protos::pbzero::FtraceEventBundle * parent_message) const141 void RedactFtraceEvents::OnFtraceEvent(
142     const Context& context,
143     const protos::pbzero::FtraceEventBundle::Decoder& bundle,
144     protozero::Field event,
145     protos::pbzero::FtraceEventBundle* parent_message) const {
146   PERFETTO_DCHECK(filter_);
147   PERFETTO_DCHECK(modifier_);
148 
149   if (event.id() != protos::pbzero::FtraceEventBundle::kEventFieldNumber) {
150     proto_util::AppendField(event, parent_message);
151     return;
152   }
153 
154   protozero::ProtoDecoder decoder(event.as_bytes());
155 
156   auto ts_field =
157       decoder.FindField(protos::pbzero::FtraceEvent::kTimestampFieldNumber);
158   PERFETTO_DCHECK(ts_field.valid());
159 
160   auto pid_field =
161       decoder.FindField(protos::pbzero::FtraceEvent::kPidFieldNumber);
162   PERFETTO_DCHECK(pid_field.valid());
163 
164   if (!filter_->Includes(context, event)) {
165     return;
166   }
167 
168   auto cpu = static_cast<int32_t>(bundle.cpu());
169   auto pid = pid_field.as_int32();
170 
171   modifier_->Modify(context, ts_field.as_uint64(), cpu, &pid, nullptr);
172 
173   auto* message = parent_message->add_event();
174 
175   for (auto it = decoder.ReadField(); it.valid(); it = decoder.ReadField()) {
176     if (it.id() == protos::pbzero::FtraceEvent::kPidFieldNumber) {
177       message->set_pid(static_cast<uint32_t>(pid));
178     } else {
179       proto_util::AppendField(it, message);
180     }
181   }
182 }
183 
184 }  // namespace perfetto::trace_redaction
185