xref: /aosp_15_r20/external/perfetto/src/trace_redaction/redact_sched_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_sched_events.h"
18 
19 #include "perfetto/protozero/scattered_heap_buffer.h"
20 #include "src/trace_processor/util/status_macros.h"
21 #include "src/trace_redaction/proto_util.h"
22 
23 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
24 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
25 #include "protos/perfetto/trace/ftrace/sched.pbzero.h"
26 
27 namespace perfetto::trace_redaction {
28 
29 namespace {
IsTrue(bool value)30 bool IsTrue(bool value) {
31   return value;
32 }
33 
34 // Copy a field from 'decoder' to 'message' if the field can be found. Returns
35 // false if the field cannot be found.
Passthrough(protozero::ProtoDecoder & decoder,uint32_t field_id,protozero::Message * message)36 bool Passthrough(protozero::ProtoDecoder& decoder,
37                  uint32_t field_id,
38                  protozero::Message* message) {
39   auto field = decoder.FindField(field_id);
40 
41   if (field.valid()) {
42     proto_util::AppendField(field, message);
43     return true;
44   }
45 
46   return false;
47 }
48 }  // namespace
49 
Push(const char * data,size_t size)50 int64_t InternTable::Push(const char* data, size_t size) {
51   std::string_view outer(data, size);
52 
53   for (size_t i = 0; i < interned_comms_.size(); ++i) {
54     auto view = interned_comms_[i];
55 
56     if (view == outer) {
57       return static_cast<int64_t>(i);
58     }
59   }
60 
61   // No room for the new string, reject the request.
62   if (comms_length_ + size > comms_.size()) {
63     return -1;
64   }
65 
66   auto* head = comms_.data() + comms_length_;
67 
68   // Important note, the null byte is not copied.
69   memcpy(head, data, size);
70   comms_length_ += size;
71 
72   size_t id = interned_comms_.size();
73   interned_comms_.emplace_back(head, size);
74 
75   return static_cast<int64_t>(id);
76 }
77 
Find(size_t index) const78 std::string_view InternTable::Find(size_t index) const {
79   if (index < interned_comms_.size()) {
80     return interned_comms_[index];
81   }
82 
83   return {};
84 }
85 
86 // Redact sched switch trace events in an ftrace event bundle:
87 //
88 //  event {
89 //    timestamp: 6702093744772646
90 //    pid: 0
91 //    sched_switch {
92 //      prev_comm: "swapper/0"
93 //      prev_pid: 0
94 //      prev_prio: 120
95 //      prev_state: 0
96 //      next_comm: "writer"
97 //      next_pid: 23020
98 //      next_prio: 96
99 //    }
100 //  }
101 //
102 // In the above message, it should be noted that "event.pid" will always be
103 // equal to "event.sched_switch.prev_pid".
104 //
105 // "ftrace_event_bundle_message" is the ftrace event bundle (contains a
106 // collection of ftrace event messages) because data in a sched_switch message
107 // is needed in order to know if the event should be added to the bundle.
108 
Transform(const Context & context,std::string * packet) const109 base::Status RedactSchedEvents::Transform(const Context& context,
110                                           std::string* packet) const {
111   PERFETTO_DCHECK(modifier_);
112   PERFETTO_DCHECK(waking_filter_);
113 
114   if (!context.timeline) {
115     return base::ErrStatus("RedactSchedEvents: missing timeline.");
116   }
117 
118   if (!context.package_uid.has_value()) {
119     return base::ErrStatus("RedactSchedEvents: missing package uid.");
120   }
121 
122   if (!packet || packet->empty()) {
123     return base::ErrStatus("RedactSchedEvents: null or empty packet.");
124   }
125 
126   protozero::HeapBuffered<protos::pbzero::TracePacket> message;
127   protozero::ProtoDecoder decoder(*packet);
128 
129   for (auto field = decoder.ReadField(); field.valid();
130        field = decoder.ReadField()) {
131     if (field.id() == protos::pbzero::TracePacket::kFtraceEventsFieldNumber) {
132       RETURN_IF_ERROR(
133           OnFtraceEvents(context, field, message->set_ftrace_events()));
134     } else {
135       proto_util::AppendField(field, message.get());
136     }
137   }
138 
139   packet->assign(message.SerializeAsString());
140 
141   return base::OkStatus();
142 }
143 
OnFtraceEvents(const Context & context,protozero::Field ftrace_events,protos::pbzero::FtraceEventBundle * message) const144 base::Status RedactSchedEvents::OnFtraceEvents(
145     const Context& context,
146     protozero::Field ftrace_events,
147     protos::pbzero::FtraceEventBundle* message) const {
148   PERFETTO_DCHECK(ftrace_events.id() ==
149                   protos::pbzero::TracePacket::kFtraceEventsFieldNumber);
150 
151   protozero::ProtoDecoder decoder(ftrace_events.as_bytes());
152 
153   auto cpu =
154       decoder.FindField(protos::pbzero::FtraceEventBundle::kCpuFieldNumber);
155   if (!cpu.valid()) {
156     return base::ErrStatus(
157         "RedactSchedEvents: missing cpu in ftrace event bundle.");
158   }
159 
160   for (auto field = decoder.ReadField(); field.valid();
161        field = decoder.ReadField()) {
162     if (field.id() == protos::pbzero::FtraceEventBundle::kEventFieldNumber) {
163       RETURN_IF_ERROR(
164           OnFtraceEvent(context, cpu.as_int32(), field, message->add_event()));
165       continue;
166     }
167 
168     if (field.id() ==
169         protos::pbzero::FtraceEventBundle::kCompactSchedFieldNumber) {
170       protos::pbzero::FtraceEventBundle::CompactSched::Decoder comp_sched(
171           field.as_bytes());
172       RETURN_IF_ERROR(OnCompSched(context, cpu.as_int32(), comp_sched,
173                                   message->set_compact_sched()));
174       continue;
175     }
176 
177     proto_util::AppendField(field, message);
178   }
179 
180   return base::OkStatus();
181 }
182 
OnFtraceEvent(const Context & context,int32_t cpu,protozero::Field ftrace_event,protos::pbzero::FtraceEvent * message) const183 base::Status RedactSchedEvents::OnFtraceEvent(
184     const Context& context,
185     int32_t cpu,
186     protozero::Field ftrace_event,
187     protos::pbzero::FtraceEvent* message) const {
188   PERFETTO_DCHECK(ftrace_event.id() ==
189                   protos::pbzero::FtraceEventBundle::kEventFieldNumber);
190 
191   protozero::ProtoDecoder decoder(ftrace_event.as_bytes());
192 
193   auto ts =
194       decoder.FindField(protos::pbzero::FtraceEvent::kTimestampFieldNumber);
195   if (!ts.valid()) {
196     return base::ErrStatus(
197         "RedactSchedEvents: missing timestamp in ftrace event.");
198   }
199 
200   std::string scratch_str;
201 
202   for (auto field = decoder.ReadField(); field.valid();
203        field = decoder.ReadField()) {
204     switch (field.id()) {
205       case protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber: {
206         protos::pbzero::SchedSwitchFtraceEvent::Decoder sched_switch(
207             field.as_bytes());
208         RETURN_IF_ERROR(OnFtraceEventSwitch(context, ts.as_uint64(), cpu,
209                                             sched_switch, &scratch_str,
210                                             message->set_sched_switch()));
211         break;
212       }
213 
214       case protos::pbzero::FtraceEvent::kSchedWakingFieldNumber: {
215         protos::pbzero::SchedWakingFtraceEvent::Decoder sched_waking(
216             field.as_bytes());
217         RETURN_IF_ERROR(OnFtraceEventWaking(
218             context, ts.as_uint64(), cpu, sched_waking, &scratch_str, message));
219         break;
220       }
221 
222       default: {
223         proto_util::AppendField(field, message);
224         break;
225       }
226     }
227   }
228 
229   return base::OkStatus();
230 }
231 
OnFtraceEventSwitch(const Context & context,uint64_t ts,int32_t cpu,protos::pbzero::SchedSwitchFtraceEvent::Decoder & sched_switch,std::string * scratch_str,protos::pbzero::SchedSwitchFtraceEvent * message) const232 base::Status RedactSchedEvents::OnFtraceEventSwitch(
233     const Context& context,
234     uint64_t ts,
235     int32_t cpu,
236     protos::pbzero::SchedSwitchFtraceEvent::Decoder& sched_switch,
237     std::string* scratch_str,
238     protos::pbzero::SchedSwitchFtraceEvent* message) const {
239   PERFETTO_DCHECK(modifier_);
240   PERFETTO_DCHECK(scratch_str);
241   PERFETTO_DCHECK(message);
242 
243   std::array<bool, 7> has_fields = {
244       sched_switch.has_prev_comm(), sched_switch.has_prev_pid(),
245       sched_switch.has_prev_prio(), sched_switch.has_prev_state(),
246       sched_switch.has_next_comm(), sched_switch.has_next_pid(),
247       sched_switch.has_next_prio()};
248 
249   if (!std::all_of(has_fields.begin(), has_fields.end(), IsTrue)) {
250     return base::ErrStatus(
251         "RedactSchedEvents: missing required SchedSwitchFtraceEvent "
252         "field.");
253   }
254 
255   auto prev_pid = sched_switch.prev_pid();
256   auto prev_comm = sched_switch.prev_comm();
257 
258   auto next_pid = sched_switch.next_pid();
259   auto next_comm = sched_switch.next_comm();
260 
261   // There are 7 values in a sched switch message. Since 4 of the 7 can be
262   // replaced, it is easier/cleaner to go value-by-value. Go in proto-defined
263   // order.
264 
265   scratch_str->assign(prev_comm.data, prev_comm.size);
266 
267   modifier_->Modify(context, ts, cpu, &prev_pid, scratch_str);
268 
269   message->set_prev_comm(*scratch_str);                // FieldNumber = 1
270   message->set_prev_pid(prev_pid);                     // FieldNumber = 2
271   message->set_prev_prio(sched_switch.prev_prio());    // FieldNumber = 3
272   message->set_prev_state(sched_switch.prev_state());  // FieldNumber = 4
273 
274   scratch_str->assign(next_comm.data, next_comm.size);
275 
276   modifier_->Modify(context, ts, cpu, &next_pid, scratch_str);
277 
278   message->set_next_comm(*scratch_str);              // FieldNumber = 5
279   message->set_next_pid(next_pid);                   // FieldNumber = 6
280   message->set_next_prio(sched_switch.next_prio());  // FieldNumber = 7
281 
282   return base::OkStatus();
283 }
284 
285 // Redact sched waking trace events in a ftrace event bundle:
286 //
287 //  event {
288 //    timestamp: 6702093787823849
289 //    pid: 814                      <-- waker
290 //    sched_waking {
291 //      comm: "surfaceflinger"
292 //      pid: 756                    <-- target
293 //      prio: 97
294 //      success: 1
295 //      target_cpu: 2
296 //    }
297 //  }
OnFtraceEventWaking(const Context & context,uint64_t ts,int32_t cpu,protos::pbzero::SchedWakingFtraceEvent::Decoder & sched_waking,std::string * scratch_str,protos::pbzero::FtraceEvent * parent_message) const298 base::Status RedactSchedEvents::OnFtraceEventWaking(
299     const Context& context,
300     uint64_t ts,
301     int32_t cpu,
302     protos::pbzero::SchedWakingFtraceEvent::Decoder& sched_waking,
303     std::string* scratch_str,
304     protos::pbzero::FtraceEvent* parent_message) const {
305   PERFETTO_DCHECK(modifier_);
306   PERFETTO_DCHECK(scratch_str);
307   PERFETTO_DCHECK(parent_message);
308 
309   std::array<bool, 5> has_fields = {
310       sched_waking.has_comm(), sched_waking.has_pid(), sched_waking.has_prio(),
311       sched_waking.has_success(), sched_waking.has_target_cpu()};
312 
313   if (!std::all_of(has_fields.begin(), has_fields.end(), IsTrue)) {
314     return base::ErrStatus(
315         "RedactSchedEvents: missing required SchedWakingFtraceEvent "
316         "field.");
317   }
318 
319   auto pid = sched_waking.pid();
320 
321   if (!waking_filter_->Includes(context, ts, pid)) {
322     return base::OkStatus();
323   }
324 
325   auto comm = sched_waking.comm();
326 
327   // There are 5 values in a sched switch message. Since 2 of the 5 can be
328   // replaced, it is easier/cleaner to go value-by-value. Go in proto-defined
329   // order.
330 
331   scratch_str->assign(comm.data, comm.size);
332 
333   modifier_->Modify(context, ts, cpu, &pid, scratch_str);
334 
335   auto message = parent_message->set_sched_waking();
336   message->set_comm(*scratch_str);                     // FieldNumber = 1
337   message->set_pid(pid);                               // FieldNumber = 2
338   message->set_prio(sched_waking.prio());              // FieldNumber = 3
339   message->set_success(sched_waking.success());        // FieldNumber = 4
340   message->set_target_cpu(sched_waking.target_cpu());  // FieldNumber = 5
341 
342   return base::OkStatus();
343 }
344 
OnCompSched(const Context & context,int32_t cpu,protos::pbzero::FtraceEventBundle::CompactSched::Decoder & comp_sched,protos::pbzero::FtraceEventBundle::CompactSched * message) const345 base::Status RedactSchedEvents::OnCompSched(
346     const Context& context,
347     int32_t cpu,
348     protos::pbzero::FtraceEventBundle::CompactSched::Decoder& comp_sched,
349     protos::pbzero::FtraceEventBundle::CompactSched* message) const {
350   // Populate the intern table once; it will be used by both sched and waking.
351   InternTable intern_table;
352 
353   for (auto it = comp_sched.intern_table(); it; ++it) {
354     auto chars = it->as_string();
355     auto index = intern_table.Push(chars.data, chars.size);
356 
357     if (index < 0) {
358       return base::ErrStatus(
359           "RedactSchedEvents: failed to insert string into intern "
360           "table.");
361     }
362   }
363 
364   std::array<bool, 5> has_switch_fields = {
365       comp_sched.has_switch_timestamp(),
366       comp_sched.has_switch_prev_state(),
367       comp_sched.has_switch_next_pid(),
368       comp_sched.has_switch_next_prio(),
369       comp_sched.has_switch_next_comm_index(),
370   };
371 
372   if (std::any_of(has_switch_fields.begin(), has_switch_fields.end(), IsTrue)) {
373     RETURN_IF_ERROR(
374         OnCompSchedSwitch(context, cpu, comp_sched, &intern_table, message));
375   }
376 
377   std::array<bool, 6> has_waking_fields = {
378       comp_sched.has_waking_timestamp(),  comp_sched.has_waking_pid(),
379       comp_sched.has_waking_target_cpu(), comp_sched.has_waking_prio(),
380       comp_sched.has_waking_comm_index(), comp_sched.has_waking_common_flags(),
381   };
382 
383   if (std::any_of(has_waking_fields.begin(), has_waking_fields.end(), IsTrue)) {
384     RETURN_IF_ERROR(
385         OnCompactSchedWaking(context, comp_sched, &intern_table, message));
386   }
387 
388   // IMPORTANT: The intern table can only be added after switch and waking
389   // because switch and/or waking can/will modify the intern table.
390   for (auto view : intern_table.values()) {
391     message->add_intern_table(view.data(), view.size());
392   }
393 
394   return base::OkStatus();
395 }
396 
OnCompSchedSwitch(const Context & context,int32_t cpu,protos::pbzero::FtraceEventBundle::CompactSched::Decoder & comp_sched,InternTable * intern_table,protos::pbzero::FtraceEventBundle::CompactSched * message) const397 base::Status RedactSchedEvents::OnCompSchedSwitch(
398     const Context& context,
399     int32_t cpu,
400     protos::pbzero::FtraceEventBundle::CompactSched::Decoder& comp_sched,
401     InternTable* intern_table,
402     protos::pbzero::FtraceEventBundle::CompactSched* message) const {
403   PERFETTO_DCHECK(modifier_);
404   PERFETTO_DCHECK(message);
405 
406   std::array<bool, 6> has_fields = {
407       comp_sched.has_intern_table(),
408       comp_sched.has_switch_timestamp(),
409       comp_sched.has_switch_prev_state(),
410       comp_sched.has_switch_next_pid(),
411       comp_sched.has_switch_next_prio(),
412       comp_sched.has_switch_next_comm_index(),
413   };
414 
415   if (!std::all_of(has_fields.begin(), has_fields.end(), IsTrue)) {
416     return base::ErrStatus(
417         "RedactSchedEvents: missing required FtraceEventBundle::CompactSched "
418         "switch field.");
419   }
420 
421   std::string scratch_str;
422 
423   protozero::PackedVarInt packed_comm;
424   protozero::PackedVarInt packed_pid;
425 
426   // The first it_ts value is an absolute value, all other values are delta
427   // values.
428   uint64_t ts = 0;
429 
430   std::array<bool, 3> parse_errors = {false, false, false};
431 
432   auto it_ts = comp_sched.switch_timestamp(&parse_errors.at(0));
433   auto it_pid = comp_sched.switch_next_pid(&parse_errors.at(1));
434   auto it_comm = comp_sched.switch_next_comm_index(&parse_errors.at(2));
435 
436   while (it_ts && it_pid && it_comm) {
437     ts += *it_ts;
438 
439     auto pid = *it_pid;
440 
441     auto comm_index = *it_comm;
442     auto comm = intern_table->Find(comm_index);
443 
444     scratch_str.assign(comm);
445 
446     modifier_->Modify(context, ts, cpu, &pid, &scratch_str);
447 
448     auto found = intern_table->Push(scratch_str.data(), scratch_str.size());
449 
450     if (found < 0) {
451       return base::ErrStatus(
452           "RedactSchedEvents: failed to insert string into intern table.");
453     }
454 
455     packed_comm.Append(found);
456     packed_pid.Append(pid);
457 
458     ++it_ts;
459     ++it_pid;
460     ++it_comm;
461   }
462 
463   if (std::any_of(parse_errors.begin(), parse_errors.end(), IsTrue)) {
464     return base::ErrStatus(
465         "RedactSchedEvents: error reading FtraceEventBundle::CompactSched.");
466   }
467 
468   if (it_ts || it_pid || it_comm) {
469     return base::ErrStatus(
470         "RedactSchedEvents: uneven associative arrays in "
471         "FtraceEventBundle::CompactSched (switch).");
472   }
473 
474   message->set_switch_next_pid(packed_pid);
475   message->set_switch_next_comm_index(packed_comm);
476 
477   // There's a lot of data in a compact sched message. Most of it is packed data
478   // and most of the data is not going to change. To avoid unpacking, doing
479   // nothing, and then packing... cheat. Find the fields and pass them as opaque
480   // blobs.
481   //
482   // kInternTableFieldNumber:         The intern table will be modified by both
483   //                                  switch events and waking events. It will
484   //                                  be written elsewhere.
485   //
486   // kSwitchNextPidFieldNumber:       The switch pid will change during thread
487   //                                  merging.
488   //
489   // kSwitchNextCommIndexFieldNumber: The switch comm value will change when
490   //                                  clearing thread names and replaced
491   //                                  during thread merging.
492 
493   auto passed_through = {
494       Passthrough(comp_sched,
495                   protos::pbzero::FtraceEventBundle::CompactSched::
496                       kSwitchTimestampFieldNumber,
497                   message),
498       Passthrough(comp_sched,
499                   protos::pbzero::FtraceEventBundle::CompactSched::
500                       kSwitchPrevStateFieldNumber,
501                   message),
502       Passthrough(comp_sched,
503                   protos::pbzero::FtraceEventBundle::CompactSched::
504                       kSwitchNextPrioFieldNumber,
505                   message)};
506 
507   if (!std::all_of(passed_through.begin(), passed_through.end(), IsTrue)) {
508     return base::ErrStatus(
509         "RedactSchedEvents: missing required "
510         "FtraceEventBundle::CompactSched switch field.");
511   }
512 
513   return base::OkStatus();
514 }
515 
OnCompactSchedWaking(const Context & context,protos::pbzero::FtraceEventBundle::CompactSched::Decoder & compact_sched,InternTable * intern_table,protos::pbzero::FtraceEventBundle::CompactSched * compact_sched_message) const516 base::Status RedactSchedEvents::OnCompactSchedWaking(
517     const Context& context,
518     protos::pbzero::FtraceEventBundle::CompactSched::Decoder& compact_sched,
519     InternTable* intern_table,
520     protos::pbzero::FtraceEventBundle::CompactSched* compact_sched_message)
521     const {
522   protozero::PackedVarInt var_comm_index;
523   protozero::PackedVarInt var_common_flags;
524   protozero::PackedVarInt var_pid;
525   protozero::PackedVarInt var_prio;
526   protozero::PackedVarInt var_target_cpu;
527   protozero::PackedVarInt var_timestamp;
528 
529   // Time is expressed as delta time, for example:
530   //
531   //         Event: A          B     C      D
532   // Absolute Time: 20         30    35     41
533   //                |          |     |      |
534   //    Delta Time: 20         10    5      6
535   //
536   // When an event is removed, for example, event B, delta times are off:
537   //
538   //               Event:  A          *     C      D
539   //       Absolute Time: 20         30    35     41
540   //                       |          |     |      |
541   //          Delta Time: 20          *     5      6
542   //                       |                |      |
543   // Effective Abs. Time: 20               25     31
544   //               Error:  0               10     10
545   //
546   // To address this issue, delta times are added into a bucket. The bucket is
547   // drained each time an event is retained. If an event is dropped, its time
548   // is added to the bucket, but the bucket won't be drained until a retained
549   // event drains it.
550   uint64_t ts_bucket = 0;
551   uint64_t ts_absolute = 0;
552 
553   std::string comm;
554 
555   std::array<bool, 7> parse_errors = {!compact_sched.has_intern_table(),
556                                       false,
557                                       false,
558                                       false,
559                                       false,
560                                       false,
561                                       false};
562 
563   // A note on readability, because the waking iterators are the primary focus,
564   // they won't have a "waking" prefix.
565   auto it_comm_index = compact_sched.waking_comm_index(&parse_errors.at(1));
566   auto it_common_flags = compact_sched.waking_common_flags(&parse_errors.at(2));
567   auto it_pid = compact_sched.waking_pid(&parse_errors.at(3));
568   auto it_prio = compact_sched.waking_prio(&parse_errors.at(4));
569   auto it_target_cpu = compact_sched.waking_target_cpu(&parse_errors.at(5));
570   auto it_timestamp = compact_sched.waking_timestamp(&parse_errors.at(6));
571 
572   while (it_comm_index && it_common_flags && it_pid && it_prio &&
573          it_target_cpu && it_timestamp) {
574     ts_bucket += *it_timestamp;  // add time to the bucket
575     ts_absolute += *it_timestamp;
576 
577     if (waking_filter_->Includes(context, ts_absolute, *it_pid)) {
578       // Now that the waking event will be kept, it can be modified using the
579       // same rules as switch events.
580       auto pid = *it_pid;
581       comm.assign(intern_table->Find(*it_comm_index));
582       modifier_->Modify(context, ts_absolute, *it_target_cpu, &pid, &comm);
583 
584       auto comm_it = intern_table->Push(comm.data(), comm.size());
585 
586       var_comm_index.Append(comm_it);
587       var_common_flags.Append(*it_common_flags);
588       var_pid.Append(pid);
589       var_prio.Append(*it_prio);
590       var_target_cpu.Append(*it_target_cpu);
591       var_timestamp.Append(ts_bucket);
592 
593       ts_bucket = 0;  // drain the whole bucket.
594     }
595 
596     ++it_comm_index;
597     ++it_common_flags;
598     ++it_pid;
599     ++it_prio;
600     ++it_target_cpu;
601     ++it_timestamp;
602   }
603 
604   if (std::any_of(parse_errors.begin(), parse_errors.end(), IsTrue)) {
605     return base::ErrStatus(
606         "RedactSchedEvents: failed to parse FtraceEventBundle::CompactSched.");
607   }
608 
609   if (it_comm_index || it_common_flags || it_pid || it_prio || it_target_cpu ||
610       it_timestamp) {
611     return base::ErrStatus(
612         "RedactSchedEvents: uneven associative arrays in "
613         "FtraceEventBundle::CompactSched (waking).");
614   }
615 
616   compact_sched_message->set_waking_comm_index(var_comm_index);
617   compact_sched_message->set_waking_common_flags(var_common_flags);
618   compact_sched_message->set_waking_pid(var_pid);
619   compact_sched_message->set_waking_prio(var_prio);
620   compact_sched_message->set_waking_target_cpu(var_target_cpu);
621   compact_sched_message->set_waking_timestamp(var_timestamp);
622 
623   return base::OkStatus();
624 }
625 
626 }  // namespace perfetto::trace_redaction
627