1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 * @param {!WebInspector.TracingManager} tracingManager
8 * @param {!WebInspector.TracingModel} tracingModel
9 * @param {!WebInspector.TimelineModel.Filter} recordFilter
10 * @extends {WebInspector.TimelineModel}
12 WebInspector.TracingTimelineModel = function(tracingManager, tracingModel, recordFilter)
14 WebInspector.TimelineModel.call(this);
16 this._tracingManager = tracingManager;
17 this._tracingModel = tracingModel;
18 this._recordFilter = recordFilter;
19 this._tracingManager.addEventListener(WebInspector.TracingManager.Events.TracingStarted, this._onTracingStarted, this);
20 this._tracingManager.addEventListener(WebInspector.TracingManager.Events.EventsCollected, this._onEventsCollected, this);
21 this._tracingManager.addEventListener(WebInspector.TracingManager.Events.TracingComplete, this._onTracingComplete, this);
25 WebInspector.TracingTimelineModel.RecordType = {
27 EventDispatch: "EventDispatch",
31 RequestMainThreadFrame: "RequestMainThreadFrame",
32 BeginFrame: "BeginFrame",
33 BeginMainThreadFrame: "BeginMainThreadFrame",
34 ActivateLayerTree: "ActivateLayerTree",
35 DrawFrame: "DrawFrame",
36 ScheduleStyleRecalculation: "ScheduleStyleRecalculation",
37 RecalculateStyles: "RecalculateStyles",
38 InvalidateLayout: "InvalidateLayout",
40 UpdateLayer: "UpdateLayer",
41 UpdateLayerTree: "UpdateLayerTree",
42 PaintSetup: "PaintSetup",
44 PaintImage: "PaintImage",
45 Rasterize: "Rasterize",
46 RasterTask: "RasterTask",
47 ScrollLayer: "ScrollLayer",
48 CompositeLayers: "CompositeLayers",
50 StyleRecalcInvalidationTracking: "StyleRecalcInvalidationTracking",
51 LayoutInvalidationTracking: "LayoutInvalidationTracking",
52 LayerInvalidationTracking: "LayerInvalidationTracking",
53 PaintInvalidationTracking: "PaintInvalidationTracking",
55 ParseHTML: "ParseHTML",
57 TimerInstall: "TimerInstall",
58 TimerRemove: "TimerRemove",
59 TimerFire: "TimerFire",
61 XHRReadyStateChange: "XHRReadyStateChange",
63 EvaluateScript: "EvaluateScript",
66 MarkDOMContent: "MarkDOMContent",
67 MarkFirstPaint: "MarkFirstPaint",
69 TimeStamp: "TimeStamp",
70 ConsoleTime: "ConsoleTime",
72 ResourceSendRequest: "ResourceSendRequest",
73 ResourceReceiveResponse: "ResourceReceiveResponse",
74 ResourceReceivedData: "ResourceReceivedData",
75 ResourceFinish: "ResourceFinish",
77 FunctionCall: "FunctionCall",
82 UpdateCounters: "UpdateCounters",
84 RequestAnimationFrame: "RequestAnimationFrame",
85 CancelAnimationFrame: "CancelAnimationFrame",
86 FireAnimationFrame: "FireAnimationFrame",
88 WebSocketCreate : "WebSocketCreate",
89 WebSocketSendHandshakeRequest : "WebSocketSendHandshakeRequest",
90 WebSocketReceiveHandshakeResponse : "WebSocketReceiveHandshakeResponse",
91 WebSocketDestroy : "WebSocketDestroy",
93 EmbedderCallback : "EmbedderCallback",
95 CallStack: "CallStack",
96 SetLayerTreeId: "SetLayerTreeId",
97 TracingStartedInPage: "TracingStartedInPage",
98 TracingSessionIdForWorker: "TracingSessionIdForWorker",
100 DecodeImage: "Decode Image",
101 ResizeImage: "Resize Image",
102 DrawLazyPixelRef: "Draw LazyPixelRef",
103 DecodeLazyPixelRef: "Decode LazyPixelRef",
105 LazyPixelRef: "LazyPixelRef",
106 LayerTreeHostImplSnapshot: "cc::LayerTreeHostImpl",
107 PictureSnapshot: "cc::Picture",
109 // CpuProfile is a virtual event created on frontend to support
110 // serialization of CPU Profiles within tracing timeline data.
111 CpuProfile: "CpuProfile"
116 * @param {string} name
118 WebInspector.TracingTimelineModel.VirtualThread = function(name)
121 /** @type {!Array.<!WebInspector.TracingModel.Event>} */
123 /** @type {!Array.<!Array.<!WebInspector.TracingModel.Event>>} */
124 this.asyncEvents = [];
127 WebInspector.TracingTimelineModel.prototype = {
129 * @param {boolean} captureCauses
130 * @param {boolean} enableJSSampling
131 * @param {boolean} captureMemory
132 * @param {boolean} capturePictures
134 startRecording: function(captureCauses, enableJSSampling, captureMemory, capturePictures)
136 function disabledByDefault(category)
138 return "disabled-by-default-" + category;
140 var categoriesArray = [
142 disabledByDefault("devtools.timeline"),
143 disabledByDefault("devtools.timeline.frame"),
144 WebInspector.TracingModel.ConsoleEventCategory
146 if (captureCauses || enableJSSampling)
147 categoriesArray.push(disabledByDefault("devtools.timeline.stack"));
148 if (enableJSSampling) {
149 this._jsProfilerStarted = true;
150 this._currentTarget = WebInspector.context.flavor(WebInspector.Target);
151 this._configureCpuProfilerSamplingInterval();
152 this._currentTarget.profilerAgent().start();
154 if (captureCauses && Runtime.experiments.isEnabled("timelineInvalidationTracking"))
155 categoriesArray.push(disabledByDefault("devtools.timeline.invalidationTracking"));
156 if (capturePictures) {
157 categoriesArray = categoriesArray.concat([
158 disabledByDefault("devtools.timeline.layers"),
159 disabledByDefault("devtools.timeline.picture"),
160 disabledByDefault("blink.graphics_context_annotations")]);
162 var categories = categoriesArray.join(",");
163 this._startRecordingWithCategories(categories);
166 stopRecording: function()
168 if (this._jsProfilerStarted) {
169 this._stopCallbackBarrier = new CallbackBarrier();
170 this._currentTarget.profilerAgent().stop(this._stopCallbackBarrier.createCallback(this._didStopRecordingJSSamples.bind(this)));
171 this._jsProfilerStarted = false;
173 this._tracingManager.stop();
177 * @param {!Array.<!WebInspector.TracingManager.EventPayload>} events
179 setEventsForTest: function(events)
181 this._startCollectingTraceEvents(false);
182 this._tracingModel.addEvents(events);
183 this._onTracingComplete();
186 _configureCpuProfilerSamplingInterval: function()
188 var intervalUs = WebInspector.settings.highResolutionCpuProfiling.get() ? 100 : 1000;
189 this._currentTarget.profilerAgent().setSamplingInterval(intervalUs, didChangeInterval);
191 function didChangeInterval(error)
194 WebInspector.console.error(error);
199 * @param {string} categories
201 _startRecordingWithCategories: function(categories)
203 this._tracingManager.start(categories, "");
206 _onTracingStarted: function()
208 this._startCollectingTraceEvents(false);
212 * @param {boolean} fromFile
214 _startCollectingTraceEvents: function(fromFile)
217 this._tracingModel.reset();
218 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordingStarted, { fromFile: fromFile });
222 * @param {!WebInspector.Event} event
224 _onEventsCollected: function(event)
226 var traceEvents = /** @type {!Array.<!WebInspector.TracingManager.EventPayload>} */ (event.data);
227 this._tracingModel.addEvents(traceEvents);
230 _onTracingComplete: function()
232 if (this._stopCallbackBarrier)
233 this._stopCallbackBarrier.callWhenDone(this._didStopRecordingTraceEvents.bind(this));
235 this._didStopRecordingTraceEvents();
239 * @param {?Protocol.Error} error
240 * @param {?ProfilerAgent.CPUProfile} cpuProfile
242 _didStopRecordingJSSamples: function(error, cpuProfile)
245 WebInspector.console.error(error);
246 this._recordedCpuProfile = cpuProfile;
249 _didStopRecordingTraceEvents: function()
251 this._stopCallbackBarrier = null;
253 if (this._recordedCpuProfile) {
254 this._injectCpuProfileEvent(this._recordedCpuProfile);
255 this._recordedCpuProfile = null;
257 this._tracingModel.tracingComplete();
259 var events = this._tracingModel.devtoolsPageMetadataEvents();
260 var workerMetadataEvents = this._tracingModel.devtoolsWorkerMetadataEvents();
262 this._resetProcessingState();
263 for (var i = 0, length = events.length; i < length; i++) {
264 var event = events[i];
265 var process = event.thread.process();
266 var startTime = event.startTime;
268 var endTime = Infinity;
270 endTime = events[i + 1].startTime;
272 var threads = process.sortedThreads();
273 for (var j = 0; j < threads.length; j++) {
274 var thread = threads[j];
275 if (thread.name() === "WebCore: Worker" && !workerMetadataEvents.some(function(e) { return e.args["data"]["workerThreadId"] === thread.id(); }))
277 this._processThreadEvents(startTime, endTime, event.thread, thread);
280 this._resetProcessingState();
282 this._inspectedTargetEvents.sort(WebInspector.TracingModel.Event.compareStartTime);
284 if (this._cpuProfile) {
285 this._processCpuProfile(this._cpuProfile);
286 this._cpuProfile = null;
288 this._buildTimelineRecords();
289 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordingStopped);
293 * @param {!ProfilerAgent.CPUProfile} cpuProfile
295 _injectCpuProfileEvent: function(cpuProfile)
297 var metaEvent = this._tracingModel.devtoolsPageMetadataEvents().peekLast();
300 var cpuProfileEvent = /** @type {!WebInspector.TracingManager.EventPayload} */ ({
301 cat: WebInspector.TracingModel.DevToolsMetadataEventCategory,
302 ph: WebInspector.TracingModel.Phase.Instant,
303 ts: this._tracingModel.maximumRecordTime() * 1000,
304 pid: metaEvent.thread.process().id(),
305 tid: metaEvent.thread.id(),
306 name: WebInspector.TracingTimelineModel.RecordType.CpuProfile,
307 args: { data: { cpuProfile: cpuProfile } }
309 this._tracingModel.addEvents([cpuProfileEvent]);
315 containsJSSamples: function()
317 return this._containsJSSamples;
321 * @param {!ProfilerAgent.CPUProfile} cpuProfile
323 _processCpuProfile: function(cpuProfile)
325 this._containsJSSamples = true;
326 var jsSamples = WebInspector.TimelineJSProfileProcessor.generateTracingEventsFromCpuProfile(this, cpuProfile);
327 this._inspectedTargetEvents = this._inspectedTargetEvents.mergeOrdered(jsSamples, WebInspector.TracingModel.Event.orderedCompareStartTime);
328 this._setMainThreadEvents(this.mainThreadEvents().mergeOrdered(jsSamples, WebInspector.TracingModel.Event.orderedCompareStartTime));
334 minimumRecordTime: function()
336 return this._tracingModel.minimumRecordTime();
342 maximumRecordTime: function()
344 return this._tracingModel.maximumRecordTime();
348 * @return {!Array.<!WebInspector.TracingModel.Event>}
350 inspectedTargetEvents: function()
352 return this._inspectedTargetEvents;
356 * @return {!Array.<!WebInspector.TracingModel.Event>}
358 mainThreadEvents: function()
360 return this._mainThreadEvents;
364 * @param {!Array.<!WebInspector.TracingModel.Event>} events
366 _setMainThreadEvents: function(events)
368 this._mainThreadEvents = events;
372 * @return {!Array.<!Array.<!WebInspector.TracingModel.Event>>}
374 mainThreadAsyncEvents: function()
376 return this._mainThreadAsyncEvents;
380 * @return {!Array.<!WebInspector.TracingTimelineModel.VirtualThread>}
382 virtualThreads: function()
384 return this._virtualThreads;
388 * @param {!WebInspector.ChunkedFileReader} fileReader
389 * @param {!WebInspector.Progress} progress
390 * @return {!WebInspector.OutputStream}
392 createLoader: function(fileReader, progress)
394 return new WebInspector.TracingModelLoader(this, fileReader, progress);
398 * @param {!WebInspector.OutputStream} stream
400 writeToStream: function(stream)
402 var saver = new WebInspector.TracingTimelineSaver(stream);
403 this._tracingModel.writeToStream(stream, saver);
408 this._virtualThreads = [];
409 this._mainThreadEvents = [];
410 this._mainThreadAsyncEvents = [];
411 this._inspectedTargetEvents = [];
412 this._containsJSSamples = false;
413 WebInspector.TimelineModel.prototype.reset.call(this);
416 _buildTimelineRecords: function()
418 var topLevelRecords = this._buildTimelineRecordsForThread(this.mainThreadEvents());
421 * @param {!WebInspector.TracingTimelineModel.TraceEventRecord} a
422 * @param {!WebInspector.TracingTimelineModel.TraceEventRecord} b
425 function compareRecordStartTime(a, b)
427 // Never return 0 as otherwise equal records would be merged.
428 return (a.startTime() <= b.startTime()) ? -1 : +1;
432 * @param {!WebInspector.TracingTimelineModel.VirtualThread} virtualThread
433 * @this {!WebInspector.TracingTimelineModel}
435 function processVirtualThreadEvents(virtualThread)
437 var threadRecords = this._buildTimelineRecordsForThread(virtualThread.events);
438 topLevelRecords = topLevelRecords.mergeOrdered(threadRecords, compareRecordStartTime);
440 this.virtualThreads().forEach(processVirtualThreadEvents.bind(this));
442 for (var i = 0; i < topLevelRecords.length; i++)
443 this._addTopLevelRecord(topLevelRecords[i]);
447 * @param {!Array.<!WebInspector.TracingModel.Event>} threadEvents
448 * @return {!Array.<!WebInspector.TracingTimelineModel.TraceEventRecord>}
450 _buildTimelineRecordsForThread: function(threadEvents)
452 var recordStack = [];
453 var topLevelRecords = [];
455 for (var i = 0, size = threadEvents.length; i < size; ++i) {
456 var event = threadEvents[i];
457 for (var top = recordStack.peekLast(); top && top._event.endTime <= event.startTime; top = recordStack.peekLast()) {
459 if (!recordStack.length)
460 topLevelRecords.push(top);
462 if (event.phase === WebInspector.TracingModel.Phase.AsyncEnd || event.phase === WebInspector.TracingModel.Phase.NestableAsyncEnd)
464 var parentRecord = recordStack.peekLast();
465 // Maintain the back-end logic of old timeline, skip console.time() / console.timeEnd() that are not properly nested.
466 if (WebInspector.TracingModel.isAsyncBeginPhase(event.phase) && parentRecord && event.endTime > parentRecord._event.endTime)
468 var record = new WebInspector.TracingTimelineModel.TraceEventRecord(this, event);
469 if (WebInspector.TracingTimelineUIUtils.isMarkerEvent(event))
470 this._eventDividerRecords.push(record);
471 if (!this._recordFilter.accept(record))
474 parentRecord._addChild(record);
476 recordStack.push(record);
479 if (recordStack.length)
480 topLevelRecords.push(recordStack[0]);
482 return topLevelRecords;
486 * @param {!WebInspector.TracingTimelineModel.TraceEventRecord} record
488 _addTopLevelRecord: function(record)
490 this._records.push(record);
491 if (record.type() === WebInspector.TracingTimelineModel.RecordType.Program)
492 this._mainThreadTasks.push(record);
493 if (record.type() === WebInspector.TracingTimelineModel.RecordType.GPUTask)
494 this._gpuThreadTasks.push(record);
495 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordAdded, record);
498 _resetProcessingState: function()
500 this._sendRequestEvents = {};
501 this._timerEvents = {};
502 this._requestAnimationFrameEvents = {};
503 this._invalidationTracker = new WebInspector.InvalidationTracker();
504 this._layoutInvalidate = {};
505 this._lastScheduleStyleRecalculation = {};
506 this._webSocketCreateEvents = {};
507 this._paintImageEventByPixelRefId = {};
508 this._lastPaintForLayer = {};
509 this._lastRecalculateStylesEvent = null;
510 this._currentScriptEvent = null;
511 this._eventStack = [];
515 * @param {number} startTime
516 * @param {?number} endTime
517 * @param {!WebInspector.TracingModel.Thread} mainThread
518 * @param {!WebInspector.TracingModel.Thread} thread
520 _processThreadEvents: function(startTime, endTime, mainThread, thread)
522 var events = thread.events();
523 var length = events.length;
524 var i = events.lowerBound(startTime, function (time, event) { return time - event.startTime });
527 if (thread === mainThread) {
528 threadEvents = this._mainThreadEvents;
529 this._mainThreadAsyncEvents = this._mainThreadAsyncEvents.concat(thread.asyncEvents());
531 var virtualThread = new WebInspector.TracingTimelineModel.VirtualThread(thread.name());
532 threadEvents = virtualThread.events;
533 virtualThread.asyncEvents = virtualThread.asyncEvents.concat(thread.asyncEvents());
534 this._virtualThreads.push(virtualThread);
537 this._eventStack = [];
538 for (; i < length; i++) {
539 var event = events[i];
540 if (endTime && event.startTime >= endTime)
542 this._processEvent(event);
543 threadEvents.push(event);
544 this._inspectedTargetEvents.push(event);
549 * @param {!WebInspector.TracingModel.Event} event
551 _processEvent: function(event)
553 var recordTypes = WebInspector.TracingTimelineModel.RecordType;
555 var eventStack = this._eventStack;
556 while (eventStack.length && eventStack.peekLast().endTime < event.startTime)
558 var duration = event.duration;
560 if (eventStack.length) {
561 var parent = eventStack.peekLast();
562 parent.selfTime -= duration;
564 event.selfTime = duration;
565 eventStack.push(event);
568 if (this._currentScriptEvent && event.startTime > this._currentScriptEvent.endTime)
569 this._currentScriptEvent = null;
571 switch (event.name) {
572 case recordTypes.CallStack:
573 var lastMainThreadEvent = this.mainThreadEvents().peekLast();
574 if (lastMainThreadEvent && event.args["stack"] && event.args["stack"].length)
575 lastMainThreadEvent.stackTrace = event.args["stack"];
578 case recordTypes.CpuProfile:
579 this._cpuProfile = event.args["data"]["cpuProfile"];
582 case recordTypes.ResourceSendRequest:
583 this._sendRequestEvents[event.args["data"]["requestId"]] = event;
584 event.imageURL = event.args["data"]["url"];
587 case recordTypes.ResourceReceiveResponse:
588 case recordTypes.ResourceReceivedData:
589 case recordTypes.ResourceFinish:
590 event.initiator = this._sendRequestEvents[event.args["data"]["requestId"]];
592 event.imageURL = event.initiator.imageURL;
595 case recordTypes.TimerInstall:
596 this._timerEvents[event.args["data"]["timerId"]] = event;
599 case recordTypes.TimerFire:
600 event.initiator = this._timerEvents[event.args["data"]["timerId"]];
603 case recordTypes.RequestAnimationFrame:
604 this._requestAnimationFrameEvents[event.args["data"]["id"]] = event;
607 case recordTypes.FireAnimationFrame:
608 event.initiator = this._requestAnimationFrameEvents[event.args["data"]["id"]];
611 case recordTypes.ScheduleStyleRecalculation:
612 this._lastScheduleStyleRecalculation[event.args["frame"]] = event;
615 case recordTypes.RecalculateStyles:
616 this._invalidationTracker.didRecalcStyle(event);
617 event.initiator = this._lastScheduleStyleRecalculation[event.args["frame"]];
618 this._lastRecalculateStylesEvent = event;
621 case recordTypes.StyleRecalcInvalidationTracking:
622 case recordTypes.LayoutInvalidationTracking:
623 case recordTypes.LayerInvalidationTracking:
624 case recordTypes.PaintInvalidationTracking:
625 this._invalidationTracker.addInvalidation(event);
628 case recordTypes.InvalidateLayout:
629 // Consider style recalculation as a reason for layout invalidation,
630 // but only if we had no earlier layout invalidation records.
631 var layoutInitator = event;
632 var frameId = event.args["frame"];
633 if (!this._layoutInvalidate[frameId] && this._lastRecalculateStylesEvent && this._lastRecalculateStylesEvent.endTime > event.startTime)
634 layoutInitator = this._lastRecalculateStylesEvent.initiator;
635 this._layoutInvalidate[frameId] = layoutInitator;
638 case recordTypes.Layout:
639 this._invalidationTracker.didLayout(event);
640 var frameId = event.args["beginData"]["frame"];
641 event.initiator = this._layoutInvalidate[frameId];
642 // In case we have no closing Layout event, endData is not available.
643 if (event.args["endData"]) {
644 event.backendNodeId = event.args["endData"]["rootNode"];
645 event.highlightQuad = event.args["endData"]["root"];
647 this._layoutInvalidate[frameId] = null;
648 if (this._currentScriptEvent)
649 event.warning = WebInspector.UIString("Forced synchronous layout is a possible performance bottleneck.");
652 case recordTypes.WebSocketCreate:
653 this._webSocketCreateEvents[event.args["data"]["identifier"]] = event;
656 case recordTypes.WebSocketSendHandshakeRequest:
657 case recordTypes.WebSocketReceiveHandshakeResponse:
658 case recordTypes.WebSocketDestroy:
659 event.initiator = this._webSocketCreateEvents[event.args["data"]["identifier"]];
662 case recordTypes.EvaluateScript:
663 case recordTypes.FunctionCall:
664 if (!this._currentScriptEvent)
665 this._currentScriptEvent = event;
668 case recordTypes.SetLayerTreeId:
669 this._inspectedTargetLayerTreeId = event.args["layerTreeId"];
672 case recordTypes.Paint:
673 this._invalidationTracker.didPaint(event);
674 event.highlightQuad = event.args["data"]["clip"];
675 event.backendNodeId = event.args["data"]["nodeId"];
676 var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLayer);
677 if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== this._inspectedTargetLayerTreeId)
679 // Only keep layer paint events, skip paints for subframes that get painted to the same layer as parent.
680 if (!event.args["data"]["layerId"])
682 this._lastPaintForLayer[layerUpdateEvent.args["layerId"]] = event;
685 case recordTypes.PictureSnapshot:
686 var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLayer);
687 if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== this._inspectedTargetLayerTreeId)
689 var paintEvent = this._lastPaintForLayer[layerUpdateEvent.args["layerId"]];
691 paintEvent.picture = event;
694 case recordTypes.ScrollLayer:
695 event.backendNodeId = event.args["data"]["nodeId"];
698 case recordTypes.PaintImage:
699 event.backendNodeId = event.args["data"]["nodeId"];
700 event.imageURL = event.args["data"]["url"];
703 case recordTypes.DecodeImage:
704 case recordTypes.ResizeImage:
705 var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage);
706 if (!paintImageEvent) {
707 var decodeLazyPixelRefEvent = this._findAncestorEvent(recordTypes.DecodeLazyPixelRef);
708 paintImageEvent = decodeLazyPixelRefEvent && this._paintImageEventByPixelRefId[decodeLazyPixelRefEvent.args["LazyPixelRef"]];
710 if (!paintImageEvent)
712 event.backendNodeId = paintImageEvent.backendNodeId;
713 event.imageURL = paintImageEvent.imageURL;
716 case recordTypes.DrawLazyPixelRef:
717 var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage);
718 if (!paintImageEvent)
720 this._paintImageEventByPixelRefId[event.args["LazyPixelRef"]] = paintImageEvent;
721 event.backendNodeId = paintImageEvent.backendNodeId;
722 event.imageURL = paintImageEvent.imageURL;
728 * @param {string} name
729 * @return {?WebInspector.TracingModel.Event}
731 _findAncestorEvent: function(name)
733 for (var i = this._eventStack.length - 1; i >= 0; --i) {
734 var event = this._eventStack[i];
735 if (event.name === name)
741 __proto__: WebInspector.TimelineModel.prototype
747 WebInspector.TracingTimelineModel.Filter = function() { }
749 WebInspector.TracingTimelineModel.Filter.prototype = {
751 * @param {!WebInspector.TracingModel.Event} event
754 accept: function(event) { }
759 * @implements {WebInspector.TracingTimelineModel.Filter}
760 * @param {!Array.<string>} eventNames
762 WebInspector.TracingTimelineModel.EventNameFilter = function(eventNames)
764 this._eventNames = eventNames.keySet();
767 WebInspector.TracingTimelineModel.EventNameFilter.prototype = {
769 * @param {!WebInspector.TracingModel.Event} event
772 accept: function(event)
774 throw new Error("Not implemented.");
780 * @extends {WebInspector.TracingTimelineModel.EventNameFilter}
781 * @param {!Array.<string>} includeNames
783 WebInspector.TracingTimelineModel.InclusiveEventNameFilter = function(includeNames)
785 WebInspector.TracingTimelineModel.EventNameFilter.call(this, includeNames)
788 WebInspector.TracingTimelineModel.InclusiveEventNameFilter.prototype = {
791 * @param {!WebInspector.TracingModel.Event} event
794 accept: function(event)
796 return event.category === WebInspector.TracingModel.ConsoleEventCategory || !!this._eventNames[event.name];
798 __proto__: WebInspector.TracingTimelineModel.EventNameFilter.prototype
803 * @extends {WebInspector.TracingTimelineModel.EventNameFilter}
804 * @param {!Array.<string>} excludeNames
806 WebInspector.TracingTimelineModel.ExclusiveEventNameFilter = function(excludeNames)
808 WebInspector.TracingTimelineModel.EventNameFilter.call(this, excludeNames)
811 WebInspector.TracingTimelineModel.ExclusiveEventNameFilter.prototype = {
814 * @param {!WebInspector.TracingModel.Event} event
817 accept: function(event)
819 return !this._eventNames[event.name];
821 __proto__: WebInspector.TracingTimelineModel.EventNameFilter.prototype
826 * @implements {WebInspector.TimelineModel.Record}
827 * @param {!WebInspector.TimelineModel} model
828 * @param {!WebInspector.TracingModel.Event} traceEvent
830 WebInspector.TracingTimelineModel.TraceEventRecord = function(model, traceEvent)
833 this._event = traceEvent;
834 traceEvent._timelineRecord = this;
838 WebInspector.TracingTimelineModel.TraceEventRecord.prototype = {
840 * @return {?Array.<!ConsoleAgent.CallFrame>}
842 callSiteStackTrace: function()
844 var initiator = this._event.initiator;
845 return initiator ? initiator.stackTrace : null;
849 * @return {?WebInspector.TimelineModel.Record}
851 initiator: function()
853 var initiator = this._event.initiator;
854 return initiator ? initiator._timelineRecord : null;
858 * @return {?WebInspector.Target}
862 return this._event.thread.target();
870 return this._event.selfTime;
874 * @return {!Array.<!WebInspector.TimelineModel.Record>}
878 return this._children;
884 startTime: function()
886 return this._event.startTime;
894 if (this._event.thread.name() === "CrRendererMain")
895 return WebInspector.TimelineModel.MainThreadName;
896 return this._event.thread.name();
904 return this._endTime || this._event.endTime || this._event.startTime;
908 * @param {number} endTime
910 setEndTime: function(endTime)
912 this._endTime = endTime;
920 return this._event.args["data"];
928 if (this._event.category === WebInspector.TracingModel.ConsoleEventCategory)
929 return WebInspector.TracingTimelineModel.RecordType.ConsoleTime;
930 return this._event.name;
938 switch (this._event.name) {
939 case WebInspector.TracingTimelineModel.RecordType.ScheduleStyleRecalculation:
940 case WebInspector.TracingTimelineModel.RecordType.RecalculateStyles:
941 case WebInspector.TracingTimelineModel.RecordType.InvalidateLayout:
942 return this._event.args["frameId"];
943 case WebInspector.TracingTimelineModel.RecordType.Layout:
944 return this._event.args["beginData"]["frameId"];
946 var data = this._event.args["data"];
947 return (data && data["frame"]) || "";
952 * @return {?Array.<!ConsoleAgent.CallFrame>}
954 stackTrace: function()
956 return this._event.stackTrace;
960 * @param {string} key
963 getUserObject: function(key)
965 if (key === "TimelineUIUtils::preview-element")
966 return this._event.previewElement;
967 throw new Error("Unexpected key: " + key);
971 * @param {string} key
972 * @param {?Object|undefined} value
974 setUserObject: function(key, value)
976 if (key !== "TimelineUIUtils::preview-element")
977 throw new Error("Unexpected key: " + key);
978 this._event.previewElement = /** @type {?Element} */ (value);
982 * @return {?Array.<string>}
986 if (this._event.warning)
987 return [this._event.warning];
992 * @return {!WebInspector.TracingModel.Event}
994 traceEvent: function()
1000 * @param {!WebInspector.TracingTimelineModel.TraceEventRecord} child
1002 _addChild: function(child)
1004 this._children.push(child);
1005 child.parent = this;
1009 * @return {!WebInspector.TimelineModel}
1011 timelineModel: function()
1021 * @implements {WebInspector.OutputStream}
1022 * @param {!WebInspector.TracingTimelineModel} model
1023 * @param {!{cancel: function()}} reader
1024 * @param {!WebInspector.Progress} progress
1026 WebInspector.TracingModelLoader = function(model, reader, progress)
1028 this._model = model;
1029 this._reader = reader;
1030 this._progress = progress;
1032 this._firstChunk = true;
1033 this._loader = new WebInspector.TracingModel.Loader(model._tracingModel);
1036 WebInspector.TracingModelLoader.prototype = {
1038 * @param {string} chunk
1040 write: function(chunk)
1042 var data = this._buffer + chunk;
1047 lastIndex = WebInspector.TextUtils.findBalancedCurlyBrackets(data, index);
1048 } while (lastIndex !== -1)
1050 var json = data.slice(0, index) + "]";
1051 this._buffer = data.slice(index);
1056 if (this._firstChunk) {
1057 this._model._startCollectingTraceEvents(true);
1059 var commaIndex = json.indexOf(",");
1060 if (commaIndex !== -1)
1061 json = json.slice(commaIndex + 1);
1067 items = /** @type {!Array.<!WebInspector.TracingManager.EventPayload>} */ (JSON.parse(json));
1069 this._reportErrorAndCancelLoading("Malformed timeline data: " + e);
1073 if (this._firstChunk) {
1074 this._firstChunk = false;
1075 if (this._looksLikeAppVersion(items[0])) {
1076 this._reportErrorAndCancelLoading("Old Timeline format is not supported.");
1082 this._loader.loadNextChunk(items);
1084 this._reportErrorAndCancelLoading("Malformed timeline data: " + e);
1089 _reportErrorAndCancelLoading: function(messsage)
1091 WebInspector.console.error(messsage);
1092 this._model._onTracingComplete();
1093 this._model.reset();
1094 this._reader.cancel();
1095 this._progress.done();
1098 _looksLikeAppVersion: function(item)
1100 return typeof item === "string" && item.indexOf("Chrome") !== -1;
1105 this._loader.finish();
1106 this._model._onTracingComplete();
1112 * @param {!WebInspector.OutputStream} stream
1113 * @implements {WebInspector.OutputStreamDelegate}
1115 WebInspector.TracingTimelineSaver = function(stream)
1117 this._stream = stream;
1120 WebInspector.TracingTimelineSaver.prototype = {
1121 onTransferStarted: function()
1123 this._stream.write("[");
1126 onTransferFinished: function()
1128 this._stream.write("]");
1132 * @param {!WebInspector.ChunkedReader} reader
1134 onChunkTransferred: function(reader) { },
1137 * @param {!WebInspector.ChunkedReader} reader
1138 * @param {!Event} event
1140 onError: function(reader, event) { },
1145 * @param {!WebInspector.TracingModel.Event} event
1147 WebInspector.InvalidationTrackingEvent = function(event)
1149 this.type = event.name;
1150 this.frameId = event.args["data"]["frame"];
1151 this.nodeId = event.args["data"]["nodeId"];
1152 this.nodeName = event.args["data"]["nodeName"];
1153 this.paintId = event.args["data"]["paintId"];
1154 this.reason = event.args["data"]["reason"];
1155 this.stackTrace = event.args["data"]["stackTrace"];
1161 WebInspector.InvalidationTracker = function()
1163 this._initializePerFrameState();
1166 WebInspector.InvalidationTracker.prototype = {
1168 * @param {!WebInspector.TracingModel.Event} event
1170 addInvalidation: function(event)
1172 var invalidation = new WebInspector.InvalidationTrackingEvent(event);
1174 this._startNewFrameIfNeeded();
1175 if (!invalidation.nodeId && !invalidation.paintId) {
1176 console.error("Invalidation lacks node information.");
1177 console.error(invalidation);
1180 // Record the paintIds for style recalc or layout invalidations.
1181 // FIXME: This O(n^2) loop could be optimized with a map.
1182 var recordTypes = WebInspector.TracingTimelineModel.RecordType;
1183 if (invalidation.type == recordTypes.PaintInvalidationTracking)
1184 this._invalidationEvents.forEach(updatePaintId);
1186 this._invalidationEvents.push(invalidation);
1188 function updatePaintId(invalidationToUpdate)
1190 if (invalidationToUpdate.nodeId !== invalidation.nodeId)
1192 if (invalidationToUpdate.type === recordTypes.StyleRecalcInvalidationTracking
1193 || invalidationToUpdate.type === recordTypes.LayoutInvalidationTracking) {
1194 invalidationToUpdate.paintId = invalidation.paintId;
1200 * @param {!WebInspector.TracingModel.Event} styleRecalcEvent
1202 didRecalcStyle: function(styleRecalcEvent)
1204 var recalcFrameId = styleRecalcEvent.args["frame"];
1205 var index = this._lastStyleRecalcEventIndex;
1206 var invalidationCount = this._invalidationEvents.length;
1207 for (; index < invalidationCount; index++) {
1208 var invalidation = this._invalidationEvents[index];
1209 if (invalidation.type !== WebInspector.TracingTimelineModel.RecordType.StyleRecalcInvalidationTracking)
1211 if (invalidation.frameId === recalcFrameId)
1212 this._addInvalidationTrackingEvent(styleRecalcEvent, invalidation);
1215 this._lastStyleRecalcEventIndex = invalidationCount;
1219 * @param {!WebInspector.TracingModel.Event} layoutEvent
1221 didLayout: function(layoutEvent)
1223 var layoutFrameId = layoutEvent.args["beginData"]["frame"];
1224 var index = this._lastLayoutEventIndex;
1225 var invalidationCount = this._invalidationEvents.length;
1226 for (; index < invalidationCount; index++) {
1227 var invalidation = this._invalidationEvents[index];
1228 if (invalidation.type !== WebInspector.TracingTimelineModel.RecordType.LayoutInvalidationTracking)
1230 if (invalidation.frameId === layoutFrameId)
1231 this._addInvalidationTrackingEvent(layoutEvent, invalidation);
1234 this._lastLayoutEventIndex = invalidationCount;
1238 * @param {!WebInspector.TracingModel.Event} paintEvent
1240 didPaint: function(paintEvent)
1242 this._didPaint = true;
1244 // If a paint doesn't have a corresponding graphics layer id, it paints
1245 // into its parent so add an effectivePaintId to these events.
1246 var layerId = paintEvent.args["data"]["layerId"];
1248 this._lastPaintWithLayer = paintEvent;
1249 if (!this._lastPaintWithLayer) {
1250 console.error("Failed to find the paint container for a paint event.");
1254 var effectivePaintId = this._lastPaintWithLayer.args["data"]["nodeId"];
1255 var frameId = paintEvent.args["data"]["frame"];
1256 this._invalidationEvents.forEach(recordInvalidationForPaint.bind(this));
1259 * @param {!WebInspector.InvalidationTrackingEvent} invalidation
1260 * @this {WebInspector.InvalidationTracker}
1262 function recordInvalidationForPaint(invalidation)
1264 if (invalidation.paintId === effectivePaintId && invalidation.frameId === frameId)
1265 this._addInvalidationTrackingEvent(paintEvent, invalidation);
1270 * @param {!WebInspector.TracingModel.Event} event
1271 * @param {!WebInspector.InvalidationTrackingEvent} invalidation
1273 _addInvalidationTrackingEvent: function(event, invalidation)
1275 if (!event.invalidationTrackingEvents)
1276 event.invalidationTrackingEvents = [ invalidation ];
1278 event.invalidationTrackingEvents.push(invalidation);
1281 _startNewFrameIfNeeded: function()
1283 if (!this._didPaint)
1286 this._initializePerFrameState();
1289 _initializePerFrameState: function()
1291 /** @type {!Array.<!WebInspector.InvalidationTrackingEvent>} */
1292 this._invalidationEvents = [];
1293 this._lastStyleRecalcEventIndex = 0;
1294 this._lastLayoutEventIndex = 0;
1295 this._lastPaintWithLayer = undefined;
1296 this._didPaint = false;