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