Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(243)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChart.js

Issue 2785533002: DevTools: Show event initiator on Flame Chart for selected entries (Closed)
Patch Set: sort experiments Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2014 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 /**
32 * @implements {PerfUI.FlameChartDataProvider}
33 * @unrestricted
34 */
35 Timeline.TimelineFlameChartDataProvider = class {
36 /**
37 * @param {!Array<!TimelineModel.TimelineModelFilter>} filters
38 */
39 constructor(filters) {
40 this.reset();
41 this._font = '11px ' + Host.fontFamily();
42 this._filters = filters;
43 /** @type {?PerfUI.FlameChart.TimelineData} */
44 this._timelineData = null;
45 this._currentLevel = 0;
46 /** @type {?Timeline.PerformanceModel} */
47 this._performanceModel = null;
48 /** @type {?TimelineModel.TimelineModel} */
49 this._model = null;
50
51 this._consoleColorGenerator =
52 new PerfUI.FlameChart.ColorGenerator({min: 30, max: 55}, {min: 70, max: 100, count: 6}, 50, 0.7);
53 this._extensionColorGenerator =
54 new PerfUI.FlameChart.ColorGenerator({min: 210, max: 300}, {min: 70, max : 100, count: 6}, 70, 0.7);
55
56 var defaultGroupStyle = {
57 padding: 4,
58 height: 17,
59 collapsible: true,
60 color: UI.themeSupport.patchColor('#222', UI.ThemeSupport.ColorUsage.Foreg round),
61 backgroundColor: UI.themeSupport.patchColor('white', UI.ThemeSupport.Color Usage.Background),
62 font: this._font,
63 nestingLevel: 0,
64 shareHeaderLine: true
65 };
66
67 this._headerLevel1 = /** @type {!PerfUI.FlameChart.GroupStyle} */
68 (Object.assign({}, defaultGroupStyle, {shareHeaderLine: false}));
69 this._headerLevel2 = /** @type {!PerfUI.FlameChart.GroupStyle} */
70 (Object.assign({}, defaultGroupStyle, {padding: 2, nestingLevel: 1, coll apsible: false}));
71 this._staticHeader = /** @type {!PerfUI.FlameChart.GroupStyle} */
72 (Object.assign({}, defaultGroupStyle, {collapsible: false}));
73 this._interactionsHeaderLevel1 = /** @type {!PerfUI.FlameChart.GroupStyle} * /
74 (Object.assign({useFirstLineForOverview: true}, defaultGroupStyle));
75 this._interactionsHeaderLevel2 = /** @type {!PerfUI.FlameChart.GroupStyle} * /
76 (Object.assign({}, defaultGroupStyle, {padding: 2, nestingLevel: 1}));
77
78 /** @type {!Map<string, number>} */
79 this._flowEventIndexById = new Map();
80 }
81
82 /**
83 * @param {?Timeline.PerformanceModel} performanceModel
84 */
85 setModel(performanceModel) {
86 this.reset();
87 this._performanceModel = performanceModel;
88 this._model = performanceModel && performanceModel.timelineModel();
89 }
90
91 /**
92 * @override
93 * @param {number} entryIndex
94 * @return {?string}
95 */
96 entryTitle(entryIndex) {
97 var entryType = this._entryType(entryIndex);
98 if (entryType === Timeline.TimelineFlameChartEntryType.Event) {
99 var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryI ndex]);
100 if (event.phase === SDK.TracingModel.Phase.AsyncStepInto || event.phase == = SDK.TracingModel.Phase.AsyncStepPast)
101 return event.name + ':' + event.args['step'];
102 if (event._blackboxRoot)
103 return Common.UIString('Blackboxed');
104 var name = Timeline.TimelineUIUtils.eventStyle(event).title;
105 // TODO(yurys): support event dividers
106 var detailsText = Timeline.TimelineUIUtils.buildDetailsTextForTraceEvent(e vent, this._model.targetByEvent(event));
107 if (event.name === TimelineModel.TimelineModel.RecordType.JSFrame && detai lsText)
108 return detailsText;
109 return detailsText ? Common.UIString('%s (%s)', name, detailsText) : name;
110 }
111 if (entryType === Timeline.TimelineFlameChartEntryType.ExtensionEvent) {
112 var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryI ndex]);
113 return event.name;
114 }
115 var title = this._entryIndexToTitle[entryIndex];
116 if (!title) {
117 title = Common.UIString('Unexpected entryIndex %d', entryIndex);
118 console.error(title);
119 }
120 return title;
121 }
122
123 /**
124 * @override
125 * @param {number} index
126 * @return {string}
127 */
128 textColor(index) {
129 var event = this._entryData[index];
130 return event && event._blackboxRoot ? '#888' : Timeline.FlameChartStyle.text Color;
131 }
132
133 /**
134 * @override
135 * @param {number} index
136 * @return {?string}
137 */
138 entryFont(index) {
139 return this._font;
140 }
141
142 reset() {
143 this._currentLevel = 0;
144 this._timelineData = null;
145 /** @type {!Array<!SDK.TracingModel.Event|!TimelineModel.TimelineFrame|!Time lineModel.TimelineIRModel.Phases>} */
146 this._entryData = [];
147 /** @type {!Array<!Timeline.TimelineFlameChartEntryType>} */
148 this._entryTypeByLevel = [];
149 /** @type {!Array<string>} */
150 this._entryIndexToTitle = [];
151 /** @type {!Array<!Timeline.TimelineFlameChartMarker>} */
152 this._markers = [];
153 /** @type {!Map<!Timeline.TimelineCategory, string>} */
154 this._asyncColorByCategory = new Map();
155 /** @type {!Map<!TimelineModel.TimelineIRModel.Phases, string>} */
156 this._asyncColorByInteractionPhase = new Map();
157 /** @type {!Array<!{title: string, model: !SDK.TracingModel}>} */
158 this._extensionInfo = [];
159 }
160
161 /**
162 * @override
163 * @return {number}
164 */
165 maxStackDepth() {
166 return this._currentLevel;
167 }
168
169 /**
170 * @override
171 * @return {!PerfUI.FlameChart.TimelineData}
172 */
173 timelineData() {
174 if (this._timelineData)
175 return this._timelineData;
176
177 this._timelineData = new PerfUI.FlameChart.TimelineData([], [], [], []);
178 if (!this._model)
179 return this._timelineData;
180
181 this._flowEventIndexById.clear();
182
183 this._minimumBoundary = this._model.minimumRecordTime();
184 this._timeSpan = this._model.isEmpty() ? 1000 : this._model.maximumRecordTim e() - this._minimumBoundary;
185 this._currentLevel = 0;
186
187 this._appendHeader(Common.UIString('Frames'), this._staticHeader);
188 this._appendFrameBars(this._performanceModel.frames());
189
190 this._appendHeader(Common.UIString('Interactions'), this._interactionsHeader Level1);
191 this._appendInteractionRecords();
192
193 var eventEntryType = Timeline.TimelineFlameChartEntryType.Event;
194
195 var asyncEventGroups = TimelineModel.TimelineModel.AsyncEventGroup;
196 var inputLatencies = this._model.mainThreadAsyncEvents().get(asyncEventGroup s.input);
197 if (inputLatencies && inputLatencies.length) {
198 var title = Timeline.TimelineUIUtils.titleForAsyncEventGroup(asyncEventGro ups.input);
199 this._appendAsyncEventsGroup(title, inputLatencies, this._interactionsHead erLevel2, eventEntryType);
200 }
201 var animations = this._model.mainThreadAsyncEvents().get(asyncEventGroups.an imation);
202 if (animations && animations.length) {
203 var title = Timeline.TimelineUIUtils.titleForAsyncEventGroup(asyncEventGro ups.animation);
204 this._appendAsyncEventsGroup(title, animations, this._interactionsHeaderLe vel2, eventEntryType);
205 }
206 var threads = this._model.virtualThreads();
207 if (!Runtime.experiments.isEnabled('timelinePerFrameTrack')) {
208 this._appendThreadTimelineData(
209 Common.UIString('Main'), this._model.mainThreadEvents(), this._model.m ainThreadAsyncEvents(), true);
210 } else {
211 this._appendThreadTimelineData(
212 Common.UIString('Page'), this._model.eventsForFrame(TimelineModel.Time lineModel.PageFrame.mainFrameId),
213 this._model.mainThreadAsyncEvents(), true);
214 for (var frame of this._model.rootFrames()) {
215 // Ignore top frame itself, since it should be part of page events.
216 frame.children.forEach(this._appendFrameEvents.bind(this, 0));
217 }
218 }
219 var compositorThreads = threads.filter(thread => thread.name.startsWith('Com positorTileWorker'));
220 var otherThreads = threads.filter(thread => !thread.name.startsWith('Composi torTileWorker'));
221 if (compositorThreads.length) {
222 this._appendHeader(Common.UIString('Raster'), this._headerLevel1);
223 for (var i = 0; i < compositorThreads.length; ++i) {
224 this._appendSyncEvents(
225 compositorThreads[i].events, Common.UIString('Rasterizer Thread %d', i), this._headerLevel2,
226 eventEntryType);
227 }
228 }
229 this._appendGPUEvents();
230
231 otherThreads.forEach(
232 thread => this._appendThreadTimelineData(
233 thread.name || Common.UIString('Thread %d', thread.id), thread.event s, thread.asyncEventsByGroup));
234
235 for (let extensionIndex = 0; extensionIndex < this._extensionInfo.length; ex tensionIndex++)
236 this._innerAppendExtensionEvents(extensionIndex);
237
238 /**
239 * @param {!Timeline.TimelineFlameChartMarker} a
240 * @param {!Timeline.TimelineFlameChartMarker} b
241 */
242 function compareStartTime(a, b) {
243 return a.startTime() - b.startTime();
244 }
245
246 this._markers.sort(compareStartTime);
247 this._timelineData.markers = this._markers;
248 this._flowEventIndexById.clear();
249
250 return this._timelineData;
251 }
252
253 /**
254 * @override
255 * @return {number}
256 */
257 minimumBoundary() {
258 return this._minimumBoundary;
259 }
260
261 /**
262 * @override
263 * @return {number}
264 */
265 totalTime() {
266 return this._timeSpan;
267 }
268
269 /**
270 * @param {number} level
271 * @param {!TimelineModel.TimelineModel.PageFrame} frame
272 */
273 _appendFrameEvents(level, frame) {
274 var events = this._model.eventsForFrame(frame.id);
275 var clonedHeader = Object.assign({}, this._headerLevel1);
276 clonedHeader.nestingLevel = level;
277 this._appendSyncEvents(
278 events, Timeline.TimelineUIUtils.displayNameForFrame(frame),
279 /** @type {!PerfUI.FlameChart.GroupStyle} */ (clonedHeader), Timeline.Ti melineFlameChartEntryType.Event);
280 frame.children.forEach(this._appendFrameEvents.bind(this, level + 1));
281 }
282
283 /**
284 * @param {string} threadTitle
285 * @param {!Array<!SDK.TracingModel.Event>} syncEvents
286 * @param {!Map<!TimelineModel.TimelineModel.AsyncEventGroup, !Array<!SDK.Trac ingModel.AsyncEvent>>} asyncEvents
287 * @param {boolean=} forceExpanded
288 */
289 _appendThreadTimelineData(threadTitle, syncEvents, asyncEvents, forceExpanded) {
290 var entryType = Timeline.TimelineFlameChartEntryType.Event;
291 this._appendAsyncEvents(asyncEvents);
292 this._appendSyncEvents(syncEvents, threadTitle, this._headerLevel1, entryTyp e, forceExpanded);
293 }
294
295 /**
296 * @param {!Array<!SDK.TracingModel.Event>} events
297 * @param {string} title
298 * @param {!PerfUI.FlameChart.GroupStyle} style
299 * @param {!Timeline.TimelineFlameChartEntryType} entryType
300 * @param {boolean=} forceExpanded
301 */
302 _appendSyncEvents(events, title, style, entryType, forceExpanded) {
303 var isExtension = entryType === Timeline.TimelineFlameChartEntryType.Extensi onEvent;
304 var openEvents = [];
305 var flowEventsEnabled = Runtime.experiments.isEnabled('timelineFlowEvents');
306 var blackboxingEnabled = !isExtension && Runtime.experiments.isEnabled('blac kboxJSFramesOnTimeline');
307 var maxStackDepth = 0;
308 for (var i = 0; i < events.length; ++i) {
309 var e = events[i];
310 if (!isExtension && TimelineModel.TimelineModel.isMarkerEvent(e)) {
311 this._markers.push(new Timeline.TimelineFlameChartMarker(
312 e.startTime, e.startTime - this._model.minimumRecordTime(),
313 Timeline.TimelineUIUtils.markerStyleForEvent(e)));
314 }
315 if (!SDK.TracingModel.isFlowPhase(e.phase)) {
316 if (!e.endTime && e.phase !== SDK.TracingModel.Phase.Instant)
317 continue;
318 if (SDK.TracingModel.isAsyncPhase(e.phase))
319 continue;
320 if (!isExtension && !this._isVisible(e))
321 continue;
322 }
323 while (openEvents.length && openEvents.peekLast().endTime <= e.startTime)
324 openEvents.pop();
325 e._blackboxRoot = false;
326 if (blackboxingEnabled && this._isBlackboxedEvent(e)) {
327 var parent = openEvents.peekLast();
328 if (parent && parent._blackboxRoot)
329 continue;
330 e._blackboxRoot = true;
331 }
332 if (title) {
333 this._appendHeader(title, style, forceExpanded);
334 title = '';
335 }
336
337 var level = this._currentLevel + openEvents.length;
338 if (flowEventsEnabled)
339 this._appendFlowEvent(e, level);
340 if (e.phase !== SDK.TracingModel.Phase.FlowEnd)
341 this._appendEvent(e, level);
342 if (!isExtension && TimelineModel.TimelineModel.isMarkerEvent(e))
343 this._timelineData.entryTotalTimes[this._entryData.length] = undefined;
344
345 maxStackDepth = Math.max(maxStackDepth, openEvents.length + 1);
346 if (e.endTime)
347 openEvents.push(e);
348 }
349 this._entryTypeByLevel.length = this._currentLevel + maxStackDepth;
350 this._entryTypeByLevel.fill(entryType, this._currentLevel);
351 this._currentLevel += maxStackDepth;
352 }
353
354 /**
355 * @param {!SDK.TracingModel.Event} event
356 * @return {boolean}
357 */
358 _isBlackboxedEvent(event) {
359 if (event.name !== TimelineModel.TimelineModel.RecordType.JSFrame)
360 return false;
361 var url = event.args['data']['url'];
362 return url && this._isBlackboxedURL(url);
363 }
364
365 /**
366 * @param {string} url
367 * @return {boolean}
368 */
369 _isBlackboxedURL(url) {
370 return Bindings.blackboxManager.isBlackboxedURL(url);
371 }
372
373 /**
374 * @param {!Map<!TimelineModel.TimelineModel.AsyncEventGroup, !Array<!SDK.Trac ingModel.AsyncEvent>>} asyncEvents
375 */
376 _appendAsyncEvents(asyncEvents) {
377 var entryType = Timeline.TimelineFlameChartEntryType.Event;
378 var groups = TimelineModel.TimelineModel.AsyncEventGroup;
379 var groupArray = Object.keys(groups).map(key => groups[key]);
380
381 groupArray.remove(groups.animation);
382 groupArray.remove(groups.input);
383
384 for (var groupIndex = 0; groupIndex < groupArray.length; ++groupIndex) {
385 var group = groupArray[groupIndex];
386 var events = asyncEvents.get(group);
387 if (!events)
388 continue;
389 var title = Timeline.TimelineUIUtils.titleForAsyncEventGroup(group);
390 this._appendAsyncEventsGroup(title, events, this._headerLevel1, entryType) ;
391 }
392 }
393
394 /**
395 * @param {string} header
396 * @param {!Array<!SDK.TracingModel.AsyncEvent>} events
397 * @param {!PerfUI.FlameChart.GroupStyle} style
398 * @param {!Timeline.TimelineFlameChartEntryType} entryType
399 */
400 _appendAsyncEventsGroup(header, events, style, entryType) {
401 var lastUsedTimeByLevel = [];
402 var groupHeaderAppended = false;
403 for (var i = 0; i < events.length; ++i) {
404 var asyncEvent = events[i];
405 if (!this._isVisible(asyncEvent))
406 continue;
407 if (!groupHeaderAppended) {
408 this._appendHeader(header, style);
409 groupHeaderAppended = true;
410 }
411 var startTime = asyncEvent.startTime;
412 var level;
413 for (level = 0; level < lastUsedTimeByLevel.length && lastUsedTimeByLevel[ level] > startTime; ++level) {
414 }
415 this._appendAsyncEvent(asyncEvent, this._currentLevel + level);
416 lastUsedTimeByLevel[level] = asyncEvent.endTime;
417 }
418 this._entryTypeByLevel.length = this._currentLevel + lastUsedTimeByLevel.len gth;
419 this._entryTypeByLevel.fill(entryType, this._currentLevel);
420 this._currentLevel += lastUsedTimeByLevel.length;
421 }
422
423 _appendGPUEvents() {
424 var eventType = Timeline.TimelineFlameChartEntryType.Event;
425 var gpuEvents = this._model.gpuEvents();
426 if (this._appendSyncEvents(gpuEvents, Common.UIString('GPU'), this._headerLe vel1, eventType, false))
427 ++this._currentLevel;
428 }
429
430 _appendInteractionRecords() {
431 this._performanceModel.interactionRecords().forEach(this._appendSegment, thi s);
432 this._entryTypeByLevel[this._currentLevel++] = Timeline.TimelineFlameChartEn tryType.InteractionRecord;
433 }
434
435 /**
436 * @param {!Array.<!TimelineModel.TimelineFrame>} frames
437 */
438 _appendFrameBars(frames) {
439 var style = Timeline.TimelineUIUtils.markerStyleForFrame();
440 this._entryTypeByLevel[this._currentLevel] = Timeline.TimelineFlameChartEntr yType.Frame;
441 for (var i = 0; i < frames.length; ++i) {
442 this._markers.push(new Timeline.TimelineFlameChartMarker(
443 frames[i].startTime, frames[i].startTime - this._model.minimumRecordTi me(), style));
444 this._appendFrame(frames[i]);
445 }
446 ++this._currentLevel;
447 }
448
449 /**
450 * @param {number} entryIndex
451 * @return {!Timeline.TimelineFlameChartEntryType}
452 */
453 _entryType(entryIndex) {
454 return this._entryTypeByLevel[this._timelineData.entryLevels[entryIndex]];
455 }
456
457 /**
458 * @override
459 * @param {number} entryIndex
460 * @return {?Element}
461 */
462 prepareHighlightedEntryInfo(entryIndex) {
463 var time = '';
464 var title;
465 var warning;
466 var type = this._entryType(entryIndex);
467 if (type === Timeline.TimelineFlameChartEntryType.Event) {
468 var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryI ndex]);
469 var totalTime = event.duration;
470 var selfTime = event.selfTime;
471 var /** @const */ eps = 1e-6;
472 if (typeof totalTime === 'number') {
473 time = Math.abs(totalTime - selfTime) > eps && selfTime > eps ?
474 Common.UIString(
475 '%s (self %s)', Number.millisToString(totalTime, true), Number.m illisToString(selfTime, true)) :
476 Number.millisToString(totalTime, true);
477 }
478 title = this.entryTitle(entryIndex);
479 warning = Timeline.TimelineUIUtils.eventWarning(event);
480 } else if (type === Timeline.TimelineFlameChartEntryType.Frame) {
481 var frame = /** @type {!TimelineModel.TimelineFrame} */ (this._entryData[e ntryIndex]);
482 time = Common.UIString(
483 '%s ~ %.0f\u2009fps', Number.preciseMillisToString(frame.duration, 1), (1000 / frame.duration));
484 title = frame.idle ? Common.UIString('Idle Frame') : Common.UIString('Fram e');
485 if (frame.hasWarnings()) {
486 warning = createElement('span');
487 warning.textContent = Common.UIString('Long frame');
488 }
489 } else {
490 return null;
491 }
492 var element = createElement('div');
493 var root = UI.createShadowRootWithCoreStyles(element, 'timeline/timelineFlam echartPopover.css');
494 var contents = root.createChild('div', 'timeline-flamechart-popover');
495 contents.createChild('span', 'timeline-info-time').textContent = time;
496 contents.createChild('span', 'timeline-info-title').textContent = title;
497 if (warning) {
498 warning.classList.add('timeline-info-warning');
499 contents.appendChild(warning);
500 }
501 return element;
502 }
503
504 /**
505 * @override
506 * @param {number} entryIndex
507 */
508 highlightEntry(entryIndex) {
509 SDK.DOMModel.hideDOMNodeHighlight();
510 if (this._entryType(entryIndex) !== Timeline.TimelineFlameChartEntryType.Eve nt)
511 return;
512 var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryInd ex]);
513 var target = this._model.targetByEvent(event);
514 if (!target)
515 return;
516 var timelineData = TimelineModel.TimelineData.forEvent(event);
517 var backendNodeId = timelineData.backendNodeId;
518 if (!backendNodeId)
519 return;
520 new SDK.DeferredDOMNode(target, backendNodeId).highlight();
521 }
522
523 /**
524 * @override
525 * @param {number} entryIndex
526 * @return {string}
527 */
528 entryColor(entryIndex) {
529 // This is not annotated due to closure compiler failure to properly infer c ache container's template type.
530 function patchColorAndCache(cache, key, lookupColor) {
531 var color = cache.get(key);
532 if (color)
533 return color;
534 var parsedColor = Common.Color.parse(lookupColor(key));
535 color = parsedColor.setAlpha(0.7).asString(Common.Color.Format.RGBA) || '' ;
536 cache.set(key, color);
537 return color;
538 }
539
540 var type = this._entryType(entryIndex);
541 if (type === Timeline.TimelineFlameChartEntryType.Event) {
542 var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryI ndex]);
543 if (!SDK.TracingModel.isAsyncPhase(event.phase))
544 return Timeline.TimelineUIUtils.eventColor(event);
545 if (event.hasCategory(TimelineModel.TimelineModel.Category.Console) ||
546 event.hasCategory(TimelineModel.TimelineModel.Category.UserTiming))
547 return this._consoleColorGenerator.colorForID(event.name);
548 if (event.hasCategory(TimelineModel.TimelineModel.Category.LatencyInfo)) {
549 var phase =
550 TimelineModel.TimelineIRModel.phaseForEvent(event) || TimelineModel. TimelineIRModel.Phases.Uncategorized;
551 return patchColorAndCache(
552 this._asyncColorByInteractionPhase, phase, Timeline.TimelineUIUtils. interactionPhaseColor);
553 }
554 var category = Timeline.TimelineUIUtils.eventStyle(event).category;
555 return patchColorAndCache(this._asyncColorByCategory, category, () => cate gory.color);
556 }
557 if (type === Timeline.TimelineFlameChartEntryType.Frame)
558 return 'white';
559 if (type === Timeline.TimelineFlameChartEntryType.InteractionRecord)
560 return 'transparent';
561 if (type === Timeline.TimelineFlameChartEntryType.ExtensionEvent) {
562 var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryI ndex]);
563 return this._extensionColorGenerator.colorForID(event.name);
564 }
565 return '';
566 }
567
568 /**
569 * @override
570 * @param {number} entryIndex
571 * @param {!CanvasRenderingContext2D} context
572 * @param {?string} text
573 * @param {number} barX
574 * @param {number} barY
575 * @param {number} barWidth
576 * @param {number} barHeight
577 * @param {number} unclippedBarX
578 * @param {number} timeToPixels
579 * @return {boolean}
580 */
581 decorateEntry(entryIndex, context, text, barX, barY, barWidth, barHeight, uncl ippedBarX, timeToPixels) {
582 var data = this._entryData[entryIndex];
583 var type = this._entryType(entryIndex);
584 if (type === Timeline.TimelineFlameChartEntryType.Frame) {
585 var /** @const */ vPadding = 1;
586 var /** @const */ hPadding = 1;
587 var frame = /** {!TimelineModel.TimelineFrame} */ (data);
588 barX += hPadding;
589 barWidth -= 2 * hPadding;
590 barY += vPadding;
591 barHeight -= 2 * vPadding + 1;
592 context.fillStyle = frame.idle ? 'white' : (frame.hasWarnings() ? '#fad1d1 ' : '#d7f0d1');
593 context.fillRect(barX, barY, barWidth, barHeight);
594 var frameDurationText = Number.preciseMillisToString(frame.duration, 1);
595 var textWidth = context.measureText(frameDurationText).width;
596 if (barWidth >= textWidth) {
597 context.fillStyle = this.textColor(entryIndex);
598 context.fillText(frameDurationText, barX + (barWidth - textWidth) / 2, b arY + barHeight - 3);
599 }
600 return true;
601 }
602
603 if (type === Timeline.TimelineFlameChartEntryType.InteractionRecord) {
604 var color = Timeline.TimelineUIUtils.interactionPhaseColor(
605 /** @type {!TimelineModel.TimelineIRModel.Phases} */ (data));
606 context.fillStyle = color;
607 context.fillRect(barX, barY, barWidth - 1, 2);
608 context.fillRect(barX, barY - 3, 2, 3);
609 context.fillRect(barX + barWidth - 3, barY - 3, 2, 3);
610 return false;
611 }
612
613 if (type === Timeline.TimelineFlameChartEntryType.Event) {
614 var event = /** @type {!SDK.TracingModel.Event} */ (data);
615 if (event.hasCategory(TimelineModel.TimelineModel.Category.LatencyInfo)) {
616 var timeWaitingForMainThread = TimelineModel.TimelineData.forEvent(event ).timeWaitingForMainThread;
617 if (timeWaitingForMainThread) {
618 context.fillStyle = 'hsla(0, 70%, 60%, 1)';
619 var width = Math.floor(unclippedBarX - barX + timeWaitingForMainThread * timeToPixels);
620 context.fillRect(barX, barY + barHeight - 3, width, 2);
621 }
622 }
623 if (TimelineModel.TimelineData.forEvent(event).warning)
624 paintWarningDecoration(barX, barWidth - 1.5);
625 }
626
627 /**
628 * @param {number} x
629 * @param {number} width
630 */
631 function paintWarningDecoration(x, width) {
632 var /** @const */ triangleSize = 8;
633 context.save();
634 context.beginPath();
635 context.rect(x, barY, width, barHeight);
636 context.clip();
637 context.beginPath();
638 context.fillStyle = 'red';
639 context.moveTo(x + width - triangleSize, barY);
640 context.lineTo(x + width, barY);
641 context.lineTo(x + width, barY + triangleSize);
642 context.fill();
643 context.restore();
644 }
645
646 return false;
647 }
648
649 /**
650 * @override
651 * @param {number} entryIndex
652 * @return {boolean}
653 */
654 forceDecoration(entryIndex) {
655 var type = this._entryType(entryIndex);
656 if (type === Timeline.TimelineFlameChartEntryType.Frame)
657 return true;
658
659 if (type === Timeline.TimelineFlameChartEntryType.Event) {
660 var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryI ndex]);
661 return !!TimelineModel.TimelineData.forEvent(event).warning;
662 }
663 return false;
664 }
665
666 /**
667 * @param {!{title: string, model: !SDK.TracingModel}} entry
668 */
669 appendExtensionEvents(entry) {
670 this._extensionInfo.push(entry);
671 if (this._timelineData)
672 this._innerAppendExtensionEvents(this._extensionInfo.length - 1);
673 }
674
675 /**
676 * @param {number} index
677 */
678 _innerAppendExtensionEvents(index) {
679 var entry = this._extensionInfo[index];
680 var entryType = Timeline.TimelineFlameChartEntryType.ExtensionEvent;
681 var allThreads = [].concat(...entry.model.sortedProcesses().map(process => p rocess.sortedThreads()));
682 if (!allThreads.length)
683 return;
684
685 this._appendHeader(entry.title, this._headerLevel1);
686 for (let thread of allThreads) {
687 this._appendAsyncEventsGroup(thread.name(), thread.asyncEvents(), this._he aderLevel2, entryType);
688 this._appendSyncEvents(thread.events(), thread.name(), this._headerLevel2, entryType, false);
689 }
690 }
691
692 /**
693 * @param {string} title
694 * @param {!PerfUI.FlameChart.GroupStyle} style
695 * @param {boolean=} expanded
696 */
697 _appendHeader(title, style, expanded) {
698 this._timelineData.groups.push({startLevel: this._currentLevel, name: title, expanded: expanded, style: style});
699 }
700
701 /**
702 * @param {!SDK.TracingModel.Event} event
703 * @param {number} level
704 */
705 _appendEvent(event, level) {
706 var index = this._entryData.length;
707 this._entryData.push(event);
708 this._timelineData.entryLevels[index] = level;
709 this._timelineData.entryTotalTimes[index] =
710 event.duration || Timeline.TimelineFlameChartDataProvider.InstantEventVi sibleDurationMs;
711 this._timelineData.entryStartTimes[index] = event.startTime;
712 }
713
714 /**
715 * @param {!SDK.TracingModel.AsyncEvent} asyncEvent
716 * @param {number} level
717 */
718 _appendAsyncEvent(asyncEvent, level) {
719 if (SDK.TracingModel.isNestableAsyncPhase(asyncEvent.phase)) {
720 // FIXME: also add steps once we support event nesting in the FlameChart.
721 this._appendEvent(asyncEvent, level);
722 return;
723 }
724 var steps = asyncEvent.steps;
725 // If we have past steps, put the end event for each range rather than start one.
726 var eventOffset = steps.length > 1 && steps[1].phase === SDK.TracingModel.Ph ase.AsyncStepPast ? 1 : 0;
727 for (var i = 0; i < steps.length - 1; ++i) {
728 var index = this._entryData.length;
729 this._entryData.push(steps[i + eventOffset]);
730 var startTime = steps[i].startTime;
731 this._timelineData.entryLevels[index] = level;
732 this._timelineData.entryTotalTimes[index] = steps[i + 1].startTime - start Time;
733 this._timelineData.entryStartTimes[index] = startTime;
734 }
735 }
736
737 /**
738 * @param {!SDK.TracingModel.Event} event
739 * @param {number} level
740 */
741 _appendFlowEvent(event, level) {
742 var timelineData = this._timelineData;
743 /**
744 * @param {!SDK.TracingModel.Event} event
745 * @return {number}
746 */
747 function pushStartFlow(event) {
748 var flowIndex = timelineData.flowStartTimes.length;
749 timelineData.flowStartTimes.push(event.startTime);
750 timelineData.flowStartLevels.push(level);
751 return flowIndex;
752 }
753
754 /**
755 * @param {!SDK.TracingModel.Event} event
756 * @param {number} flowIndex
757 */
758 function pushEndFlow(event, flowIndex) {
759 timelineData.flowEndTimes[flowIndex] = event.startTime;
760 timelineData.flowEndLevels[flowIndex] = level;
761 }
762
763 switch (event.phase) {
764 case SDK.TracingModel.Phase.FlowBegin:
765 this._flowEventIndexById.set(event.id, pushStartFlow(event));
766 break;
767 case SDK.TracingModel.Phase.FlowStep:
768 pushEndFlow(event, this._flowEventIndexById.get(event.id));
769 this._flowEventIndexById.set(event.id, pushStartFlow(event));
770 break;
771 case SDK.TracingModel.Phase.FlowEnd:
772 pushEndFlow(event, this._flowEventIndexById.get(event.id));
773 this._flowEventIndexById.delete(event.id);
774 break;
775 }
776 }
777
778 /**
779 * @param {!TimelineModel.TimelineFrame} frame
780 */
781 _appendFrame(frame) {
782 var index = this._entryData.length;
783 this._entryData.push(frame);
784 this._entryIndexToTitle[index] = Number.millisToString(frame.duration, true) ;
785 this._timelineData.entryLevels[index] = this._currentLevel;
786 this._timelineData.entryTotalTimes[index] = frame.duration;
787 this._timelineData.entryStartTimes[index] = frame.startTime;
788 }
789
790 /**
791 * @param {!Common.Segment} segment
792 */
793 _appendSegment(segment) {
794 var index = this._entryData.length;
795 this._entryData.push(/** @type {!TimelineModel.TimelineIRModel.Phases} */ (s egment.data));
796 this._entryIndexToTitle[index] = /** @type {string} */ (segment.data);
797 this._timelineData.entryLevels[index] = this._currentLevel;
798 this._timelineData.entryTotalTimes[index] = segment.end - segment.begin;
799 this._timelineData.entryStartTimes[index] = segment.begin;
800 }
801
802 /**
803 * @param {number} entryIndex
804 * @return {?Timeline.TimelineSelection}
805 */
806 createSelection(entryIndex) {
807 var type = this._entryType(entryIndex);
808 var timelineSelection = null;
809 if (type === Timeline.TimelineFlameChartEntryType.Event) {
810 timelineSelection = Timeline.TimelineSelection.fromTraceEvent(
811 /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]));
812 } else if (type === Timeline.TimelineFlameChartEntryType.Frame) {
813 timelineSelection = Timeline.TimelineSelection.fromFrame(
814 /** @type {!TimelineModel.TimelineFrame} */ (this._entryData[entryInde x]));
815 }
816 if (timelineSelection)
817 this._lastSelection = new Timeline.TimelineFlameChartView.Selection(timeli neSelection, entryIndex);
818 return timelineSelection;
819 }
820
821 /**
822 * @override
823 * @param {number} value
824 * @param {number=} precision
825 * @return {string}
826 */
827 formatValue(value, precision) {
828 return Number.preciseMillisToString(value, precision);
829 }
830
831 /**
832 * @override
833 * @param {number} entryIndex
834 * @return {boolean}
835 */
836 canJumpToEntry(entryIndex) {
837 return false;
838 }
839
840 /**
841 * @param {?Timeline.TimelineSelection} selection
842 * @return {number}
843 */
844 entryIndexForSelection(selection) {
845 if (!selection || selection.type() === Timeline.TimelineSelection.Type.Range )
846 return -1;
847
848 if (this._lastSelection && this._lastSelection.timelineSelection.object() == = selection.object())
849 return this._lastSelection.entryIndex;
850 var index = this._entryData.indexOf(
851 /** @type {!SDK.TracingModel.Event|!TimelineModel.TimelineFrame|!Timelin eModel.TimelineIRModel.Phases} */
852 (selection.object()));
853 if (index !== -1)
854 this._lastSelection = new Timeline.TimelineFlameChartView.Selection(select ion, index);
855 return index;
856 }
857
858 /**
859 * @param {!SDK.TracingModel.Event} event
860 * @return {?Timeline.TimelineSelection} selection
861 */
862 selectionForEvent(event) {
863 var entryIndex = this._entryData.indexOf(event);
864 return this.createSelection(entryIndex);
865 }
866
867 /**
868 * @param {!SDK.TracingModel.Event} event
869 * @return {boolean}
870 */
871 _isVisible(event) {
872 return this._filters.every(function(filter) {
873 return filter.accept(event);
874 });
875 }
876 };
877
878 Timeline.TimelineFlameChartDataProvider.InstantEventVisibleDurationMs = 0.001;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698