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