xref: /aosp_15_r20/external/perfetto/src/trace_redaction/collect_frame_cookies_unittest.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 
2 /*
3  * Copyright (C) 2024 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #include "src/trace_redaction/collect_frame_cookies.h"
19 #include "perfetto/ext/base/status_or.h"
20 #include "src/base/test/status_matchers.h"
21 #include "test/gtest_and_gmock.h"
22 
23 #include "protos/perfetto/trace/android/frame_timeline_event.gen.h"
24 #include "protos/perfetto/trace/trace_packet.gen.h"
25 #include "protos/perfetto/trace/trace_packet.pbzero.h"
26 
27 namespace perfetto::trace_redaction {
28 namespace {
29 
30 constexpr uint64_t kTimeStep = 1000;
31 
32 constexpr uint64_t kTimestampA = 0;
33 constexpr uint64_t kTimestampB = kTimeStep;
34 constexpr uint64_t kTimestampC = kTimeStep * 2;
35 constexpr uint64_t kTimestampD = kTimeStep * 3;
36 constexpr uint64_t kTimestampE = kTimeStep * 4;
37 
38 constexpr int64_t kCookieA = 1234;
39 constexpr int64_t kCookieB = 2345;
40 
41 // Start at 1, amd not zero, because zero hnas special meaning (system uid).
42 constexpr uint64_t kUidA = 1;
43 
44 constexpr int32_t kPidNone = 10;
45 constexpr int32_t kPidA = 11;
46 
47 enum class FrameCookieType {
48   ExpectedSurface,
49   ExpectedDisplay,
50   ActualSurface,
51   ActualDisplay,
52 };
53 
CreateExpectedSurfaceFrameStart(uint64_t ts,int32_t pid,int64_t cookie)54 protos::gen::TracePacket CreateExpectedSurfaceFrameStart(uint64_t ts,
55                                                          int32_t pid,
56                                                          int64_t cookie) {
57   protos::gen::TracePacket packet;
58   packet.set_timestamp(ts);
59   auto* start = packet.mutable_frame_timeline_event()
60                     ->mutable_expected_surface_frame_start();
61   start->set_cookie(cookie);
62   start->set_pid(pid);
63   return packet;
64 }
65 
CreateActualSurfaceFrameStart(uint64_t ts,int32_t pid,int64_t cookie)66 protos::gen::TracePacket CreateActualSurfaceFrameStart(uint64_t ts,
67                                                        int32_t pid,
68                                                        int64_t cookie) {
69   protos::gen::TracePacket packet;
70   packet.set_timestamp(ts);
71   auto* start = packet.mutable_frame_timeline_event()
72                     ->mutable_actual_surface_frame_start();
73   start->set_cookie(cookie);
74   start->set_pid(pid);
75 
76   return packet;
77 }
78 
CreateExpectedDisplayFrameStart(uint64_t ts,int32_t pid,int64_t cookie)79 protos::gen::TracePacket CreateExpectedDisplayFrameStart(uint64_t ts,
80                                                          int32_t pid,
81                                                          int64_t cookie) {
82   protos::gen::TracePacket packet;
83   packet.set_timestamp(ts);
84   auto* start = packet.mutable_frame_timeline_event()
85                     ->mutable_expected_display_frame_start();
86   start->set_cookie(cookie);
87   start->set_pid(pid);
88 
89   return packet;
90 }
91 
CreateActualDisplayFrameStart(uint64_t ts,int32_t pid,int64_t cookie)92 protos::gen::TracePacket CreateActualDisplayFrameStart(uint64_t ts,
93                                                        int32_t pid,
94                                                        int64_t cookie) {
95   protos::gen::TracePacket packet;
96   packet.set_timestamp(ts);
97   auto* start = packet.mutable_frame_timeline_event()
98                     ->mutable_actual_display_frame_start();
99   start->set_cookie(cookie);
100   start->set_pid(pid);
101 
102   return packet;
103 }
104 
CreateFrameEnd(uint64_t ts,int64_t cookie)105 protos::gen::TracePacket CreateFrameEnd(uint64_t ts, int64_t cookie) {
106   protos::gen::TracePacket packet;
107   packet.set_timestamp(ts);
108 
109   auto* start = packet.mutable_frame_timeline_event()->mutable_frame_end();
110   start->set_cookie(cookie);
111 
112   return packet;
113 }
114 
CreateStartPacket(FrameCookieType type,uint64_t ts,int32_t pid,int64_t cookie)115 protos::gen::TracePacket CreateStartPacket(FrameCookieType type,
116                                            uint64_t ts,
117                                            int32_t pid,
118                                            int64_t cookie) {
119   switch (type) {
120     case FrameCookieType::ExpectedSurface:
121       return CreateExpectedSurfaceFrameStart(ts, pid, cookie);
122     case FrameCookieType::ExpectedDisplay:
123       return CreateExpectedDisplayFrameStart(ts, pid, cookie);
124     case FrameCookieType::ActualSurface:
125       return CreateActualSurfaceFrameStart(ts, pid, cookie);
126     case FrameCookieType::ActualDisplay:
127       return CreateActualDisplayFrameStart(ts, pid, cookie);
128   }
129 
130   PERFETTO_FATAL("Unhandled case. This should never happen.");
131 }
132 
CollectCookies(const std::string & packet,Context * context)133 void CollectCookies(const std::string& packet, Context* context) {
134   protos::pbzero::TracePacket::Decoder decoder(packet);
135 
136   CollectFrameCookies collect_;
137   ASSERT_OK(collect_.Begin(context));
138   ASSERT_OK(collect_.Collect(decoder, context));
139   ASSERT_OK(collect_.End(context));
140 }
141 
142 class FrameCookieTest : public testing::Test {
143  protected:
144   CollectFrameCookies collect_;
145   Context context_;
146 };
147 
TEST_F(FrameCookieTest,ExtractsExpectedSurfaceFrameStart)148 TEST_F(FrameCookieTest, ExtractsExpectedSurfaceFrameStart) {
149   auto packet = CreateExpectedSurfaceFrameStart(kTimestampA, kPidA, kCookieA);
150   auto bytes = packet.SerializeAsString();
151   CollectCookies(bytes, &context_);
152 
153   ASSERT_EQ(context_.global_frame_cookies.size(), 1u);
154 
155   const auto& cookie = context_.global_frame_cookies.back();
156   ASSERT_EQ(cookie.cookie, kCookieA);
157   ASSERT_EQ(cookie.pid, kPidA);
158   ASSERT_EQ(cookie.ts, kTimestampA);
159 }
160 
TEST_F(FrameCookieTest,ExtractsActualSurfaceFrameStart)161 TEST_F(FrameCookieTest, ExtractsActualSurfaceFrameStart) {
162   auto packet = CreateActualSurfaceFrameStart(kTimestampA, kPidA, kCookieA);
163   auto bytes = packet.SerializeAsString();
164   CollectCookies(bytes, &context_);
165 
166   ASSERT_EQ(context_.global_frame_cookies.size(), 1u);
167 
168   const auto& cookie = context_.global_frame_cookies.back();
169   ASSERT_EQ(cookie.cookie, kCookieA);
170   ASSERT_EQ(cookie.pid, kPidA);
171   ASSERT_EQ(cookie.ts, kTimestampA);
172 }
173 
TEST_F(FrameCookieTest,ExtractsExpectedDisplayFrameStart)174 TEST_F(FrameCookieTest, ExtractsExpectedDisplayFrameStart) {
175   auto packet = CreateExpectedDisplayFrameStart(kTimestampA, kPidA, kCookieA);
176   auto bytes = packet.SerializeAsString();
177   CollectCookies(bytes, &context_);
178 
179   ASSERT_EQ(context_.global_frame_cookies.size(), 1u);
180 
181   const auto& cookie = context_.global_frame_cookies.back();
182   ASSERT_EQ(cookie.cookie, kCookieA);
183   ASSERT_EQ(cookie.pid, kPidA);
184   ASSERT_EQ(cookie.ts, kTimestampA);
185 }
186 
TEST_F(FrameCookieTest,ExtractsActualDisplayFrameStart)187 TEST_F(FrameCookieTest, ExtractsActualDisplayFrameStart) {
188   auto packet = CreateActualDisplayFrameStart(kTimestampA, kPidA, kCookieA);
189   auto bytes = packet.SerializeAsString();
190   CollectCookies(bytes, &context_);
191 
192   ASSERT_EQ(context_.global_frame_cookies.size(), 1u);
193 
194   const auto& cookie = context_.global_frame_cookies.back();
195   ASSERT_EQ(cookie.cookie, kCookieA);
196   ASSERT_EQ(cookie.pid, kPidA);
197   ASSERT_EQ(cookie.ts, kTimestampA);
198 }
199 
200 // End events have no influence during the collect phase because they don't have
201 // a direct connection to a process. They're indirectly connected to a pid via a
202 // start event (via a common cookie value).
TEST_F(FrameCookieTest,IgnoresFrameEnd)203 TEST_F(FrameCookieTest, IgnoresFrameEnd) {
204   auto packet = CreateFrameEnd(kTimestampA, kCookieA);
205   auto bytes = packet.SerializeAsString();
206   CollectCookies(bytes, &context_);
207 
208   ASSERT_TRUE(context_.global_frame_cookies.empty());
209 }
210 
211 class ReduceFrameCookiesTest
212     : public testing::Test,
213       public testing::WithParamInterface<FrameCookieType> {
214  protected:
SetUp()215   void SetUp() {
216     context_.package_uid = kUidA;
217 
218     // Time A   +- Time B       +- Time C    +- Time D   +- Time E
219     //          |                            |
220     //          +------------ Pid A ---------+
221     //
222     // The pid will be active from time b to time d. Time A will be used for
223     // "before active". Time C will be used for "while active". Time E will be
224     // used for "after active".
225     context_.timeline = std::make_unique<ProcessThreadTimeline>();
226     context_.timeline->Append(ProcessThreadTimeline::Event::Open(
227         kTimestampB, kPidA, kPidNone, kUidA));
228     context_.timeline->Append(
229         ProcessThreadTimeline::Event::Close(kTimestampD, kPidA));
230     context_.timeline->Sort();
231   }
232 
233   ReduceFrameCookies reduce_;
234   Context context_;
235 };
236 
TEST_P(ReduceFrameCookiesTest,RejectBeforeStart)237 TEST_P(ReduceFrameCookiesTest, RejectBeforeStart) {
238   auto packet = CreateStartPacket(GetParam(), kTimestampA, kPidA, kCookieA);
239   auto bytes = packet.SerializeAsString();
240   CollectCookies(bytes, &context_);
241 
242   ASSERT_OK(reduce_.Build(&context_));
243   ASSERT_FALSE(context_.package_frame_cookies.count(kCookieA));
244 }
245 
TEST_P(ReduceFrameCookiesTest,AcceptAtStart)246 TEST_P(ReduceFrameCookiesTest, AcceptAtStart) {
247   auto packet = CreateStartPacket(GetParam(), kTimestampB, kPidA, kCookieA);
248   auto bytes = packet.SerializeAsString();
249   CollectCookies(bytes, &context_);
250 
251   ASSERT_OK(reduce_.Build(&context_));
252   ASSERT_TRUE(context_.package_frame_cookies.count(kCookieA));
253 }
254 
TEST_P(ReduceFrameCookiesTest,AcceptBetweenStartAndEnd)255 TEST_P(ReduceFrameCookiesTest, AcceptBetweenStartAndEnd) {
256   auto packet = CreateStartPacket(GetParam(), kTimestampC, kPidA, kCookieA);
257   auto bytes = packet.SerializeAsString();
258   CollectCookies(bytes, &context_);
259 
260   ASSERT_OK(reduce_.Build(&context_));
261   ASSERT_TRUE(context_.package_frame_cookies.count(kCookieA));
262 }
263 
TEST_P(ReduceFrameCookiesTest,AcceptAtEnd)264 TEST_P(ReduceFrameCookiesTest, AcceptAtEnd) {
265   auto packet = CreateStartPacket(GetParam(), kTimestampD, kPidA, kCookieA);
266   auto bytes = packet.SerializeAsString();
267   CollectCookies(bytes, &context_);
268 
269   ASSERT_OK(reduce_.Build(&context_));
270   ASSERT_TRUE(context_.package_frame_cookies.count(kCookieA));
271 }
272 
TEST_P(ReduceFrameCookiesTest,RejectAfterEnd)273 TEST_P(ReduceFrameCookiesTest, RejectAfterEnd) {
274   auto packet = CreateStartPacket(GetParam(), kTimestampE, kPidA, kCookieA);
275   auto bytes = packet.SerializeAsString();
276   CollectCookies(bytes, &context_);
277 
278   ASSERT_OK(reduce_.Build(&context_));
279   ASSERT_FALSE(context_.package_frame_cookies.count(kCookieA));
280 }
281 
282 INSTANTIATE_TEST_SUITE_P(Default,
283                          ReduceFrameCookiesTest,
284                          testing::Values(FrameCookieType::ExpectedSurface,
285                                          FrameCookieType::ExpectedDisplay,
286                                          FrameCookieType::ActualSurface,
287                                          FrameCookieType::ActualDisplay));
288 
289 class TransformStartCookiesTest
290     : public testing::Test,
291       public testing::WithParamInterface<FrameCookieType> {
292  protected:
SetUp()293   void SetUp() { context_.package_frame_cookies.insert(kCookieA); }
294 
295   FilterFrameEvents filter_;
296   Context context_;
297 };
298 
TEST_P(TransformStartCookiesTest,RetainStartEvent)299 TEST_P(TransformStartCookiesTest, RetainStartEvent) {
300   auto packet = CreateStartPacket(GetParam(), kTimestampE, kPidA, kCookieA);
301   auto bytes = packet.SerializeAsString();
302 
303   ASSERT_OK(filter_.Transform(context_, &bytes));
304 
305   protos::gen::TracePacket redacted;
306   ASSERT_TRUE(redacted.ParseFromString(bytes));
307 
308   ASSERT_TRUE(redacted.has_frame_timeline_event());
309   const auto& timeline = redacted.frame_timeline_event();
310 
311   int64_t cookie;
312 
313   // Find the cookie from the packet.
314   switch (GetParam()) {
315     case FrameCookieType::ExpectedSurface: {
316       ASSERT_TRUE(timeline.has_expected_surface_frame_start());
317       const auto& start = timeline.expected_surface_frame_start();
318       ASSERT_TRUE(start.has_cookie());
319       cookie = start.cookie();
320 
321       break;
322     }
323 
324     case FrameCookieType::ExpectedDisplay: {
325       ASSERT_TRUE(timeline.has_expected_display_frame_start());
326       const auto& start = timeline.expected_display_frame_start();
327       ASSERT_TRUE(start.has_cookie());
328       cookie = start.cookie();
329 
330       break;
331     }
332 
333     case FrameCookieType::ActualSurface: {
334       ASSERT_TRUE(timeline.has_actual_surface_frame_start());
335       const auto& start = timeline.actual_surface_frame_start();
336       ASSERT_TRUE(start.has_cookie());
337       cookie = start.cookie();
338 
339       break;
340     }
341     case FrameCookieType::ActualDisplay: {
342       ASSERT_TRUE(timeline.has_actual_display_frame_start());
343       const auto& start = timeline.actual_display_frame_start();
344       ASSERT_TRUE(start.has_cookie());
345       cookie = start.cookie();
346 
347       break;
348     }
349   }
350 
351   ASSERT_EQ(cookie, kCookieA);
352 }
353 
TEST_P(TransformStartCookiesTest,DropStartEvent)354 TEST_P(TransformStartCookiesTest, DropStartEvent) {
355   // Even those this packet is using PidA, because CookieA is not in the package
356   // coookie pool, the event should be dropped.
357   auto packet = CreateStartPacket(GetParam(), kTimestampE, kPidA, kCookieB);
358   auto bytes = packet.SerializeAsString();
359 
360   ASSERT_OK(filter_.Transform(context_, &bytes));
361 
362   protos::gen::TracePacket redacted;
363   ASSERT_TRUE(redacted.ParseFromString(bytes));
364 
365   ASSERT_FALSE(redacted.has_frame_timeline_event());
366 }
367 
368 INSTANTIATE_TEST_SUITE_P(Default,
369                          TransformStartCookiesTest,
370                          testing::Values(FrameCookieType::ExpectedSurface,
371                                          FrameCookieType::ExpectedDisplay,
372                                          FrameCookieType::ActualSurface,
373                                          FrameCookieType::ActualDisplay));
374 
375 class TransformEndFrameCookiesTest : public testing::Test {
376  protected:
SetUp()377   void SetUp() { context_.package_frame_cookies.insert(kCookieA); }
378 
379   FilterFrameEvents filter_;
380   Context context_;
381 };
382 
383 // An end event has no pid. The cookie connects it to a pid. If the start event
384 // cookie moved from the global pool into the package pool, then end the end
385 // event should be retained.
TEST_F(TransformStartCookiesTest,Retain)386 TEST_F(TransformStartCookiesTest, Retain) {
387   auto packet = CreateFrameEnd(kTimestampA, kCookieA);
388   auto bytes = packet.SerializeAsString();
389 
390   ASSERT_OK(filter_.Transform(context_, &bytes));
391 
392   protos::gen::TracePacket redacted;
393   ASSERT_TRUE(redacted.ParseFromString(bytes));
394 
395   ASSERT_TRUE(redacted.has_frame_timeline_event());
396   const auto& timeline = redacted.frame_timeline_event();
397 
398   ASSERT_TRUE(timeline.has_frame_end());
399   const auto& end = timeline.frame_end();
400 
401   ASSERT_EQ(end.cookie(), kCookieA);
402 }
403 
TEST_F(TransformStartCookiesTest,Drop)404 TEST_F(TransformStartCookiesTest, Drop) {
405   auto packet = CreateFrameEnd(kTimestampA, kCookieB);
406   auto bytes = packet.SerializeAsString();
407 
408   ASSERT_OK(filter_.Transform(context_, &bytes));
409 
410   protos::gen::TracePacket redacted;
411   ASSERT_TRUE(redacted.ParseFromString(bytes));
412 
413   ASSERT_FALSE(redacted.has_frame_timeline_event());
414 }
415 
416 }  // namespace
417 }  // namespace perfetto::trace_redaction
418