• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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_processor/importers/proto/frame_timeline_event_parser.h"
18 
19 #include <cstdint>
20 #include <cstdlib>
21 #include <limits>
22 #include <numeric>
23 #include <optional>
24 #include <string>
25 #include <utility>
26 #include <vector>
27 
28 #include "perfetto/ext/base/string_view.h"
29 #include "protos/perfetto/trace/android/frame_timeline_event.pbzero.h"
30 #include "src/trace_processor/importers/common/args_tracker.h"
31 #include "src/trace_processor/importers/common/event_tracker.h"
32 #include "src/trace_processor/importers/common/flow_tracker.h"
33 #include "src/trace_processor/importers/common/process_tracker.h"
34 #include "src/trace_processor/importers/common/slice_tracker.h"
35 #include "src/trace_processor/importers/common/track_compressor.h"
36 #include "src/trace_processor/importers/common/tracks.h"
37 #include "src/trace_processor/importers/common/tracks_common.h"
38 #include "src/trace_processor/storage/stats.h"
39 #include "src/trace_processor/storage/trace_storage.h"
40 #include "src/trace_processor/tables/slice_tables_py.h"
41 #include "src/trace_processor/types/trace_processor_context.h"
42 #include "src/trace_processor/types/variadic.h"
43 
44 namespace perfetto::trace_processor {
45 namespace {
46 
IsBadTimestamp(int64_t ts)47 bool IsBadTimestamp(int64_t ts) {
48   // Very small or very large timestamps are likely a mistake.
49   // See b/185978397
50   constexpr int64_t kBadTimestamp =
51       std::numeric_limits<int64_t>::max() - (10LL * 1000 * 1000 * 1000);
52   return std::abs(ts) >= kBadTimestamp;
53 }
54 
JankTypeBitmaskToStringId(TraceProcessorContext * context,int32_t jank_type)55 StringId JankTypeBitmaskToStringId(TraceProcessorContext* context,
56                                    int32_t jank_type) {
57   if (jank_type == FrameTimelineEvent::JANK_UNSPECIFIED)
58     return context->storage->InternString("Unspecified");
59   if (jank_type == FrameTimelineEvent::JANK_NONE)
60     return context->storage->InternString("None");
61 
62   std::vector<std::string> jank_reasons;
63   if (jank_type & FrameTimelineEvent::JANK_SF_SCHEDULING)
64     jank_reasons.emplace_back("SurfaceFlinger Scheduling");
65   if (jank_type & FrameTimelineEvent::JANK_PREDICTION_ERROR)
66     jank_reasons.emplace_back("Prediction Error");
67   if (jank_type & FrameTimelineEvent::JANK_DISPLAY_HAL)
68     jank_reasons.emplace_back("Display HAL");
69   if (jank_type & FrameTimelineEvent::JANK_SF_CPU_DEADLINE_MISSED)
70     jank_reasons.emplace_back("SurfaceFlinger CPU Deadline Missed");
71   if (jank_type & FrameTimelineEvent::JANK_SF_GPU_DEADLINE_MISSED)
72     jank_reasons.emplace_back("SurfaceFlinger GPU Deadline Missed");
73   if (jank_type & FrameTimelineEvent::JANK_APP_DEADLINE_MISSED)
74     jank_reasons.emplace_back("App Deadline Missed");
75   if (jank_type & FrameTimelineEvent::JANK_BUFFER_STUFFING)
76     jank_reasons.emplace_back("Buffer Stuffing");
77   if (jank_type & FrameTimelineEvent::JANK_UNKNOWN)
78     jank_reasons.emplace_back("Unknown Jank");
79   if (jank_type & FrameTimelineEvent::JANK_SF_STUFFING)
80     jank_reasons.emplace_back("SurfaceFlinger Stuffing");
81   if (jank_type & FrameTimelineEvent::JANK_DROPPED)
82     jank_reasons.emplace_back("Dropped Frame");
83 
84   std::string jank_str(
85       std::accumulate(jank_reasons.begin(), jank_reasons.end(), std::string(),
86                       [](const std::string& l, const std::string& r) {
87                         return l.empty() ? r : l + ", " + r;
88                       }));
89   return context->storage->InternString(base::StringView(jank_str));
90 }
91 
DisplayFrameJanky(int32_t jank_type)92 bool DisplayFrameJanky(int32_t jank_type) {
93   if (jank_type == FrameTimelineEvent::JANK_UNSPECIFIED ||
94       jank_type == FrameTimelineEvent::JANK_NONE)
95     return false;
96 
97   int32_t display_frame_jank_bitmask =
98       FrameTimelineEvent::JANK_SF_SCHEDULING |
99       FrameTimelineEvent::JANK_PREDICTION_ERROR |
100       FrameTimelineEvent::JANK_DISPLAY_HAL |
101       FrameTimelineEvent::JANK_SF_CPU_DEADLINE_MISSED |
102       FrameTimelineEvent::JANK_SF_GPU_DEADLINE_MISSED;
103   return (jank_type & display_frame_jank_bitmask) != 0;
104 }
105 
SurfaceFrameJanky(int32_t jank_type)106 bool SurfaceFrameJanky(int32_t jank_type) {
107   if (jank_type == FrameTimelineEvent::JANK_UNSPECIFIED ||
108       jank_type == FrameTimelineEvent::JANK_NONE)
109     return false;
110 
111   int32_t surface_frame_jank_bitmask =
112       FrameTimelineEvent::JANK_APP_DEADLINE_MISSED |
113       FrameTimelineEvent::JANK_UNKNOWN;
114   return (jank_type & surface_frame_jank_bitmask) != 0;
115 }
116 
ValidatePredictionType(TraceProcessorContext * context,int32_t prediction_type)117 bool ValidatePredictionType(TraceProcessorContext* context,
118                             int32_t prediction_type) {
119   if (prediction_type >= FrameTimelineEvent::PREDICTION_VALID /*1*/ &&
120       prediction_type <= FrameTimelineEvent::PREDICTION_UNKNOWN /*3*/)
121     return true;
122   context->storage->IncrementStats(stats::frame_timeline_event_parser_errors);
123   return false;
124 }
125 
ValidatePresentType(TraceProcessorContext * context,int32_t present_type)126 bool ValidatePresentType(TraceProcessorContext* context, int32_t present_type) {
127   if (present_type >= FrameTimelineEvent::PRESENT_ON_TIME /*1*/ &&
128       present_type <= FrameTimelineEvent::PRESENT_UNKNOWN /*5*/)
129     return true;
130   context->storage->IncrementStats(stats::frame_timeline_event_parser_errors);
131   return false;
132 }
133 
134 using ExpectedDisplayFrameStartDecoder =
135     protos::pbzero::FrameTimelineEvent::ExpectedDisplayFrameStart::Decoder;
136 using ActualDisplayFrameStartDecoder =
137     protos::pbzero::FrameTimelineEvent::ActualDisplayFrameStart::Decoder;
138 
139 using ExpectedSurfaceFrameStartDecoder =
140     protos::pbzero::FrameTimelineEvent::ExpectedSurfaceFrameStart::Decoder;
141 using ActualSurfaceFrameStartDecoder =
142     protos::pbzero::FrameTimelineEvent::ActualSurfaceFrameStart::Decoder;
143 
144 using FrameEndDecoder = protos::pbzero::FrameTimelineEvent::FrameEnd::Decoder;
145 
146 constexpr auto kExpectedBlueprint = TrackCompressor::SliceBlueprint(
147     "android_expected_frame_timeline",
148     tracks::DimensionBlueprints(tracks::kProcessDimensionBlueprint),
149     tracks::StaticNameBlueprint("Expected Timeline"));
150 
151 constexpr auto kActualBlueprint = TrackCompressor::SliceBlueprint(
152     "android_actual_frame_timeline",
153     tracks::DimensionBlueprints(tracks::kProcessDimensionBlueprint),
154     tracks::StaticNameBlueprint("Actual Timeline"));
155 
156 }  // namespace
157 
FrameTimelineEventParser(TraceProcessorContext * context)158 FrameTimelineEventParser::FrameTimelineEventParser(
159     TraceProcessorContext* context)
160     : context_(context),
161       present_type_ids_{
162           {context->storage->InternString(
163                "Unspecified Present") /* PRESENT_UNSPECIFIED */,
164            context->storage->InternString(
165                "On-time Present") /* PRESENT_ON_TIME */,
166            context->storage->InternString("Late Present") /* PRESENT_LATE */,
167            context->storage->InternString("Early Present") /* PRESENT_EARLY */,
168            context->storage->InternString(
169                "Dropped Frame") /* PRESENT_DROPPED */,
170            context->storage->InternString(
171                "Unknown Present") /* PRESENT_UNKNOWN */}},
172       prediction_type_ids_{
173           {context->storage->InternString(
174                "Unspecified Prediction") /* PREDICTION_UNSPECIFIED */,
175            context->storage->InternString(
176                "Valid Prediction") /* PREDICTION_VALID */,
177            context->storage->InternString(
178                "Expired Prediction") /* PREDICTION_EXPIRED */,
179            context->storage->InternString(
180                "Unknown Prediction") /* PREDICTION_UNKNOWN */}},
181       jank_severity_type_ids_{{context->storage->InternString("Unknown"),
182                                context->storage->InternString("None"),
183                                context->storage->InternString("Partial"),
184                                context->storage->InternString("Full")}},
185       surface_frame_token_id_(
186           context->storage->InternString("Surface frame token")),
187       display_frame_token_id_(
188           context->storage->InternString("Display frame token")),
189       present_type_id_(context->storage->InternString("Present type")),
190       on_time_finish_id_(context->storage->InternString("On time finish")),
191       gpu_composition_id_(context->storage->InternString("GPU composition")),
192       jank_type_id_(context->storage->InternString("Jank type")),
193       jank_severity_type_id_(
194           context->storage->InternString("Jank severity type")),
195       layer_name_id_(context->storage->InternString("Layer name")),
196       prediction_type_id_(context->storage->InternString("Prediction type")),
197       jank_tag_id_(context->storage->InternString("Jank tag")),
198       is_buffer_id_(context->storage->InternString("Is Buffer?")),
199       jank_tag_none_id_(context->storage->InternString("No Jank")),
200       jank_tag_self_id_(context->storage->InternString("Self Jank")),
201       jank_tag_other_id_(context->storage->InternString("Other Jank")),
202       jank_tag_dropped_id_(context->storage->InternString("Dropped Frame")),
203       jank_tag_buffer_stuffing_id_(
204           context->storage->InternString("Buffer Stuffing")),
205       jank_tag_sf_stuffing_id_(
206           context->storage->InternString("SurfaceFlinger Stuffing")) {}
207 
ParseExpectedDisplayFrameStart(int64_t timestamp,ConstBytes blob)208 void FrameTimelineEventParser::ParseExpectedDisplayFrameStart(int64_t timestamp,
209                                                               ConstBytes blob) {
210   ExpectedDisplayFrameStartDecoder event(blob);
211 
212   if (!event.has_cookie() || !event.has_token() || !event.has_pid()) {
213     context_->storage->IncrementStats(
214         stats::frame_timeline_event_parser_errors);
215     return;
216   }
217 
218   int64_t cookie = event.cookie();
219   int64_t token = event.token();
220   StringId name_id =
221       context_->storage->InternString(base::StringView(std::to_string(token)));
222   UniquePid upid = context_->process_tracker->GetOrCreateProcess(
223       static_cast<uint32_t>(event.pid()));
224   cookie_map_[cookie] = std::make_pair(upid, TrackType::kExpected);
225 
226   TrackId track_id = context_->track_compressor->InternBegin(
227       kExpectedBlueprint, tracks::Dimensions(upid), cookie);
228   context_->slice_tracker->Begin(
229       timestamp, track_id, kNullStringId, name_id,
230       [this, token](ArgsTracker::BoundInserter* inserter) {
231         inserter->AddArg(display_frame_token_id_, Variadic::Integer(token));
232       });
233 }
234 
ParseActualDisplayFrameStart(int64_t timestamp,ConstBytes blob)235 void FrameTimelineEventParser::ParseActualDisplayFrameStart(int64_t timestamp,
236                                                             ConstBytes blob) {
237   ActualDisplayFrameStartDecoder event(blob);
238 
239   if (!event.has_cookie() || !event.has_token() || !event.has_pid()) {
240     context_->storage->IncrementStats(
241         stats::frame_timeline_event_parser_errors);
242     return;
243   }
244 
245   int64_t cookie = event.cookie();
246   int64_t token = event.token();
247   StringId name_id =
248       context_->storage->InternString(base::StringView(std::to_string(token)));
249   UniquePid upid = context_->process_tracker->GetOrCreateProcess(
250       static_cast<uint32_t>(event.pid()));
251   cookie_map_[cookie] = std::make_pair(upid, TrackType::kActual);
252 
253   TrackId track_id = context_->track_compressor->InternBegin(
254       kActualBlueprint, tracks::Dimensions(upid), cookie);
255 
256   // parse present type
257   StringId present_type = present_type_ids_[0];
258   if (event.has_present_type() &&
259       ValidatePresentType(context_, event.present_type())) {
260     present_type = present_type_ids_[static_cast<size_t>(event.present_type())];
261   }
262 
263   // parse jank type
264   StringId jank_type = JankTypeBitmaskToStringId(context_, event.jank_type());
265 
266   // parse jank severity type
267   StringId jank_severity_type;
268   if (event.has_jank_severity_type()) {
269     jank_severity_type = jank_severity_type_ids_[static_cast<size_t>(
270         event.jank_severity_type())];
271   } else {
272     // NOTE: Older traces don't have this field. If JANK_NONE use
273     // |severity_type| "None", and is not present, use "Unknown".
274     jank_severity_type = (event.jank_type() == FrameTimelineEvent::JANK_NONE)
275                              ? jank_severity_type_ids_[1]  /* None */
276                              : jank_severity_type_ids_[0]; /* Unknown */
277   }
278 
279   // parse prediction type
280   StringId prediction_type = prediction_type_ids_[0];
281   if (event.has_prediction_type() &&
282       ValidatePredictionType(context_, event.prediction_type())) {
283     prediction_type =
284         prediction_type_ids_[static_cast<size_t>(event.prediction_type())];
285   }
286 
287   StringId jank_tag;
288   if (DisplayFrameJanky(event.jank_type())) {
289     jank_tag = jank_tag_self_id_;
290   } else if (event.jank_type() == FrameTimelineEvent::JANK_SF_STUFFING) {
291     jank_tag = jank_tag_sf_stuffing_id_;
292   } else if (event.jank_type() == FrameTimelineEvent::JANK_DROPPED) {
293     jank_tag = jank_tag_dropped_id_;
294   } else {
295     jank_tag = jank_tag_none_id_;
296   }
297 
298   std::optional<SliceId> opt_slice_id = context_->slice_tracker->Begin(
299       timestamp, track_id, kNullStringId, name_id,
300       [&](ArgsTracker::BoundInserter* inserter) {
301         inserter->AddArg(display_frame_token_id_, Variadic::Integer(token));
302         inserter->AddArg(present_type_id_, Variadic::String(present_type));
303         inserter->AddArg(on_time_finish_id_,
304                          Variadic::Integer(event.on_time_finish()));
305         inserter->AddArg(gpu_composition_id_,
306                          Variadic::Integer(event.gpu_composition()));
307         inserter->AddArg(jank_type_id_, Variadic::String(jank_type));
308         inserter->AddArg(jank_severity_type_id_,
309                          Variadic::String(jank_severity_type));
310         inserter->AddArg(prediction_type_id_,
311                          Variadic::String(prediction_type));
312         inserter->AddArg(jank_tag_id_, Variadic::String(jank_tag));
313       });
314 
315   // SurfaceFrames will always be parsed before the matching DisplayFrame
316   // (since the app works on the frame before SurfaceFlinger does). Because
317   // of this it's safe to add all the flow events here and then forget the
318   // surface_slice id - we shouldn't see more surfaces_slices that should be
319   // connected to this slice after this point.
320   auto range = display_token_to_surface_slice_.equal_range(token);
321   if (opt_slice_id) {
322     for (auto it = range.first; it != range.second; ++it) {
323       SliceId surface_slice = it->second;     // App
324       SliceId display_slice = *opt_slice_id;  // SurfaceFlinger
325       context_->flow_tracker->InsertFlow(surface_slice, display_slice);
326     }
327   }
328   display_token_to_surface_slice_.erase(range.first, range.second);
329 }
330 
ParseExpectedSurfaceFrameStart(int64_t timestamp,ConstBytes blob)331 void FrameTimelineEventParser::ParseExpectedSurfaceFrameStart(int64_t timestamp,
332                                                               ConstBytes blob) {
333   ExpectedSurfaceFrameStartDecoder event(blob);
334 
335   if (!event.has_cookie() || !event.has_token() ||
336       !event.has_display_frame_token() || !event.has_pid()) {
337     context_->storage->IncrementStats(
338         stats::frame_timeline_event_parser_errors);
339     return;
340   }
341 
342   int64_t cookie = event.cookie();
343   int64_t token = event.token();
344   int64_t display_frame_token = event.display_frame_token();
345   UniquePid upid = context_->process_tracker->GetOrCreateProcess(
346       static_cast<uint32_t>(event.pid()));
347   cookie_map_[cookie] = std::make_pair(upid, TrackType::kExpected);
348 
349   auto token_set_it = expected_timeline_token_map_.find(upid);
350   if (token_set_it != expected_timeline_token_map_.end()) {
351     auto& token_set = token_set_it->second;
352     if (token_set.find(token) != token_set.end()) {
353       // If we already have an expected timeline for a token, the expectations
354       // are same for all frames that use the token. No need to add duplicate
355       // entries.
356       return;
357     }
358   }
359   // This is the first time we are seeing this token for this process. Add to
360   // the map.
361   expected_timeline_token_map_[upid].insert(token);
362 
363   StringId layer_name_id = event.has_layer_name()
364                                ? context_->storage->InternString(
365                                      base::StringView(event.layer_name()))
366                                : kNullStringId;
367   StringId name_id =
368       context_->storage->InternString(base::StringView(std::to_string(token)));
369 
370   TrackId track_id = context_->track_compressor->InternBegin(
371       kExpectedBlueprint, tracks::Dimensions(upid), cookie);
372   context_->slice_tracker->Begin(
373       timestamp, track_id, kNullStringId, name_id,
374       [&](ArgsTracker::BoundInserter* inserter) {
375         inserter->AddArg(surface_frame_token_id_, Variadic::Integer(token));
376         inserter->AddArg(display_frame_token_id_,
377                          Variadic::Integer(display_frame_token));
378         inserter->AddArg(layer_name_id_, Variadic::String(layer_name_id));
379       });
380 }
381 
ParseActualSurfaceFrameStart(int64_t timestamp,ConstBytes blob)382 void FrameTimelineEventParser::ParseActualSurfaceFrameStart(int64_t timestamp,
383                                                             ConstBytes blob) {
384   ActualSurfaceFrameStartDecoder event(blob);
385 
386   if (!event.has_cookie() || !event.has_token() ||
387       !event.has_display_frame_token() || !event.has_pid()) {
388     context_->storage->IncrementStats(
389         stats::frame_timeline_event_parser_errors);
390     return;
391   }
392 
393   int64_t cookie = event.cookie();
394   int64_t token = event.token();
395   int64_t display_frame_token = event.display_frame_token();
396   UniquePid upid = context_->process_tracker->GetOrCreateProcess(
397       static_cast<uint32_t>(event.pid()));
398   cookie_map_[cookie] = std::make_pair(upid, TrackType::kActual);
399 
400   StringId layer_name_id;
401   if (event.has_layer_name())
402     layer_name_id =
403         context_->storage->InternString(base::StringView(event.layer_name()));
404   StringId name_id =
405       context_->storage->InternString(base::StringView(std::to_string(token)));
406 
407   TrackId track_id = context_->track_compressor->InternBegin(
408       kActualBlueprint, tracks::Dimensions(upid), cookie);
409 
410   // parse present type
411   StringId present_type = present_type_ids_[0];
412   bool present_type_validated = false;
413   if (event.has_present_type() &&
414       ValidatePresentType(context_, event.present_type())) {
415     present_type_validated = true;
416     present_type = present_type_ids_[static_cast<size_t>(event.present_type())];
417   }
418 
419   // parse jank type
420   StringId jank_type = JankTypeBitmaskToStringId(context_, event.jank_type());
421 
422   // parse jank severity type
423   StringId jank_severity_type;
424   if (event.has_jank_severity_type()) {
425     jank_severity_type = jank_severity_type_ids_[static_cast<size_t>(
426         event.jank_severity_type())];
427   } else {
428     // NOTE: Older traces don't have this field. If JANK_NONE use
429     // |severity_type| "None", and is not present, use "Unknown".
430     jank_severity_type = (event.jank_type() == FrameTimelineEvent::JANK_NONE)
431                              ? jank_severity_type_ids_[1]  /* None */
432                              : jank_severity_type_ids_[0]; /* Unknown */
433   }
434 
435   // parse prediction type
436   StringId prediction_type = prediction_type_ids_[0];
437   if (event.has_prediction_type() &&
438       ValidatePredictionType(context_, event.prediction_type())) {
439     prediction_type =
440         prediction_type_ids_[static_cast<size_t>(event.prediction_type())];
441   }
442 
443   StringId jank_tag;
444   if (SurfaceFrameJanky(event.jank_type())) {
445     jank_tag = jank_tag_self_id_;
446   } else if (DisplayFrameJanky(event.jank_type())) {
447     jank_tag = jank_tag_other_id_;
448   } else if (event.jank_type() == FrameTimelineEvent::JANK_BUFFER_STUFFING) {
449     jank_tag = jank_tag_buffer_stuffing_id_;
450   } else if (present_type_validated &&
451              event.present_type() == FrameTimelineEvent::PRESENT_DROPPED) {
452     jank_tag = jank_tag_dropped_id_;
453   } else {
454     jank_tag = jank_tag_none_id_;
455   }
456   StringId is_buffer = context_->storage->InternString("Unspecified");
457   if (event.has_is_buffer()) {
458     if (event.is_buffer()) {
459       is_buffer = context_->storage->InternString("Yes");
460     } else {
461       is_buffer = context_->storage->InternString("No");
462     }
463   }
464 
465   std::optional<SliceId> opt_slice_id = context_->slice_tracker->Begin(
466       timestamp, track_id, kNullStringId, name_id,
467       [&](ArgsTracker::BoundInserter* inserter) {
468         inserter->AddArg(surface_frame_token_id_, Variadic::Integer(token));
469         inserter->AddArg(display_frame_token_id_,
470                          Variadic::Integer(display_frame_token));
471         inserter->AddArg(layer_name_id_, Variadic::String(layer_name_id));
472         inserter->AddArg(present_type_id_, Variadic::String(present_type));
473         inserter->AddArg(on_time_finish_id_,
474                          Variadic::Integer(event.on_time_finish()));
475         inserter->AddArg(gpu_composition_id_,
476                          Variadic::Integer(event.gpu_composition()));
477         inserter->AddArg(jank_type_id_, Variadic::String(jank_type));
478         inserter->AddArg(jank_severity_type_id_,
479                          Variadic::String(jank_severity_type));
480         inserter->AddArg(prediction_type_id_,
481                          Variadic::String(prediction_type));
482         inserter->AddArg(jank_tag_id_, Variadic::String(jank_tag));
483         inserter->AddArg(is_buffer_id_, Variadic::String(is_buffer));
484       });
485 
486   if (opt_slice_id) {
487     display_token_to_surface_slice_.emplace(display_frame_token, *opt_slice_id);
488   }
489 }
490 
ParseFrameEnd(int64_t timestamp,ConstBytes blob)491 void FrameTimelineEventParser::ParseFrameEnd(int64_t timestamp,
492                                              ConstBytes blob) {
493   FrameEndDecoder event(blob);
494   if (!event.has_cookie()) {
495     context_->storage->IncrementStats(
496         stats::frame_timeline_event_parser_errors);
497     return;
498   }
499 
500   int64_t cookie = event.cookie();
501   auto* it = cookie_map_.Find(cookie);
502   if (!it) {
503     context_->storage->IncrementStats(stats::frame_timeline_unpaired_end_event);
504     return;
505   }
506   TrackId track_id;
507   switch (it->second) {
508     case TrackType::kExpected:
509       track_id = context_->track_compressor->InternEnd(
510           kExpectedBlueprint, tracks::Dimensions(it->first), cookie);
511       break;
512     case TrackType::kActual:
513       track_id = context_->track_compressor->InternEnd(
514           kActualBlueprint, tracks::Dimensions(it->first), cookie);
515       break;
516   }
517   context_->slice_tracker->End(timestamp, track_id);
518   cookie_map_.Erase(cookie);
519 }
520 
ParseFrameTimelineEvent(int64_t timestamp,ConstBytes blob)521 void FrameTimelineEventParser::ParseFrameTimelineEvent(int64_t timestamp,
522                                                        ConstBytes blob) {
523   protos::pbzero::FrameTimelineEvent_Decoder frame_event(blob);
524 
525   // Due to platform bugs, negative timestamps can creep into into traces.
526   // Ensure that it doesn't make it into the tables.
527   // TODO(mayzner): remove the negative check once we have some logic handling
528   // this at the sorter level.
529   if (timestamp < 0 || IsBadTimestamp(timestamp)) {
530     context_->storage->IncrementStats(
531         stats::frame_timeline_event_parser_errors);
532     return;
533   }
534 
535   if (frame_event.has_expected_display_frame_start()) {
536     ParseExpectedDisplayFrameStart(timestamp,
537                                    frame_event.expected_display_frame_start());
538   } else if (frame_event.has_actual_display_frame_start()) {
539     ParseActualDisplayFrameStart(timestamp,
540                                  frame_event.actual_display_frame_start());
541   } else if (frame_event.has_expected_surface_frame_start()) {
542     ParseExpectedSurfaceFrameStart(timestamp,
543                                    frame_event.expected_surface_frame_start());
544   } else if (frame_event.has_actual_surface_frame_start()) {
545     ParseActualSurfaceFrameStart(timestamp,
546                                  frame_event.actual_surface_frame_start());
547   } else if (frame_event.has_frame_end()) {
548     ParseFrameEnd(timestamp, frame_event.frame_end());
549   } else {
550     context_->storage->IncrementStats(
551         stats::frame_timeline_event_parser_errors);
552   }
553 }
554 }  // namespace perfetto::trace_processor
555