xref: /aosp_15_r20/external/perfetto/src/trace_redaction/collect_frame_cookies.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/collect_frame_cookies.h"
18 
19 #include "perfetto/base/status.h"
20 #include "perfetto/protozero/field.h"
21 #include "perfetto/protozero/proto_decoder.h"
22 #include "perfetto/protozero/scattered_heap_buffer.h"
23 #include "src/trace_redaction/proto_util.h"
24 #include "src/trace_redaction/trace_redaction_framework.h"
25 
26 #include "protos/perfetto/trace/android/frame_timeline_event.pbzero.h"
27 #include "protos/perfetto/trace/trace_packet.pbzero.h"
28 
29 namespace perfetto::trace_redaction {
30 
31 namespace {
32 
33 using FrameTimelineEvent = protos::pbzero::FrameTimelineEvent;
34 
35 struct Frame {
36   uint32_t id;
37   uint32_t pid;
38   uint32_t cookie;
39 };
40 
41 constexpr Frame kActualDisplayFrameStart = {
42     FrameTimelineEvent::kActualDisplayFrameStartFieldNumber,
43     FrameTimelineEvent::ActualDisplayFrameStart::kPidFieldNumber,
44     FrameTimelineEvent::ActualDisplayFrameStart::kCookieFieldNumber,
45 };
46 
47 constexpr Frame kExpectedDisplayFrameStart = {
48     FrameTimelineEvent::kExpectedDisplayFrameStartFieldNumber,
49     FrameTimelineEvent::ExpectedDisplayFrameStart::kPidFieldNumber,
50     FrameTimelineEvent::ExpectedDisplayFrameStart::kCookieFieldNumber,
51 };
52 
53 constexpr Frame kActualSurfaceFrameStart = {
54     FrameTimelineEvent::kActualSurfaceFrameStartFieldNumber,
55     FrameTimelineEvent::ActualSurfaceFrameStart::kPidFieldNumber,
56     FrameTimelineEvent::ActualSurfaceFrameStart::kCookieFieldNumber,
57 };
58 
59 constexpr Frame kExpectedSurfaceFrameStart = {
60     FrameTimelineEvent::kExpectedSurfaceFrameStartFieldNumber,
61     FrameTimelineEvent::ExpectedSurfaceFrameStart::kPidFieldNumber,
62     FrameTimelineEvent::ExpectedSurfaceFrameStart::kCookieFieldNumber,
63 };
64 
65 // Do not use `pid` from `kFrameEnd`.
66 constexpr Frame kFrameEnd = {
67     FrameTimelineEvent::kFrameEndFieldNumber,
68     0,
69     FrameTimelineEvent::FrameEnd::kCookieFieldNumber,
70 };
71 
72 }  // namespace
73 
Begin(Context * context) const74 base::Status CollectFrameCookies::Begin(Context* context) const {
75   if (context->global_frame_cookies.empty()) {
76     return base::OkStatus();
77   }
78 
79   return base::ErrStatus("FindFrameCookies: frame cookies already populated");
80 }
81 
Collect(const protos::pbzero::TracePacket::Decoder & packet,Context * context) const82 base::Status CollectFrameCookies::Collect(
83     const protos::pbzero::TracePacket::Decoder& packet,
84     Context* context) const {
85   // A frame cookie needs a time and pid for a timeline query. Ignore packets
86   // without a timestamp.
87   if (!packet.has_timestamp() || !packet.has_frame_timeline_event()) {
88     return base::OkStatus();
89   }
90 
91   auto timestamp = packet.timestamp();
92 
93   // Only use the start frames. They are the only ones with a pid. End events
94   // use the cookies to reference the pid in a start event.
95   auto handlers = {
96       kActualDisplayFrameStart,
97       kActualSurfaceFrameStart,
98       kExpectedDisplayFrameStart,
99       kExpectedSurfaceFrameStart,
100   };
101 
102   // Timeline Event Decoder.
103   protozero::ProtoDecoder decoder(packet.frame_timeline_event());
104 
105   // If no handler worked, cookie will not get added to the global cookie field.
106   for (const auto& handler : handlers) {
107     auto outer = decoder.FindField(handler.id);
108 
109     if (!outer.valid()) {
110       continue;
111     }
112 
113     protozero::ProtoDecoder inner(outer.as_bytes());
114 
115     auto pid = inner.FindField(handler.pid);
116     auto cookie = inner.FindField(handler.cookie);
117 
118     // This should be handled, but it is not valid. Drop the event by not adding
119     // it to the global_frame_cookies list.
120     if (!pid.valid() || !cookie.valid()) {
121       continue;
122     }
123 
124     FrameCookie frame_cookie;
125     frame_cookie.pid = pid.as_int32();
126     frame_cookie.cookie = cookie.as_int64();
127     frame_cookie.ts = timestamp;
128 
129     context->global_frame_cookies.push_back(frame_cookie);
130 
131     break;
132   }
133 
134   return base::OkStatus();
135 }
136 
Build(Context * context) const137 base::Status ReduceFrameCookies::Build(Context* context) const {
138   if (!context->package_uid.has_value()) {
139     return base::ErrStatus("ReduceFrameCookies: missing package uid.");
140   }
141 
142   if (!context->timeline) {
143     return base::ErrStatus("ReduceFrameCookies: missing timeline.");
144   }
145 
146   // Even though it is rare, it is possible for there to be no SurfaceFlinger
147   // frame cookies. Even through the main path handles this, we use this early
148   // exit to document this edge case.
149   if (context->global_frame_cookies.empty()) {
150     return base::OkStatus();
151   }
152 
153   // Filter the global cookies down to cookies that belong to the target package
154   // (uid).
155   for (const auto& cookie : context->global_frame_cookies) {
156     if (context->timeline->PidConnectsToUid(cookie.ts, cookie.pid,
157                                             *context->package_uid)) {
158       context->package_frame_cookies.insert(cookie.cookie);
159     }
160   }
161 
162   return base::OkStatus();
163 }
164 
Transform(const Context & context,std::string * packet) const165 base::Status FilterFrameEvents::Transform(const Context& context,
166                                           std::string* packet) const {
167   if (packet == nullptr || packet->empty()) {
168     return base::ErrStatus("FilterFrameEvents: null or empty packet.");
169   }
170 
171   protozero::ProtoDecoder decoder(*packet);
172 
173   if (!decoder
174            .FindField(
175                protos::pbzero::TracePacket::kFrameTimelineEventFieldNumber)
176            .valid()) {
177     return base::OkStatus();
178   }
179 
180   protozero::HeapBuffered<protos::pbzero::TracePacket> message;
181 
182   for (auto field = decoder.ReadField(); field.valid();
183        field = decoder.ReadField()) {
184     if (field.id() ==
185         protos::pbzero::TracePacket::kFrameTimelineEventFieldNumber) {
186       if (KeepField(context, field)) {
187         proto_util::AppendField(field, message.get());
188       }
189 
190     } else {
191       proto_util::AppendField(field, message.get());
192     }
193   }
194 
195   packet->assign(message.SerializeAsString());
196   return base::OkStatus();
197 }
198 
KeepField(const Context & context,const protozero::Field & field) const199 bool FilterFrameEvents::KeepField(const Context& context,
200                                   const protozero::Field& field) const {
201   PERFETTO_DCHECK(field.id() ==
202                   protos::pbzero::TracePacket::kFrameTimelineEventFieldNumber);
203 
204   protozero::ProtoDecoder timeline_event_decoder(field.as_bytes());
205 
206   auto handlers = {
207       kActualDisplayFrameStart,
208       kActualSurfaceFrameStart,
209       kExpectedDisplayFrameStart,
210       kExpectedSurfaceFrameStart,
211       kFrameEnd,
212   };
213 
214   const auto& cookies = context.package_frame_cookies;
215 
216   for (const auto& handler : handlers) {
217     auto event = timeline_event_decoder.FindField(handler.id);
218 
219     if (!event.valid()) {
220       continue;
221     }
222 
223     protozero::ProtoDecoder event_decoder(event.as_bytes());
224 
225     auto cookie = event_decoder.FindField(handler.cookie);
226 
227     if (cookie.valid() && cookies.count(cookie.as_int64())) {
228       return true;
229     }
230   }
231 
232   return false;
233 }
234 
235 }  // namespace perfetto::trace_redaction
236