xref: /aosp_15_r20/external/perfetto/src/trace_redaction/redact_sched_events_unittest.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 #include "src/base/test/status_matchers.h"
19 #include "test/gtest_and_gmock.h"
20 
21 #include "protos/perfetto/trace/ftrace/ftrace_event.gen.h"
22 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.gen.h"
23 #include "protos/perfetto/trace/ftrace/sched.gen.h"
24 #include "protos/perfetto/trace/trace.gen.h"
25 #include "protos/perfetto/trace/trace_packet.gen.h"
26 
27 namespace perfetto::trace_redaction {
28 
29 namespace {
30 constexpr uint64_t kUidA = 1;
31 constexpr uint64_t kUidB = 2;
32 constexpr uint64_t kUidC = 3;
33 
34 constexpr int32_t kNoParent = 10;
35 constexpr int32_t kPidA = 11;
36 constexpr int32_t kPidB = 12;
37 constexpr int32_t kPidC = 13;
38 constexpr int32_t kPidD = 14;
39 
40 constexpr int32_t kCpuA = 0;
41 constexpr int32_t kCpuB = 1;
42 constexpr int32_t kCpuC = 2;
43 
44 constexpr uint64_t kHalfStep = 500;
45 constexpr uint64_t kFullStep = kHalfStep * 2;
46 
47 constexpr uint64_t kTimeA = 0;
48 constexpr uint64_t kTimeB = kFullStep;
49 constexpr uint64_t kTimeC = kFullStep * 2;
50 
51 constexpr auto kCommA = "comm-a";
52 constexpr auto kCommB = "comm-b";
53 constexpr auto kCommC = "comm-c";
54 constexpr auto kCommNone = "";
55 
56 template <int32_t new_pid>
57 class ChangePidTo : public PidCommModifier {
58  public:
Modify(const Context & context,uint64_t ts,int32_t,int32_t * pid,std::string *) const59   void Modify(const Context& context,
60               uint64_t ts,
61               int32_t,
62               int32_t* pid,
63               std::string*) const override {
64     PERFETTO_DCHECK(context.timeline);
65     PERFETTO_DCHECK(context.package_uid.has_value());
66     PERFETTO_DCHECK(pid);
67     if (!context.timeline->PidConnectsToUid(ts, *pid, *context.package_uid)) {
68       *pid = new_pid;
69     }
70   }
71 };
72 }  // namespace
73 
74 class RedactSchedSwitchFtraceEventTest : public testing::Test {
75  protected:
SetUp()76   void SetUp() override {
77     // Create a packet where two pids are swapping back-and-forth.
78     auto* bundle = packet_.mutable_ftrace_events();
79     bundle->set_cpu(kCpuA);
80 
81     {
82       auto* event = bundle->add_event();
83 
84       event->set_timestamp(kTimeA);
85       event->set_pid(kPidA);
86 
87       auto* sched_switch = event->mutable_sched_switch();
88       sched_switch->set_prev_comm(kCommA);
89       sched_switch->set_prev_pid(kPidA);
90       sched_switch->set_prev_prio(0);
91       sched_switch->set_prev_state(0);
92       sched_switch->set_next_comm(kCommB);
93       sched_switch->set_next_pid(kPidB);
94       sched_switch->set_next_prio(0);
95     }
96 
97     {
98       auto* event = bundle->add_event();
99 
100       event->set_timestamp(kTimeB);
101       event->set_pid(kPidB);
102 
103       auto* sched_switch = event->mutable_sched_switch();
104       sched_switch->set_prev_comm(kCommB);
105       sched_switch->set_prev_pid(kPidB);
106       sched_switch->set_prev_prio(0);
107       sched_switch->set_prev_state(0);
108       sched_switch->set_next_comm(kCommA);
109       sched_switch->set_next_pid(kPidA);
110       sched_switch->set_next_prio(0);
111     }
112 
113     // PID A and PID B need to be attached to different packages (UID) so that
114     // its possible to include one but not the other.
115     context_.timeline = std::make_unique<ProcessThreadTimeline>();
116     context_.timeline->Append(
117         ProcessThreadTimeline::Event::Open(kTimeA, kPidA, kNoParent, kUidA));
118     context_.timeline->Append(
119         ProcessThreadTimeline::Event::Open(kTimeA, kPidB, kNoParent, kUidB));
120     context_.timeline->Sort();
121 
122     redact_.emplace_modifier<ClearComms>();
123     redact_.emplace_waking_filter<AllowAll>();
124   }
125 
126   protos::gen::TracePacket packet_;
127   Context context_;
128   RedactSchedEvents redact_;
129 };
130 
131 // In this case, the target uid will be UID A. That means the comm values for
132 // PID B should be removed, and the comm values for PID A should remain.
TEST_F(RedactSchedSwitchFtraceEventTest,KeepsTargetCommValues)133 TEST_F(RedactSchedSwitchFtraceEventTest, KeepsTargetCommValues) {
134   context_.package_uid = kUidA;
135 
136   auto packet_buffer = packet_.SerializeAsString();
137 
138   ASSERT_OK(redact_.Transform(context_, &packet_buffer));
139 
140   protos::gen::TracePacket packet;
141   ASSERT_TRUE(packet.ParseFromString(packet_buffer));
142 
143   const auto& bundle = packet.ftrace_events();
144   const auto& events = bundle.event();
145 
146   ASSERT_EQ(events.size(), 2u);
147 
148   ASSERT_EQ(events[0].sched_switch().prev_pid(), kPidA);
149   ASSERT_EQ(events[0].sched_switch().prev_comm(), kCommA);
150 
151   ASSERT_EQ(events[0].sched_switch().next_pid(), kPidB);
152   ASSERT_EQ(events[0].sched_switch().next_comm(), kCommNone);
153 
154   ASSERT_EQ(events[1].sched_switch().prev_pid(), kPidB);
155   ASSERT_EQ(events[1].sched_switch().prev_comm(), kCommNone);
156 
157   ASSERT_EQ(events[1].sched_switch().next_pid(), kPidA);
158   ASSERT_EQ(events[1].sched_switch().next_comm(), kCommA);
159 }
160 
161 // This case is very similar to the "some are connected", expect that it
162 // verifies all comm values will be removed when testing against an unused
163 // uid.
TEST_F(RedactSchedSwitchFtraceEventTest,RemovesAllCommsIfPackageDoesntExist)164 TEST_F(RedactSchedSwitchFtraceEventTest, RemovesAllCommsIfPackageDoesntExist) {
165   context_.package_uid = kUidC;
166 
167   auto packet_buffer = packet_.SerializeAsString();
168 
169   ASSERT_OK(redact_.Transform(context_, &packet_buffer));
170 
171   protos::gen::TracePacket packet;
172   ASSERT_TRUE(packet.ParseFromString(packet_buffer));
173 
174   const auto& bundle = packet.ftrace_events();
175   const auto& events = bundle.event();
176 
177   ASSERT_EQ(events.size(), 2u);
178 
179   ASSERT_EQ(events[0].sched_switch().prev_comm(), kCommNone);
180   ASSERT_EQ(events[0].sched_switch().next_comm(), kCommNone);
181 
182   ASSERT_EQ(events[1].sched_switch().prev_comm(), kCommNone);
183   ASSERT_EQ(events[1].sched_switch().next_comm(), kCommNone);
184 }
185 
186 class RedactCompactSchedSwitchTest : public testing::Test {
187  protected:
SetUp()188   void SetUp() override {
189     // PID A and PID B need to be attached to different packages (UID) so that
190     // its possible to include one but not the other.
191     context_.timeline = std::make_unique<ProcessThreadTimeline>();
192     context_.timeline->Append(
193         ProcessThreadTimeline::Event::Open(kTimeA, kPidA, kNoParent, kUidA));
194     context_.timeline->Append(
195         ProcessThreadTimeline::Event::Open(kTimeA, kPidB, kNoParent, kUidB));
196     context_.timeline->Sort();
197 
198     auto* bundle = packet_.mutable_ftrace_events();
199     bundle->set_cpu(kCpuA);  // All switch events occur on this CPU
200 
201     compact_sched = bundle->mutable_compact_sched();
202 
203     compact_sched->add_intern_table(kCommA);
204     compact_sched->add_intern_table(kCommB);
205 
206     redact_.emplace_modifier<ClearComms>();
207     redact_.emplace_waking_filter<AllowAll>();
208   }
209 
AddSwitchEvent(uint64_t ts,int32_t next_pid,int32_t prev_state,int32_t prio,uint32_t comm)210   void AddSwitchEvent(uint64_t ts,
211                       int32_t next_pid,
212                       int32_t prev_state,
213                       int32_t prio,
214                       uint32_t comm) {
215     compact_sched->add_switch_timestamp(ts);
216     compact_sched->add_switch_next_pid(next_pid);
217     compact_sched->add_switch_prev_state(prev_state);
218     compact_sched->add_switch_next_prio(prio);
219     compact_sched->add_switch_next_comm_index(comm);
220   }
221 
222   protos::gen::TracePacket packet_;
223   protos::gen::FtraceEventBundle::CompactSched* compact_sched;
224 
225   Context context_;
226   RedactSchedEvents redact_;
227 };
228 
TEST_F(RedactCompactSchedSwitchTest,KeepsTargetCommValues)229 TEST_F(RedactCompactSchedSwitchTest, KeepsTargetCommValues) {
230   uint32_t kCommIndexA = 0;
231   uint32_t kCommIndexB = 1;
232 
233   // The new entry will be appended to the table. Another primitive can be used
234   // to reduce the intern string table.
235   uint32_t kCommIndexNone = 2;
236 
237   AddSwitchEvent(kTimeA, kPidA, 0, 0, kCommIndexA);
238   AddSwitchEvent(kTimeB, kPidB, 0, 0, kCommIndexB);
239 
240   context_.package_uid = kUidA;
241 
242   auto packet_buffer = packet_.SerializeAsString();
243 
244   ASSERT_OK(redact_.Transform(context_, &packet_buffer));
245 
246   protos::gen::TracePacket packet;
247   ASSERT_TRUE(packet.ParseFromString(packet_buffer));
248 
249   const auto& bundle = packet.ftrace_events();
250   ASSERT_TRUE(bundle.has_compact_sched());
251 
252   const auto& compact_sched = bundle.compact_sched();
253 
254   // A new entry (empty string) should have been added to the table.
255   ASSERT_EQ(compact_sched.intern_table_size(), 3);
256   ASSERT_EQ(compact_sched.intern_table().back(), kCommNone);
257 
258   ASSERT_EQ(compact_sched.switch_next_comm_index_size(), 2);
259   ASSERT_EQ(compact_sched.switch_next_comm_index().at(0), kCommIndexA);
260   ASSERT_EQ(compact_sched.switch_next_comm_index().at(1), kCommIndexNone);
261 }
262 
263 // If two pids use the same comm, but one pid changes, the shared comm should
264 // still be available.
TEST_F(RedactCompactSchedSwitchTest,ChangingSharedCommonRetainsComm)265 TEST_F(RedactCompactSchedSwitchTest, ChangingSharedCommonRetainsComm) {
266   uint32_t kCommIndexA = 0;
267 
268   AddSwitchEvent(kTimeA, kPidA, 0, 0, kCommIndexA);
269   AddSwitchEvent(kTimeB, kPidB, 0, 0, kCommIndexA);
270 
271   context_.package_uid = kUidA;
272 
273   auto packet_buffer = packet_.SerializeAsString();
274 
275   ASSERT_OK(redact_.Transform(context_, &packet_buffer));
276 
277   protos::gen::TracePacket packet;
278   ASSERT_TRUE(packet.ParseFromString(packet_buffer));
279 
280   const auto& bundle = packet.ftrace_events();
281   ASSERT_TRUE(bundle.has_compact_sched());
282 
283   const auto& compact_sched = bundle.compact_sched();
284 
285   // A new entry should have been appended, but comm A (previously shared)
286   // should still exist in the table.
287   ASSERT_EQ(compact_sched.intern_table_size(), 3);
288   ASSERT_EQ(compact_sched.intern_table().front(), kCommA);
289   ASSERT_EQ(compact_sched.intern_table().back(), kCommNone);
290 }
291 
TEST_F(RedactCompactSchedSwitchTest,RemovesAllCommsIfPackageDoesntExist)292 TEST_F(RedactCompactSchedSwitchTest, RemovesAllCommsIfPackageDoesntExist) {
293   uint32_t kCommIndexA = 0;
294   uint32_t kCommIndexB = 1;
295 
296   // The new entry will be appended to the table. Another primitive can be used
297   // to reduce the intern string table.
298   uint32_t kCommIndexNone = 2;
299 
300   AddSwitchEvent(kTimeA, kPidA, 0, 0, kCommIndexA);
301   AddSwitchEvent(kTimeB, kPidB, 0, 0, kCommIndexB);
302 
303   context_.package_uid = kUidC;
304 
305   auto packet_buffer = packet_.SerializeAsString();
306 
307   ASSERT_OK(redact_.Transform(context_, &packet_buffer));
308 
309   protos::gen::TracePacket packet;
310   ASSERT_TRUE(packet.ParseFromString(packet_buffer));
311 
312   const auto& bundle = packet.ftrace_events();
313   ASSERT_TRUE(bundle.has_compact_sched());
314 
315   const auto& compact_sched = bundle.compact_sched();
316 
317   // A new entry (empty string) should have been added to the table.
318   ASSERT_EQ(compact_sched.intern_table_size(), 3);
319   ASSERT_EQ(compact_sched.intern_table().back(), kCommNone);
320 
321   ASSERT_EQ(compact_sched.switch_next_comm_index_size(), 2);
322   ASSERT_EQ(compact_sched.switch_next_comm_index().at(0), kCommIndexNone);
323   ASSERT_EQ(compact_sched.switch_next_comm_index().at(1), kCommIndexNone);
324 }
325 
TEST_F(RedactCompactSchedSwitchTest,CanChangePid)326 TEST_F(RedactCompactSchedSwitchTest, CanChangePid) {
327   uint32_t kCommIndexA = 0;
328   uint32_t kCommIndexB = 1;
329 
330   AddSwitchEvent(kTimeA, kPidA, 0, 0, kCommIndexA);
331   AddSwitchEvent(kTimeB, kPidB, 0, 0, kCommIndexB);
332 
333   // Because the target is package A, PidA should be remain. PidB should change.
334   context_.package_uid = kUidA;
335 
336   auto packet_buffer = packet_.SerializeAsString();
337 
338   redact_.emplace_modifier<ChangePidTo<kPidC>>();
339 
340   ASSERT_OK(redact_.Transform(context_, &packet_buffer));
341 
342   protos::gen::TracePacket packet;
343   ASSERT_TRUE(packet.ParseFromString(packet_buffer));
344 
345   const auto& bundle = packet.ftrace_events();
346   ASSERT_TRUE(bundle.has_compact_sched());
347 
348   const auto& compact_sched = bundle.compact_sched();
349 
350   // The intern table should not change.
351   ASSERT_EQ(compact_sched.intern_table_size(), 2);
352 
353   ASSERT_EQ(compact_sched.switch_next_pid_size(), 2);
354   ASSERT_EQ(compact_sched.switch_next_pid().at(0), kPidA);
355 
356   // Because Pid B was not connected to Uid A, it should have its pid changed.
357   ASSERT_EQ(compact_sched.switch_next_pid().at(1), kPidC);
358 }
359 
360 class RedactSchedWakingFtraceEventTest : public testing::Test {
361  protected:
SetUp()362   void SetUp() override {
363     // Create a packet where two pids are swapping back-and-forth.
364     auto* bundle = packet_.mutable_ftrace_events();
365     bundle->set_cpu(kCpuA);
366 
367     // Pid A wakes up Pid B at time Time B
368     {
369       auto* event = bundle->add_event();
370 
371       event->set_timestamp(kTimeB);
372       event->set_pid(kPidA);
373 
374       auto* sched_waking = event->mutable_sched_waking();
375       sched_waking->set_comm(kCommB);
376       sched_waking->set_pid(kPidB);
377       sched_waking->set_prio(0);
378       sched_waking->set_success(true);
379       sched_waking->set_target_cpu(kCpuB);
380     }
381 
382     // Pid A wakes up Pid C at time Time C.
383     {
384       auto* event = bundle->add_event();
385 
386       event->set_timestamp(kTimeC);
387       event->set_pid(kPidA);
388 
389       auto* sched_waking = event->mutable_sched_waking();
390       sched_waking->set_comm(kCommC);
391       sched_waking->set_pid(kPidC);
392       sched_waking->set_prio(0);
393       sched_waking->set_success(true);
394       sched_waking->set_target_cpu(kCpuC);
395     }
396 
397     // PID A and PID B need to be attached to different packages (UID) so that
398     // its possible to include one but not the other.
399     context_.timeline = std::make_unique<ProcessThreadTimeline>();
400     context_.timeline->Append(
401         ProcessThreadTimeline::Event::Open(kTimeA, kPidA, kNoParent, kUidA));
402     context_.timeline->Append(
403         ProcessThreadTimeline::Event::Open(kTimeA, kPidB, kNoParent, kUidB));
404     context_.timeline->Append(
405         ProcessThreadTimeline::Event::Open(kTimeA, kPidC, kNoParent, kUidC));
406     context_.timeline->Sort();
407 
408     redact.emplace_modifier<ClearComms>();
409     redact.emplace_waking_filter<AllowAll>();
410   }
411 
412   protos::gen::TracePacket packet_;
413   Context context_;
414 
415   RedactSchedEvents redact;
416 };
417 
TEST_F(RedactSchedWakingFtraceEventTest,WakeeKeepsCommWhenConnectedToPackage)418 TEST_F(RedactSchedWakingFtraceEventTest, WakeeKeepsCommWhenConnectedToPackage) {
419   context_.package_uid = kUidB;
420 
421   auto packet_buffer = packet_.SerializeAsString();
422 
423   ASSERT_OK(redact.Transform(context_, &packet_buffer));
424 
425   protos::gen::TracePacket packet;
426   ASSERT_TRUE(packet.ParseFromString(packet_buffer));
427 
428   const auto& bundle = packet.ftrace_events();
429   const auto& events = bundle.event();
430 
431   ASSERT_EQ(events.size(), 2u);
432 
433   ASSERT_EQ(events.front().sched_waking().comm(), kCommB);
434   ASSERT_EQ(events.back().sched_waking().comm(), kCommNone);
435 }
436 
TEST_F(RedactSchedWakingFtraceEventTest,WakeeLosesCommWhenNotConnectedToPackage)437 TEST_F(RedactSchedWakingFtraceEventTest,
438        WakeeLosesCommWhenNotConnectedToPackage) {
439   context_.package_uid = kUidA;
440 
441   auto packet_buffer = packet_.SerializeAsString();
442 
443   ASSERT_OK(redact.Transform(context_, &packet_buffer));
444 
445   protos::gen::TracePacket packet;
446   ASSERT_TRUE(packet.ParseFromString(packet_buffer));
447 
448   const auto& bundle = packet.ftrace_events();
449   const auto& events = bundle.event();
450 
451   ASSERT_EQ(events.size(), 2u);
452 
453   ASSERT_EQ(events.front().sched_waking().comm(), kCommNone);
454   ASSERT_EQ(events.back().sched_waking().comm(), kCommNone);
455 }
456 
TEST_F(RedactSchedWakingFtraceEventTest,WakeeKeepsPidWhenConnectedToPackage)457 TEST_F(RedactSchedWakingFtraceEventTest, WakeeKeepsPidWhenConnectedToPackage) {
458   redact.emplace_modifier<ChangePidTo<kPidD>>();
459 
460   context_.package_uid = kUidB;
461 
462   auto packet_buffer = packet_.SerializeAsString();
463 
464   ASSERT_OK(redact.Transform(context_, &packet_buffer));
465 
466   protos::gen::TracePacket packet;
467   ASSERT_TRUE(packet.ParseFromString(packet_buffer));
468 
469   const auto& bundle = packet.ftrace_events();
470   const auto& events = bundle.event();
471 
472   ASSERT_EQ(events.size(), 2u);
473 
474   ASSERT_EQ(events.front().sched_waking().pid(), kPidB);
475 
476   // Because Pid C was not connected to Uid B, it should have its pid changed.
477   ASSERT_EQ(events.back().sched_waking().pid(), kPidD);
478 }
479 
TEST_F(RedactSchedWakingFtraceEventTest,WakeeLosesPidWhenNotConnectedToPackage)480 TEST_F(RedactSchedWakingFtraceEventTest,
481        WakeeLosesPidWhenNotConnectedToPackage) {
482   redact.emplace_modifier<ChangePidTo<kPidD>>();
483 
484   context_.package_uid = kUidA;
485 
486   auto packet_buffer = packet_.SerializeAsString();
487 
488   ASSERT_OK(redact.Transform(context_, &packet_buffer));
489 
490   protos::gen::TracePacket packet;
491   ASSERT_TRUE(packet.ParseFromString(packet_buffer));
492 
493   const auto& bundle = packet.ftrace_events();
494   const auto& events = bundle.event();
495 
496   ASSERT_EQ(events.size(), 2u);
497 
498   // Both pids should have changed.
499   ASSERT_EQ(events.at(0).sched_waking().pid(), kPidD);
500   ASSERT_EQ(events.at(1).sched_waking().pid(), kPidD);
501 }
502 
TEST_F(RedactSchedWakingFtraceEventTest,WakerPidIsLeftUnaffected)503 TEST_F(RedactSchedWakingFtraceEventTest, WakerPidIsLeftUnaffected) {
504   redact.emplace_modifier<ChangePidTo<kPidD>>();
505 
506   context_.package_uid = kUidB;
507 
508   auto packet_buffer = packet_.SerializeAsString();
509 
510   ASSERT_OK(redact.Transform(context_, &packet_buffer));
511 
512   protos::gen::TracePacket packet;
513   ASSERT_TRUE(packet.ParseFromString(packet_buffer));
514 
515   const auto& bundle = packet.ftrace_events();
516   const auto& events = bundle.event();
517 
518   ASSERT_EQ(events.size(), 2u);
519 
520   // The waker in the ftrace event waking event should change, but by another
521   // primitive. This case only appears in the ftrace events because the waker is
522   // inferred in the comp sched case.
523   ASSERT_EQ(events.at(0).pid(), static_cast<uint32_t>(kPidA));
524   ASSERT_EQ(events.at(1).pid(), static_cast<uint32_t>(kPidA));
525 }
526 
527 class FilterCompactSchedWakingEventsTest : public testing::Test {
528  protected:
SetUp()529   void SetUp() {
530     // Uid B is used instead of Uid A because Pid A, belonging to Uid A, is the
531     // waker. Pid B and Pid C are the wakees.
532     context_.package_uid = kUidB;
533 
534     // FilterSchedWakingEvents expects a timeline because most
535     // FilterSchedWakingEvents::Filter filters will need one. However, the
536     // filter used in this test doesn't require one.
537     context_.timeline = std::make_unique<ProcessThreadTimeline>();
538 
539     context_.timeline->Append(
540         ProcessThreadTimeline::Event::Open(kTimeA, kPidA, kNoParent, kUidA));
541     context_.timeline->Append(
542         ProcessThreadTimeline::Event::Open(kTimeA, kPidB, kNoParent, kUidB));
543     context_.timeline->Append(
544         ProcessThreadTimeline::Event::Open(kTimeA, kPidC, kNoParent, kUidC));
545     context_.timeline->Sort();
546 
547     // Default to "allow all" and "change nothing" so a test only needs to
548     // override what they need.
549     redact_.emplace_waking_filter<AllowAll>();
550     redact_.emplace_modifier<DoNothing>();
551   }
552 
553   Context context_;
554   RedactSchedEvents redact_;
555 };
556 
557 // Builds a simple ftrace bundle that contains two ftrace events:
558 //
559 //  - Pid A wakes up pid B
560 //  - Pid A wakes up pid C
561 //
562 // Because compact sched uses associative arrays, the data will look like:
563 //
564 //  - Time | PID   | CPU   | *
565 //    -----+-------+-------+---
566 //    0.5  | kPidB | kCpuB |
567 //    1.5  | kPidC | kCpuB |
568 //
569 // Because the filter will only keep events where pid is being waked, only the
570 // first of the two events should remain.
TEST_F(FilterCompactSchedWakingEventsTest,FilterCompactSched)571 TEST_F(FilterCompactSchedWakingEventsTest, FilterCompactSched) {
572   redact_.emplace_waking_filter<ConnectedToPackage>();
573 
574   protos::gen::TracePacket packet_builder;
575   packet_builder.mutable_ftrace_events()->set_cpu(kCpuA);
576 
577   auto* compact_sched =
578       packet_builder.mutable_ftrace_events()->mutable_compact_sched();
579 
580   compact_sched->add_intern_table(kCommA);
581 
582   // Implementation detail: The timestamp, target cpu, and pid matter. The other
583   // values are copied to the output, but have no influence over the internal
584   // logic.
585   compact_sched->add_waking_comm_index(0);
586   compact_sched->add_waking_common_flags(0);
587   compact_sched->add_waking_prio(0);
588   compact_sched->add_waking_timestamp(kHalfStep);
589   compact_sched->add_waking_target_cpu(kCpuB);
590   compact_sched->add_waking_pid(kPidB);
591 
592   compact_sched->add_waking_comm_index(0);
593   compact_sched->add_waking_common_flags(0);
594   compact_sched->add_waking_prio(0);
595   compact_sched->add_waking_timestamp(kFullStep + kHalfStep);
596   compact_sched->add_waking_target_cpu(kCpuB);
597   compact_sched->add_waking_pid(kPidC);
598 
599   auto bytes = packet_builder.SerializeAsString();
600   ASSERT_OK(redact_.Transform(context_, &bytes));
601 
602   protos::gen::TracePacket packet;
603   packet.ParseFromString(bytes);
604 
605   ASSERT_TRUE(packet.has_ftrace_events());
606 
607   const auto& events = packet.ftrace_events();
608   ASSERT_TRUE(events.has_compact_sched());
609 
610   // All events not from Pid B should be removed. In this case, that means the
611   // event from Pid C should be dropped.
612   ASSERT_EQ(events.compact_sched().waking_pid_size(), 1);
613   ASSERT_EQ(events.compact_sched().waking_pid().at(0), kPidB);
614 }
615 
616 // Timing information is based off delta-time values. When a row is removed
617 // from the compact sched arrays, downstream timing data is corrupted. The
618 // delta value of removed rows should be rolled into the next row.
TEST_F(FilterCompactSchedWakingEventsTest,CorrectsTimeWhenRemovingWakingEvents)619 TEST_F(FilterCompactSchedWakingEventsTest,
620        CorrectsTimeWhenRemovingWakingEvents) {
621   // All the times are delta times. The commented times are the absolute times.
622   std::array<uint64_t, 7> before = {
623       0,
624       kFullStep,  // 1
625       kFullStep,  // 2
626       kHalfStep,  // 2.5
627       kHalfStep,  // 3
628       kFullStep,  // 4
629       kFullStep,  // 5
630   };
631 
632   // These are the times that should be drop
633   std::array<uint64_t, 3> drop_times = {
634       kFullStep,  // 6
635       kFullStep,  // 7
636       kHalfStep,  // 7.5
637   };
638 
639   // When the times are dropped, the times removed from drop_times should be
640   // rolling into the first time. So it should got from 1 unit to 3.5 units.
641   std::array<uint64_t, 2> after = {
642       kFullStep,  // 8
643       kFullStep,  // 9
644   };
645 
646   protos::gen::TracePacket packet_builder;
647   packet_builder.mutable_ftrace_events()->set_cpu(kCpuA);
648 
649   auto* compact_sched =
650       packet_builder.mutable_ftrace_events()->mutable_compact_sched();
651 
652   compact_sched->add_intern_table(kCommA);
653 
654   // Before and after, this events should not be affected.
655   for (auto time : before) {
656     compact_sched->add_waking_comm_index(0);
657     compact_sched->add_waking_common_flags(0);
658     compact_sched->add_waking_prio(0);
659     compact_sched->add_waking_timestamp(time);
660     compact_sched->add_waking_target_cpu(kCpuB);
661     compact_sched->add_waking_pid(kPidB);
662   }
663 
664   // Use pid B so that these times will be dropped.
665   for (auto time : drop_times) {
666     compact_sched->add_waking_comm_index(0);
667     compact_sched->add_waking_common_flags(0);
668     compact_sched->add_waking_prio(0);
669     compact_sched->add_waking_timestamp(time);
670     compact_sched->add_waking_target_cpu(kCpuB);
671     compact_sched->add_waking_pid(kPidC);
672   }
673 
674   // After redaction, these events should still exist, but the first event in
675   // this series, the timestamp should be larger (before of the dropped events).
676   for (auto time : after) {
677     compact_sched->add_waking_comm_index(0);
678     compact_sched->add_waking_common_flags(0);
679     compact_sched->add_waking_prio(0);
680     compact_sched->add_waking_timestamp(time);
681     compact_sched->add_waking_target_cpu(kCpuB);
682     compact_sched->add_waking_pid(kPidB);
683   }
684 
685   auto bytes = packet_builder.SerializeAsString();
686 
687   redact_.emplace_waking_filter<ConnectedToPackage>();
688   ASSERT_OK(redact_.Transform(context_, &bytes));
689 
690   protos::gen::TracePacket packet;
691   ASSERT_TRUE(packet.ParseFromString(bytes));
692 
693   ASSERT_TRUE(packet.has_ftrace_events());
694   const auto& events = packet.ftrace_events();
695 
696   ASSERT_TRUE(events.has_compact_sched());
697   const auto& times = packet.ftrace_events().compact_sched().waking_timestamp();
698 
699   ASSERT_EQ(times.size(), 9u);  // i.e. before + after
700 
701   // Nothing in the before should have changed.
702   for (size_t i = 0; i < before.size(); ++i) {
703     ASSERT_EQ(times[i], before[i]);
704   }
705 
706   // Sum of all dropped event time.
707   ASSERT_EQ(drop_times.size(), 3u);
708   auto lost_time = drop_times[0] + drop_times[1] + drop_times[2];
709 
710   // Only the first of the two "after" events should have changed.
711   ASSERT_EQ(times[before.size()], after[0] + lost_time);
712   ASSERT_EQ(times[before.size() + 1], after[1]);
713 }
714 
715 // This is an implementation detail. When an event is removed, the gap is
716 // collapsed into the next event by tracking the error created by removing the
717 // event. If implemented incorrectly, flipping between keep and remove will
718 // break as the error will not be reset correctly.
TEST_F(FilterCompactSchedWakingEventsTest,RemovingWakingEventsThrashing)719 TEST_F(FilterCompactSchedWakingEventsTest, RemovingWakingEventsThrashing) {
720   //   X  : Drop this event
721   //  [ ] : This is an event
722   //   =  : Number of time units
723   //
724   //           X          X          X
725   //  [==][==][=][==][==][=][==][==][=]
726   //
727   // Events are going to follow a "keep, keep, drop" pattern. All keep events
728   // will be full time units. All drop events will be half time units.
729   //
730   // It is key to notice that the series ends on a removed event. This creates a
731   // special: remove an event without an event to accept the error.
732   std::array<uint64_t, 9> before = {
733       0,          // abs time 0
734       kFullStep,  // abs time 1
735       kHalfStep,  // abs time 1.5
736 
737       kFullStep,  // abs time 2.5
738       kFullStep,  // abs time 3.5
739       kHalfStep,  // abs time 4
740 
741       kFullStep,  // abs time 5
742       kFullStep,  // abs time 6
743       kHalfStep,  // abs time 6.5
744   };
745 
746   std::array<uint64_t, 6> after = {
747       0,                      // abs time 0
748       kFullStep,              // abs time 1
749       kFullStep + kHalfStep,  // abs time 2.5
750       kFullStep,              // abs time 3.5
751       kFullStep + kHalfStep,  // abs time 5
752       kFullStep,              // abs time 6
753   };
754 
755   protos::gen::TracePacket packet_builder;
756   packet_builder.mutable_ftrace_events()->set_cpu(kCpuA);
757 
758   auto* compact_sched =
759       packet_builder.mutable_ftrace_events()->mutable_compact_sched();
760 
761   compact_sched->add_intern_table(kCommA);
762 
763   for (size_t i = 0; i < before.size(); ++i) {
764     auto time = before[i];
765 
766     compact_sched->add_waking_comm_index(0);
767     compact_sched->add_waking_common_flags(0);
768     compact_sched->add_waking_prio(0);
769     compact_sched->add_waking_timestamp(time);
770     compact_sched->add_waking_target_cpu(kCpuB);
771 
772     // The pattern is "keep, keep, drop", therefore, PID B > B > C ...
773     if (i % 3 == 2) {
774       compact_sched->add_waking_pid(kPidC);
775     } else {
776       compact_sched->add_waking_pid(kPidB);
777     }
778   }
779 
780   auto bytes = packet_builder.SerializeAsString();
781 
782   redact_.emplace_waking_filter<ConnectedToPackage>();
783   ASSERT_OK(redact_.Transform(context_, &bytes));
784 
785   protos::gen::TracePacket packet;
786   ASSERT_TRUE(packet.ParseFromString(bytes));
787 
788   ASSERT_TRUE(packet.has_ftrace_events());
789   const auto& events = packet.ftrace_events();
790 
791   ASSERT_TRUE(events.has_compact_sched());
792   const auto& times = packet.ftrace_events().compact_sched().waking_timestamp();
793 
794   ASSERT_EQ(times.size(), after.size());
795 
796   for (size_t i = 0; i < after.size(); ++i) {
797     ASSERT_EQ(times[i], after[i]);
798   }
799 }
800 
801 }  // namespace perfetto::trace_redaction
802