xref: /aosp_15_r20/external/perfetto/src/trace_redaction/redact_process_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_process_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/ftrace/sched.pbzero.h"
29 #include "protos/perfetto/trace/ftrace/task.pbzero.h"
30 
31 namespace perfetto::trace_redaction {
32 
Transform(const Context & context,std::string * packet) const33 base::Status RedactProcessEvents::Transform(const Context& context,
34                                             std::string* packet) const {
35   PERFETTO_DCHECK(modifier_);
36   PERFETTO_DCHECK(filter_);
37 
38   if (!context.timeline) {
39     return base::ErrStatus("RedactProcessEvents: missing timeline.");
40   }
41 
42   if (!context.package_uid.has_value()) {
43     return base::ErrStatus("RedactProcessEvents: missing package uid.");
44   }
45 
46   if (!packet || packet->empty()) {
47     return base::ErrStatus("RedactProcessEvents: null or empty packet.");
48   }
49 
50   protozero::ProtoDecoder packet_decoder(*packet);
51 
52   protozero::HeapBuffered<protos::pbzero::TracePacket> message;
53 
54   for (auto it = packet_decoder.ReadField(); it.valid();
55        it = packet_decoder.ReadField()) {
56     if (it.id() == protos::pbzero::TracePacket::kFtraceEventsFieldNumber) {
57       RETURN_IF_ERROR(
58           OnFtraceEvents(context, it.as_bytes(), message->set_ftrace_events()));
59     } else {
60       proto_util::AppendField(it, message.get());
61     }
62   }
63 
64   packet->assign(message.SerializeAsString());
65   return base::OkStatus();
66 }
67 
OnFtraceEvents(const Context & context,protozero::ConstBytes bytes,protos::pbzero::FtraceEventBundle * message) const68 base::Status RedactProcessEvents::OnFtraceEvents(
69     const Context& context,
70     protozero::ConstBytes bytes,
71     protos::pbzero::FtraceEventBundle* message) const {
72   protozero::ProtoDecoder decoder(bytes);
73 
74   auto cpu =
75       decoder.FindField(protos::pbzero::FtraceEventBundle::kCpuFieldNumber);
76 
77   std::string shared_comm;
78 
79   for (auto it = decoder.ReadField(); it.valid(); it = decoder.ReadField()) {
80     if (it.id() == protos::pbzero::FtraceEventBundle::kEventFieldNumber) {
81       RETURN_IF_ERROR(OnFtraceEvent(context, cpu.as_int32(), it.as_bytes(),
82                                     &shared_comm, message->add_event()));
83     } else {
84       proto_util::AppendField(it, message);
85     }
86   }
87 
88   return base::OkStatus();
89 }
90 
OnFtraceEvent(const Context & context,int32_t cpu,protozero::ConstBytes bytes,std::string * shared_comm,protos::pbzero::FtraceEvent * message) const91 base::Status RedactProcessEvents::OnFtraceEvent(
92     const Context& context,
93     int32_t cpu,
94     protozero::ConstBytes bytes,
95     std::string* shared_comm,
96     protos::pbzero::FtraceEvent* message) const {
97   PERFETTO_DCHECK(shared_comm);
98   PERFETTO_DCHECK(message);
99 
100   protozero::ProtoDecoder decoder(bytes);
101 
102   auto ts =
103       decoder.FindField(protos::pbzero::FtraceEvent::kTimestampFieldNumber);
104 
105   if (!ts.valid()) {
106     return base::ErrStatus("RedactProcessEvents: missing FtraceEvent %d",
107                            protos::pbzero::FtraceEvent::kTimestampFieldNumber);
108   }
109 
110   for (auto it = decoder.ReadField(); it.valid(); it = decoder.ReadField()) {
111     switch (it.id()) {
112       case protos::pbzero::FtraceEvent::kSchedProcessFreeFieldNumber:
113         RETURN_IF_ERROR(OnProcessFree(context, ts.as_uint64(), cpu,
114                                       it.as_bytes(), shared_comm, message));
115         break;
116       case protos::pbzero::FtraceEvent::kTaskNewtaskFieldNumber:
117         RETURN_IF_ERROR(OnNewTask(context, ts.as_uint64(), cpu, it.as_bytes(),
118                                   shared_comm, message));
119         break;
120       case protos::pbzero::FtraceEvent::kTaskRenameFieldNumber:
121         RETURN_IF_ERROR(OnProcessRename(context, ts.as_uint64(), cpu,
122                                         it.as_bytes(), shared_comm, message));
123         break;
124       case protos::pbzero::FtraceEvent::kPrintFieldNumber:
125         RETURN_IF_ERROR(OnPrint(context, ts.as_uint64(), bytes, message));
126         break;
127       case protos::pbzero::FtraceEvent::kSuspendResumeFieldNumber:
128         RETURN_IF_ERROR(
129             OnSuspendResume(context, ts.as_uint64(), bytes, message));
130         break;
131       case protos::pbzero::FtraceEvent::kSchedBlockedReasonFieldNumber:
132         RETURN_IF_ERROR(
133             OnSchedBlockedReason(context, ts.as_uint64(), bytes, message));
134         break;
135       default:
136         proto_util::AppendField(it, message);
137         break;
138     }
139   }
140 
141   return base::OkStatus();
142 }
143 
OnProcessFree(const Context & context,uint64_t ts,int32_t cpu,protozero::ConstBytes bytes,std::string * shared_comm,protos::pbzero::FtraceEvent * parent_message) const144 base::Status RedactProcessEvents::OnProcessFree(
145     const Context& context,
146     uint64_t ts,
147     int32_t cpu,
148     protozero::ConstBytes bytes,
149     std::string* shared_comm,
150     protos::pbzero::FtraceEvent* parent_message) const {
151   PERFETTO_DCHECK(shared_comm);
152   PERFETTO_DCHECK(parent_message);
153 
154   protos::pbzero::SchedProcessFreeFtraceEvent::Decoder decoder(bytes);
155 
156   if (!decoder.has_pid()) {
157     return base::ErrStatus(
158         "RedactProcessEvents: missing SchedProcessFreeFtraceEvent %d",
159         protos::pbzero::SchedProcessFreeFtraceEvent::kPidFieldNumber);
160   }
161 
162   if (!decoder.has_comm()) {
163     return base::ErrStatus(
164         "RedactProcessEvents: missing SchedProcessFreeFtraceEvent %d",
165         protos::pbzero::SchedProcessFreeFtraceEvent::kCommFieldNumber);
166   }
167 
168   if (!decoder.has_prio()) {
169     return base::ErrStatus(
170         "RedactProcessEvents: missing SchedProcessFreeFtraceEvent %d",
171         protos::pbzero::SchedProcessFreeFtraceEvent::kPrioFieldNumber);
172   }
173 
174   auto pid = decoder.pid();
175   auto comm = decoder.comm();
176   auto prio = decoder.prio();
177 
178   PERFETTO_DCHECK(filter_);
179   if (!filter_->Includes(context, ts, pid)) {
180     return base::OkStatus();
181   }
182 
183   shared_comm->assign(comm.data, comm.size);
184 
185   PERFETTO_DCHECK(modifier_);
186   modifier_->Modify(context, ts, cpu, &pid, shared_comm);
187 
188   auto* message = parent_message->set_sched_process_free();
189   message->set_pid(pid);
190   message->set_comm(*shared_comm);
191   message->set_prio(prio);
192 
193   return base::OkStatus();
194 }
195 
OnNewTask(const Context & context,uint64_t ts,int32_t cpu,protozero::ConstBytes bytes,std::string * shared_comm,protos::pbzero::FtraceEvent * parent_message) const196 base::Status RedactProcessEvents::OnNewTask(
197     const Context& context,
198     uint64_t ts,
199     int32_t cpu,
200     protozero::ConstBytes bytes,
201     std::string* shared_comm,
202     protos::pbzero::FtraceEvent* parent_message) const {
203   PERFETTO_DCHECK(shared_comm);
204   PERFETTO_DCHECK(parent_message);
205 
206   protos::pbzero::TaskNewtaskFtraceEvent::Decoder decoder(bytes);
207 
208   if (!decoder.has_clone_flags()) {
209     return base::ErrStatus(
210         "RedactProcessEvents: missing TaskNewtaskFtraceEvent %d",
211         protos::pbzero::TaskNewtaskFtraceEvent::kCloneFlagsFieldNumber);
212   }
213 
214   if (!decoder.has_comm()) {
215     return base::ErrStatus(
216         "RedactProcessEvents: missing TaskNewtaskFtraceEvent %d",
217         protos::pbzero::TaskNewtaskFtraceEvent::kCommFieldNumber);
218   }
219 
220   if (!decoder.has_oom_score_adj()) {
221     return base::ErrStatus(
222         "RedactProcessEvents: missing TaskNewtaskFtraceEvent %d",
223         protos::pbzero::TaskNewtaskFtraceEvent::kOomScoreAdjFieldNumber);
224   }
225 
226   if (!decoder.has_pid()) {
227     return base::ErrStatus(
228         "RedactProcessEvents: missing TaskNewtaskFtraceEvent %d",
229         protos::pbzero::TaskNewtaskFtraceEvent::kPidFieldNumber);
230   }
231 
232   auto clone_flags = decoder.clone_flags();
233   auto comm = decoder.comm();
234   auto omm_score_adj = decoder.oom_score_adj();
235   auto pid = decoder.pid();
236 
237   PERFETTO_DCHECK(filter_);
238   if (!filter_->Includes(context, ts, pid)) {
239     return base::OkStatus();
240   }
241 
242   shared_comm->assign(comm.data, comm.size);
243 
244   PERFETTO_DCHECK(modifier_);
245   modifier_->Modify(context, ts, cpu, &pid, shared_comm);
246 
247   auto* message = parent_message->set_task_newtask();
248   message->set_clone_flags(clone_flags);
249   message->set_comm(*shared_comm);
250   message->set_oom_score_adj(omm_score_adj);
251   message->set_pid(pid);
252 
253   return base::OkStatus();
254 }
255 
OnProcessRename(const Context & context,uint64_t ts,int32_t cpu,protozero::ConstBytes bytes,std::string * shared_comm,protos::pbzero::FtraceEvent * parent_message) const256 base::Status RedactProcessEvents::OnProcessRename(
257     const Context& context,
258     uint64_t ts,
259     int32_t cpu,
260     protozero::ConstBytes bytes,
261     std::string* shared_comm,
262     protos::pbzero::FtraceEvent* parent_message) const {
263   PERFETTO_DCHECK(shared_comm);
264   PERFETTO_DCHECK(parent_message);
265 
266   protos::pbzero::TaskRenameFtraceEvent::Decoder decoder(bytes);
267 
268   if (!decoder.has_pid()) {
269     return base::ErrStatus(
270         "RedactProcessEvents: missing TaskRenameFtraceEvent %d",
271         protos::pbzero::TaskRenameFtraceEvent::kPidFieldNumber);
272   }
273 
274   if (!decoder.has_newcomm()) {
275     return base::ErrStatus(
276         "RedactProcessEvents: missing TaskRenameFtraceEvent %d",
277         protos::pbzero::TaskRenameFtraceEvent::kNewcommFieldNumber);
278   }
279 
280   if (!decoder.has_oldcomm()) {
281     return base::ErrStatus(
282         "RedactProcessEvents: missing TaskRenameFtraceEvent %d",
283         protos::pbzero::TaskRenameFtraceEvent::kOldcommFieldNumber);
284   }
285 
286   if (!decoder.has_oom_score_adj()) {
287     return base::ErrStatus(
288         "RedactProcessEvents: missing TaskRenameFtraceEvent %d",
289         protos::pbzero::TaskRenameFtraceEvent::kOomScoreAdjFieldNumber);
290   }
291 
292   auto pid = decoder.pid();
293   auto new_comm = decoder.newcomm();
294   auto old_comm = decoder.oldcomm();
295   auto oom_score_adj = decoder.oom_score_adj();
296 
297   PERFETTO_DCHECK(filter_);
298   if (!filter_->Includes(context, ts, pid)) {
299     return base::OkStatus();
300   }
301 
302   auto* message = parent_message->set_task_rename();
303 
304   auto noop_pid = pid;
305 
306   shared_comm->assign(old_comm.data, old_comm.size);
307 
308   PERFETTO_DCHECK(modifier_);
309   modifier_->Modify(context, ts, cpu, &noop_pid, shared_comm);
310 
311   // Write the old-comm now so shared_comm can be used new-comm.
312   message->set_oldcomm(*shared_comm);
313 
314   shared_comm->assign(new_comm.data, new_comm.size);
315 
316   PERFETTO_DCHECK(modifier_);
317   modifier_->Modify(context, ts, cpu, &pid, shared_comm);
318 
319   message->set_newcomm(*shared_comm);
320 
321   // Because the same modification is used for each comm, the resulting pids
322   // should be the same.
323   PERFETTO_DCHECK(noop_pid == pid);
324 
325   message->set_pid(pid);
326   message->set_oom_score_adj(oom_score_adj);
327 
328   return base::OkStatus();
329 }
330 
OnPrint(const Context & context,uint64_t ts,protozero::ConstBytes event_bytes,protos::pbzero::FtraceEvent * parent_message) const331 base::Status RedactProcessEvents::OnPrint(
332     const Context& context,
333     uint64_t ts,
334     protozero::ConstBytes event_bytes,
335     protos::pbzero::FtraceEvent* parent_message) const {
336   PERFETTO_DCHECK(parent_message);
337 
338   protozero::ProtoDecoder decoder(event_bytes);
339 
340   auto pid = decoder.FindField(protos::pbzero::FtraceEvent::kPidFieldNumber);
341   if (!pid.valid()) {
342     return base::ErrStatus("RedactProcessEvents: missing FtraceEvent %u",
343                            pid.id());
344   }
345 
346   auto print =
347       decoder.FindField(protos::pbzero::FtraceEvent::kPrintFieldNumber);
348   if (!print.valid()) {
349     return base::ErrStatus("RedactProcessEvents: missing FtraceEvent %u",
350                            print.id());
351   }
352 
353   if (filter_->Includes(context, ts, pid.as_int32())) {
354     proto_util::AppendField(print, parent_message);
355   }
356 
357   return base::OkStatus();
358 }
359 
OnSuspendResume(const Context & context,uint64_t ts,protozero::ConstBytes event_bytes,protos::pbzero::FtraceEvent * parent_message) const360 base::Status RedactProcessEvents::OnSuspendResume(
361     const Context& context,
362     uint64_t ts,
363     protozero::ConstBytes event_bytes,
364     protos::pbzero::FtraceEvent* parent_message) const {
365   PERFETTO_DCHECK(parent_message);
366 
367   // Values are taken from "suspend_period.textproto". These values would
368   // ideally be provided via the context, but until there are multiple sources,
369   // they can be here.
370   constexpr std::array<std::string_view, 3> kValidActions = {
371       "syscore_suspend", "syscore_resume", "timekeeping_freeze"};
372 
373   protozero::ProtoDecoder decoder(event_bytes);
374 
375   auto pid = decoder.FindField(protos::pbzero::FtraceEvent::kPidFieldNumber);
376   if (!pid.valid()) {
377     return base::ErrStatus("RedactProcessEvents: missing FtraceEvent::kPid");
378   }
379 
380   auto suspend_resume_field =
381       decoder.FindField(protos::pbzero::FtraceEvent::kSuspendResumeFieldNumber);
382   if (!suspend_resume_field.valid()) {
383     return base::ErrStatus(
384         "RedactProcessEvents: missing FtraceEvent::kSuspendResume");
385   }
386 
387   protos::pbzero::SuspendResumeFtraceEvent::Decoder suspend_resume(
388       suspend_resume_field.as_bytes());
389 
390   auto action = suspend_resume.action();
391   std::string_view action_str(action.data, action.size);
392 
393   // Do the allow list first because it should be cheaper (e.g. array look-up vs
394   // timeline query).
395   if (std::find(kValidActions.begin(), kValidActions.end(), action_str) !=
396       kValidActions.end()) {
397     if (filter_->Includes(context, ts, pid.as_int32())) {
398       proto_util::AppendField(suspend_resume_field, parent_message);
399     }
400   }
401 
402   return base::OkStatus();
403 }
404 
OnSchedBlockedReason(const Context & context,uint64_t ts,protozero::ConstBytes event_bytes,protos::pbzero::FtraceEvent * parent_message) const405 base::Status RedactProcessEvents::OnSchedBlockedReason(
406     const Context& context,
407     uint64_t ts,
408     protozero::ConstBytes event_bytes,
409     protos::pbzero::FtraceEvent* parent_message) const {
410   PERFETTO_DCHECK(parent_message);
411 
412   protos::pbzero::FtraceEvent::Decoder decoder(event_bytes);
413 
414   auto pid = decoder.FindField(protos::pbzero::FtraceEvent::kPidFieldNumber);
415   if (!pid.valid()) {
416     return base::ErrStatus("RedactProcessEvents: missing FtraceEvent::kPid");
417   }
418 
419   auto blocked_reason_field = decoder.FindField(
420       protos::pbzero::FtraceEvent::kSchedBlockedReasonFieldNumber);
421   if (!blocked_reason_field.valid()) {
422     return base::ErrStatus(
423         "RedactProcessEvents: missing FtraceEvent::kSchedBlockedReason");
424   }
425 
426   protos::pbzero::SchedBlockedReasonFtraceEvent::Decoder blocking_reason(
427       blocked_reason_field.as_bytes());
428 
429   auto has_fields = {
430       blocking_reason.has_caller(),
431       blocking_reason.has_io_wait(),
432       blocking_reason.has_pid(),
433   };
434 
435   if (std::find(has_fields.begin(), has_fields.end(), false) !=
436       has_fields.end()) {
437     return base::ErrStatus(
438         "RedactProcessEvents: missing SchedBlockedReasonFtraceEvent::*");
439   }
440 
441   // The semantics here is similar to waking events (i.e. event.pid is the
442   // blocker, and sched_blocked_reason.pid is the blockee).
443   // sched_blocked_reason.pid only has meaning when the pid is not merged. If
444   // pid was merged, it could have conflicting blocking events.
445   if (filter_->Includes(context, ts, blocking_reason.pid())) {
446     proto_util::AppendField(blocked_reason_field, parent_message);
447   }
448 
449   return base::OkStatus();
450 }
451 
452 }  // namespace perfetto::trace_redaction
453