• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "src/base/test/status_matchers.h"
20 #include "src/trace_redaction/collect_timeline_events.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/android/frame_timeline_event.pbzero.h"
25 #include "protos/perfetto/trace/trace_packet.gen.h"
26 #include "protos/perfetto/trace/trace_packet.pbzero.h"
27 
28 namespace perfetto::trace_redaction {
29 namespace {
30 
31 constexpr uint64_t kTimeStep = 1000;
32 
33 constexpr uint64_t kTimestampA = 0;
34 constexpr uint64_t kTimestampB = kTimeStep;
35 constexpr uint64_t kTimestampC = kTimeStep * 2;
36 constexpr uint64_t kTimestampD = kTimeStep * 3;
37 constexpr uint64_t kTimestampE = kTimeStep * 4;
38 
39 constexpr int64_t kCookieA = 1234;
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 }  // namespace
48 
49 class FrameCookieFixture {
50  protected:
CreateStartEvent(int32_t field_id,uint64_t ts,int32_t pid,int64_t cookie) const51   std::string CreateStartEvent(int32_t field_id,
52                                uint64_t ts,
53                                int32_t pid,
54                                int64_t cookie) const {
55     protos::gen::TracePacket packet;
56     packet.set_timestamp(ts);
57 
58     switch (field_id) {
59       case protos::pbzero::FrameTimelineEvent::
60           kExpectedSurfaceFrameStartFieldNumber:
61         CreateExpectedSurfaceFrameStart(pid, cookie,
62                                         packet.mutable_frame_timeline_event());
63         break;
64 
65       case protos::pbzero::FrameTimelineEvent::
66           kActualSurfaceFrameStartFieldNumber:
67         CreateActualSurfaceFrameStart(pid, cookie,
68                                       packet.mutable_frame_timeline_event());
69         break;
70 
71       case protos::pbzero::FrameTimelineEvent::
72           kExpectedDisplayFrameStartFieldNumber:
73         CreateExpectedDisplayFrameStart(pid, cookie,
74                                         packet.mutable_frame_timeline_event());
75         break;
76 
77       case protos::pbzero::FrameTimelineEvent::
78           kActualDisplayFrameStartFieldNumber:
79         CreateActualDisplayFrameStart(pid, cookie,
80                                       packet.mutable_frame_timeline_event());
81         break;
82 
83       default:
84         PERFETTO_FATAL("Invalid field id");
85         break;
86     }
87 
88     return packet.SerializeAsString();
89   }
90 
CreateFrameEnd(uint64_t ts,int64_t cookie) const91   std::string CreateFrameEnd(uint64_t ts, int64_t cookie) const {
92     protos::gen::TracePacket packet;
93     packet.set_timestamp(ts);
94 
95     auto* start = packet.mutable_frame_timeline_event()->mutable_frame_end();
96     start->set_cookie(cookie);
97 
98     return packet.SerializeAsString();
99   }
100 
CollectEvents(std::initializer_list<ProcessThreadTimeline::Event> events,Context * context) const101   void CollectEvents(std::initializer_list<ProcessThreadTimeline::Event> events,
102                      Context* context) const {
103     CollectTimelineEvents collect;
104     ASSERT_OK(collect.Begin(context));
105 
106     for (const auto& event : events) {
107       context->timeline->Append(event);
108     }
109 
110     ASSERT_OK(collect.End(context));
111   }
112 
CollectCookies(std::initializer_list<std::string> packets,Context * context) const113   void CollectCookies(std::initializer_list<std::string> packets,
114                       Context* context) const {
115     CollectFrameCookies collect;
116     ASSERT_OK(collect.Begin(context));
117 
118     for (const auto& packet : packets) {
119       protos::pbzero::TracePacket::Decoder decoder(packet);
120       ASSERT_OK(collect.Collect(decoder, context));
121     }
122 
123     ASSERT_OK(collect.End(context));
124   }
125 
126  private:
CreateExpectedSurfaceFrameStart(int32_t pid,int64_t cookie,protos::gen::FrameTimelineEvent * event) const127   void CreateExpectedSurfaceFrameStart(
128       int32_t pid,
129       int64_t cookie,
130       protos::gen::FrameTimelineEvent* event) const {
131     auto* start = event->mutable_expected_surface_frame_start();
132     start->set_cookie(cookie);
133     start->set_pid(pid);
134   }
135 
CreateActualSurfaceFrameStart(int32_t pid,int64_t cookie,protos::gen::FrameTimelineEvent * event) const136   void CreateActualSurfaceFrameStart(
137       int32_t pid,
138       int64_t cookie,
139       protos::gen::FrameTimelineEvent* event) const {
140     auto* start = event->mutable_actual_surface_frame_start();
141     start->set_cookie(cookie);
142     start->set_pid(pid);
143   }
144 
CreateExpectedDisplayFrameStart(int32_t pid,int64_t cookie,protos::gen::FrameTimelineEvent * event) const145   void CreateExpectedDisplayFrameStart(
146       int32_t pid,
147       int64_t cookie,
148       protos::gen::FrameTimelineEvent* event) const {
149     auto* start = event->mutable_expected_display_frame_start();
150     start->set_cookie(cookie);
151     start->set_pid(pid);
152   }
153 
CreateActualDisplayFrameStart(int32_t pid,int64_t cookie,protos::gen::FrameTimelineEvent * event) const154   void CreateActualDisplayFrameStart(
155       int32_t pid,
156       int64_t cookie,
157       protos::gen::FrameTimelineEvent* event) const {
158     auto* start = event->mutable_actual_display_frame_start();
159     start->set_cookie(cookie);
160     start->set_pid(pid);
161   }
162 };
163 
164 class CollectFrameCookiesTest : public testing::Test,
165                                 protected FrameCookieFixture,
166                                 public testing::WithParamInterface<int32_t> {
167  protected:
168   Context context_;
169 };
170 
TEST_P(CollectFrameCookiesTest,ExtractsExpectedSurfaceFrameStart)171 TEST_P(CollectFrameCookiesTest, ExtractsExpectedSurfaceFrameStart) {
172   auto field_id = GetParam();
173 
174   auto packet = CreateStartEvent(field_id, kTimestampA, kPidA, kCookieA);
175 
176   CollectCookies({packet}, &context_);
177 
178   ASSERT_EQ(context_.global_frame_cookies.size(), 1u);
179 
180   auto& cookie = context_.global_frame_cookies.back();
181   ASSERT_EQ(cookie.cookie, kCookieA);
182   ASSERT_EQ(cookie.pid, kPidA);
183   ASSERT_EQ(cookie.ts, kTimestampA);
184 }
185 
186 INSTANTIATE_TEST_SUITE_P(
187     EveryStartEventType,
188     CollectFrameCookiesTest,
189     testing::Values(
190         protos::pbzero::FrameTimelineEvent::
191             kExpectedSurfaceFrameStartFieldNumber,
192         protos::pbzero::FrameTimelineEvent::kActualSurfaceFrameStartFieldNumber,
193         protos::pbzero::FrameTimelineEvent::
194             kExpectedDisplayFrameStartFieldNumber,
195         protos::pbzero::FrameTimelineEvent::
196             kActualDisplayFrameStartFieldNumber));
197 
198 // End events have no influence during the collect phase because they don't have
199 // a direct connection to a process. They're indirectly connected to a pid via a
200 // start event (via a common cookie value).
TEST_F(CollectFrameCookiesTest,IgnoresFrameEnd)201 TEST_F(CollectFrameCookiesTest, IgnoresFrameEnd) {
202   CollectCookies({CreateFrameEnd(kTimestampA, kPidA)}, &context_);
203 
204   ASSERT_TRUE(context_.global_frame_cookies.empty());
205 }
206 
207 class ReduceFrameCookiesTest : public testing::Test,
208                                protected FrameCookieFixture,
209                                public testing::WithParamInterface<int32_t> {
210  protected:
SetUp()211   void SetUp() {
212     context_.package_uid = kUidA;
213 
214     // Time A   +- Time B       +- Time C    +- Time D   +- Time E
215     //          |                            |
216     //          +------------ Pid A ---------+
217     //
218     // The pid will be active from time b to time d. Time A will be used for
219     // "before active". Time C will be used for "while active". Time E will be
220     // used for "after active".
221     CollectEvents(
222         {
223             ProcessThreadTimeline::Event::Open(kTimestampB, kPidA, kPidNone,
224                                                kUidA),
225             ProcessThreadTimeline::Event::Close(kTimestampD, kPidA),
226         },
227         &context_);
228   }
229 
230   ReduceFrameCookies reduce_;
231   Context context_;
232 };
233 
TEST_P(ReduceFrameCookiesTest,RejectBeforeActive)234 TEST_P(ReduceFrameCookiesTest, RejectBeforeActive) {
235   auto field_id = GetParam();
236 
237   // kTimestampA is before pid starts.
238   auto packet = CreateStartEvent(field_id, kTimestampA, kPidA, kCookieA);
239 
240   CollectCookies({packet}, &context_);
241 
242   ASSERT_OK(reduce_.Build(&context_));
243   ASSERT_FALSE(context_.package_frame_cookies.count(kCookieA));
244 }
245 
TEST_P(ReduceFrameCookiesTest,AcceptDuringActive)246 TEST_P(ReduceFrameCookiesTest, AcceptDuringActive) {
247   auto field_id = GetParam();
248 
249   // kTimestampC is between pid starts and ends.
250   auto packet = CreateStartEvent(field_id, kTimestampC, kPidA, kCookieA);
251 
252   CollectCookies({packet}, &context_);
253 
254   ASSERT_OK(reduce_.Build(&context_));
255   ASSERT_TRUE(context_.package_frame_cookies.count(kCookieA));
256 }
257 
TEST_P(ReduceFrameCookiesTest,RejectAfterActive)258 TEST_P(ReduceFrameCookiesTest, RejectAfterActive) {
259   auto field_id = GetParam();
260 
261   // kTimestampE is after pid ends.
262   auto packet = CreateStartEvent(field_id, kTimestampE, kPidA, kCookieA);
263 
264   CollectCookies({packet}, &context_);
265 
266   ASSERT_OK(reduce_.Build(&context_));
267   ASSERT_FALSE(context_.package_frame_cookies.count(kCookieA));
268 }
269 
270 INSTANTIATE_TEST_SUITE_P(
271     EveryStartEventType,
272     ReduceFrameCookiesTest,
273     testing::Values(
274         protos::pbzero::FrameTimelineEvent::
275             kExpectedSurfaceFrameStartFieldNumber,
276         protos::pbzero::FrameTimelineEvent::kActualSurfaceFrameStartFieldNumber,
277         protos::pbzero::FrameTimelineEvent::
278             kExpectedDisplayFrameStartFieldNumber,
279         protos::pbzero::FrameTimelineEvent::
280             kActualDisplayFrameStartFieldNumber));
281 
282 class FilterCookiesFieldsTest : public testing::Test,
283                                 protected FrameCookieFixture,
284                                 public testing::WithParamInterface<int32_t> {
285  protected:
ExtractTimelineEvent(const std::string & packet) const286   protozero::Field ExtractTimelineEvent(const std::string& packet) const {
287     protozero::ProtoDecoder packet_decoder(packet);
288 
289     // There must be one in order for the test to work, so we assume it's there.
290     return packet_decoder.FindField(
291         protos::pbzero::TracePacket::kFrameTimelineEventFieldNumber);
292   }
293 
294   FilterFrameEvents filter_;
295   Context context_;
296 };
297 
298 // If the event was within a valid pid's lifespan and was connected to the
299 // package, it should be kept.
TEST_P(FilterCookiesFieldsTest,IncludeIncludedStartCookies)300 TEST_P(FilterCookiesFieldsTest, IncludeIncludedStartCookies) {
301   context_.package_frame_cookies.insert(kCookieA);
302 
303   auto field_id = GetParam();
304   auto packet = CreateStartEvent(field_id, kTimestampA, kPidA, kCookieA);
305   auto timeline_field = ExtractTimelineEvent(packet);
306 
307   ASSERT_TRUE(filter_.KeepField(context_, timeline_field));
308 }
309 
310 // If the event wasn't within a valid pid's lifespans and/or was connected to a
311 // package, it should be removed.
TEST_P(FilterCookiesFieldsTest,ExcludeMissingStartCookies)312 TEST_P(FilterCookiesFieldsTest, ExcludeMissingStartCookies) {
313   auto field_id = GetParam();
314   auto packet = CreateStartEvent(field_id, kTimestampA, kPidA, kCookieA);
315   auto timeline_field = ExtractTimelineEvent(packet);
316 
317   ASSERT_FALSE(filter_.KeepField(context_, timeline_field));
318 }
319 
320 INSTANTIATE_TEST_SUITE_P(
321     EveryStartEventType,
322     FilterCookiesFieldsTest,
323     testing::Values(
324         protos::pbzero::FrameTimelineEvent::
325             kExpectedSurfaceFrameStartFieldNumber,
326         protos::pbzero::FrameTimelineEvent::kActualSurfaceFrameStartFieldNumber,
327         protos::pbzero::FrameTimelineEvent::
328             kExpectedDisplayFrameStartFieldNumber,
329         protos::pbzero::FrameTimelineEvent::
330             kActualDisplayFrameStartFieldNumber));
331 
TEST_F(FilterCookiesFieldsTest,IncludeIncludedEndCookies)332 TEST_F(FilterCookiesFieldsTest, IncludeIncludedEndCookies) {
333   context_.package_frame_cookies.insert(kCookieA);
334 
335   auto packet = CreateFrameEnd(kTimestampA, kCookieA);
336   auto timeline_field = ExtractTimelineEvent(packet);
337 
338   ASSERT_TRUE(filter_.KeepField(context_, timeline_field));
339 }
340 
TEST_F(FilterCookiesFieldsTest,ExcludeMissingEndCookies)341 TEST_F(FilterCookiesFieldsTest, ExcludeMissingEndCookies) {
342   auto packet = CreateFrameEnd(kTimestampA, kCookieA);
343   auto timeline_field = ExtractTimelineEvent(packet);
344 
345   ASSERT_FALSE(filter_.KeepField(context_, timeline_field));
346 }
347 
348 }  // namespace perfetto::trace_redaction
349