summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/WebKit/Source/devtools/front_end/timeline/TracingTimelineModel.js
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/WebKit/Source/devtools/front_end/timeline/TracingTimelineModel.js')
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/timeline/TracingTimelineModel.js661
1 files changed, 661 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/timeline/TracingTimelineModel.js b/chromium/third_party/WebKit/Source/devtools/front_end/timeline/TracingTimelineModel.js
new file mode 100644
index 00000000000..482fa1364e9
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/timeline/TracingTimelineModel.js
@@ -0,0 +1,661 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @param {!WebInspector.TracingModel} tracingModel
+ * @constructor
+ * @extends {WebInspector.TimelineModel}
+ */
+WebInspector.TracingTimelineModel = function(tracingModel)
+{
+ WebInspector.TimelineModel.call(this, tracingModel.target());
+ this._tracingModel = tracingModel;
+ this._mainThreadEvents = [];
+ this._inspectedTargetEvents = [];
+
+ this.reset();
+}
+
+WebInspector.TracingTimelineModel.RecordType = {
+ Program: "Program",
+ EventDispatch: "EventDispatch",
+
+ GPUTask: "GPUTask",
+
+ RequestMainThreadFrame: "RequestMainThreadFrame",
+ BeginFrame: "BeginFrame",
+ BeginMainThreadFrame: "BeginMainThreadFrame",
+ ActivateLayerTree: "ActivateLayerTree",
+ DrawFrame: "DrawFrame",
+ ScheduleStyleRecalculation: "ScheduleStyleRecalculation",
+ RecalculateStyles: "RecalculateStyles",
+ InvalidateLayout: "InvalidateLayout",
+ Layout: "Layout",
+ UpdateLayer: "UpdateLayer",
+ PaintSetup: "PaintSetup",
+ Paint: "Paint",
+ PaintImage: "PaintImage",
+ Rasterize: "Rasterize",
+ RasterTask: "RasterTask",
+ ScrollLayer: "ScrollLayer",
+ CompositeLayers: "CompositeLayers",
+
+ ParseHTML: "ParseHTML",
+
+ TimerInstall: "TimerInstall",
+ TimerRemove: "TimerRemove",
+ TimerFire: "TimerFire",
+
+ XHRReadyStateChange: "XHRReadyStateChange",
+ XHRLoad: "XHRLoad",
+ EvaluateScript: "EvaluateScript",
+
+ MarkLoad: "MarkLoad",
+ MarkDOMContent: "MarkDOMContent",
+ MarkFirstPaint: "MarkFirstPaint",
+
+ TimeStamp: "TimeStamp",
+ ConsoleTime: "ConsoleTime",
+
+ ResourceSendRequest: "ResourceSendRequest",
+ ResourceReceiveResponse: "ResourceReceiveResponse",
+ ResourceReceivedData: "ResourceReceivedData",
+ ResourceFinish: "ResourceFinish",
+
+ FunctionCall: "FunctionCall",
+ GCEvent: "GCEvent",
+ JSFrame: "JSFrame",
+
+ UpdateCounters: "UpdateCounters",
+
+ RequestAnimationFrame: "RequestAnimationFrame",
+ CancelAnimationFrame: "CancelAnimationFrame",
+ FireAnimationFrame: "FireAnimationFrame",
+
+ WebSocketCreate : "WebSocketCreate",
+ WebSocketSendHandshakeRequest : "WebSocketSendHandshakeRequest",
+ WebSocketReceiveHandshakeResponse : "WebSocketReceiveHandshakeResponse",
+ WebSocketDestroy : "WebSocketDestroy",
+
+ EmbedderCallback : "EmbedderCallback",
+
+ CallStack: "CallStack",
+ SetLayerTreeId: "SetLayerTreeId",
+ TracingStartedInPage: "TracingStartedInPage",
+
+ DecodeImage: "Decode Image",
+ ResizeImage: "Resize Image",
+ DrawLazyPixelRef: "Draw LazyPixelRef",
+ DecodeLazyPixelRef: "Decode LazyPixelRef",
+
+ LazyPixelRef: "LazyPixelRef",
+ LayerTreeHostImplSnapshot: "cc::LayerTreeHostImpl",
+ PictureSnapshot: "cc::Picture"
+};
+
+WebInspector.TracingTimelineModel.defaultTracingCategoryFilter = "*,disabled-by-default-cc.debug,disabled-by-default-devtools.timeline,disabled-by-default-devtools.timeline.frame";
+
+WebInspector.TracingTimelineModel.prototype = {
+ /**
+ * @param {boolean} captureStacks
+ * @param {boolean} captureMemory
+ * @param {boolean} capturePictures
+ */
+ startRecording: function(captureStacks, captureMemory, capturePictures)
+ {
+ var categories;
+ if (WebInspector.experimentsSettings.timelineTracingMode.isEnabled()) {
+ categories = WebInspector.TracingTimelineModel.defaultTracingCategoryFilter;
+ } else {
+ var categoriesArray = ["disabled-by-default-devtools.timeline", "disabled-by-default-devtools.timeline.frame", "devtools"];
+ if (captureStacks)
+ categoriesArray.push("disabled-by-default-devtools.timeline.stack");
+ if (capturePictures)
+ categoriesArray.push("disabled-by-default-devtools.timeline.layers", "disabled-by-default-devtools.timeline.picture");
+ categories = categoriesArray.join(",");
+ }
+ this._startRecordingWithCategories(categories);
+ },
+
+ stopRecording: function()
+ {
+ this._tracingModel.stop(this._didStopRecordingTraceEvents.bind(this));
+ },
+
+ /**
+ * @param {string} sessionId
+ * @param {!Array.<!WebInspector.TracingModel.EventPayload>} events
+ */
+ setEventsForTest: function(sessionId, events)
+ {
+ this.reset();
+ this._didStartRecordingTraceEvents();
+ this._tracingModel.setEventsForTest(sessionId, events);
+ this._didStopRecordingTraceEvents();
+ },
+
+ /**
+ * @param {string} categories
+ */
+ _startRecordingWithCategories: function(categories)
+ {
+ this.reset();
+ this._tracingModel.start(categories, "", this._didStartRecordingTraceEvents.bind(this));
+ },
+
+ _didStartRecordingTraceEvents: function()
+ {
+ this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordingStarted);
+ },
+
+ _didStopRecordingTraceEvents: function()
+ {
+ var events = this._tracingModel.devtoolsMetadataEvents();
+ events.sort(WebInspector.TracingModel.Event.compareStartTime);
+
+ this._resetProcessingState();
+ for (var i = 0, length = events.length; i < length; i++) {
+ var event = events[i];
+ var process = event.thread.process();
+ var startTime = event.startTime;
+
+ var endTime = Infinity;
+ if (i + 1 < length)
+ endTime = events[i + 1].startTime;
+
+ process.sortedThreads().forEach(this._processThreadEvents.bind(this, startTime, endTime, event.thread));
+ }
+ this._resetProcessingState();
+
+ this._inspectedTargetEvents.sort(WebInspector.TracingModel.Event.compareStartTime);
+
+ this._buildTimelineRecords();
+ this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordingStopped);
+ },
+
+ /**
+ * @return {number}
+ */
+ minimumRecordTime: function()
+ {
+ return this._tracingModel.minimumRecordTime();
+ },
+
+ /**
+ * @return {number}
+ */
+ maximumRecordTime: function()
+ {
+ return this._tracingModel.maximumRecordTime();
+ },
+
+ /**
+ * @return {!Array.<!WebInspector.TracingModel.Event>}
+ */
+ inspectedTargetEvents: function()
+ {
+ return this._inspectedTargetEvents;
+ },
+
+ /**
+ * @return {!Array.<!WebInspector.TracingModel.Event>}
+ */
+ mainThreadEvents: function()
+ {
+ return this._mainThreadEvents;
+ },
+
+ reset: function()
+ {
+ this._mainThreadEvents = [];
+ this._inspectedTargetEvents = [];
+ WebInspector.TimelineModel.prototype.reset.call(this);
+ },
+
+ _buildTimelineRecords: function()
+ {
+ var recordStack = [];
+ var mainThreadEvents = this._mainThreadEvents;
+ for (var i = 0, size = mainThreadEvents.length; i < size; ++i) {
+ var event = mainThreadEvents[i];
+ while (recordStack.length) {
+ var top = recordStack.peekLast();
+ if (top._event.endTime >= event.startTime)
+ break;
+ recordStack.pop();
+ }
+ var parentRecord = recordStack.peekLast() || null;
+ var record = new WebInspector.TracingTimelineModel.TraceEventRecord(this, event, parentRecord);
+ if (WebInspector.TracingTimelineUIUtils.isEventDivider(record))
+ this._eventDividerRecords.push(record);
+ if (!recordStack.length)
+ this._addTopLevelRecord(record);
+ if (event.endTime)
+ recordStack.push(record);
+ }
+ },
+
+ /**
+ * @param {!WebInspector.TracingTimelineModel.TraceEventRecord} record
+ */
+ _addTopLevelRecord: function(record)
+ {
+ this._updateBoundaries(record);
+ this._records.push(record);
+ if (record.type() === WebInspector.TimelineModel.RecordType.Program)
+ this._mainThreadTasks.push(record);
+ if (record.type() === WebInspector.TimelineModel.RecordType.GPUTask)
+ this._gpuThreadTasks.push(record);
+ this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordAdded, record);
+ },
+
+ _resetProcessingState: function()
+ {
+ this._sendRequestEvents = {};
+ this._timerEvents = {};
+ this._requestAnimationFrameEvents = {};
+ this._layoutInvalidate = {};
+ this._lastScheduleStyleRecalculation = {};
+ this._webSocketCreateEvents = {};
+ this._paintImageEventByPixelRefId = {};
+ this._lastPaintForLayer = {};
+ this._lastRecalculateStylesEvent = null;
+ this._currentScriptEvent = null;
+ this._eventStack = [];
+ },
+
+ /**
+ * @param {number} startTime
+ * @param {?number} endTime
+ * @param {!WebInspector.TracingModel.Thread} mainThread
+ * @param {!WebInspector.TracingModel.Thread} thread
+ */
+ _processThreadEvents: function(startTime, endTime, mainThread, thread)
+ {
+ var events = thread.events();
+ var length = events.length;
+ var i = events.lowerBound(startTime, function (time, event) { return time - event.startTime });
+
+ this._eventStack = [];
+ for (; i < length; i++) {
+ var event = events[i];
+ if (endTime && event.startTime >= endTime)
+ break;
+ this._processEvent(event);
+ if (thread === mainThread)
+ this._mainThreadEvents.push(event);
+ this._inspectedTargetEvents.push(event);
+ }
+ },
+
+ /**
+ * @param {!WebInspector.TracingModel.Event} event
+ */
+ _processEvent: function(event)
+ {
+ var recordTypes = WebInspector.TracingTimelineModel.RecordType;
+
+ var eventStack = this._eventStack;
+ while (eventStack.length && eventStack.peekLast().endTime < event.startTime)
+ eventStack.pop();
+ var duration = event.duration;
+ if (duration) {
+ if (eventStack.length) {
+ var parent = eventStack.peekLast();
+ parent.selfTime -= duration;
+ }
+ event.selfTime = duration;
+ eventStack.push(event);
+ }
+
+ if (this._currentScriptEvent && event.startTime > this._currentScriptEvent.endTime)
+ this._currentScriptEvent = null;
+
+ switch (event.name) {
+ case recordTypes.CallStack:
+ var lastMainThreadEvent = this._mainThreadEvents.peekLast();
+ if (lastMainThreadEvent && event.args.stack && event.args.stack.length)
+ lastMainThreadEvent.stackTrace = event.args.stack;
+ break;
+
+ case recordTypes.ResourceSendRequest:
+ this._sendRequestEvents[event.args.data["requestId"]] = event;
+ event.imageURL = event.args.data["url"];
+ break;
+
+ case recordTypes.ResourceReceiveResponse:
+ case recordTypes.ResourceReceivedData:
+ case recordTypes.ResourceFinish:
+ event.initiator = this._sendRequestEvents[event.args.data["requestId"]];
+ if (event.initiator)
+ event.imageURL = event.initiator.imageURL;
+ break;
+
+ case recordTypes.TimerInstall:
+ this._timerEvents[event.args.data["timerId"]] = event;
+ break;
+
+ case recordTypes.TimerFire:
+ event.initiator = this._timerEvents[event.args.data["timerId"]];
+ break;
+
+ case recordTypes.RequestAnimationFrame:
+ this._requestAnimationFrameEvents[event.args.data["id"]] = event;
+ break;
+
+ case recordTypes.FireAnimationFrame:
+ event.initiator = this._requestAnimationFrameEvents[event.args.data["id"]];
+ break;
+
+ case recordTypes.ScheduleStyleRecalculation:
+ this._lastScheduleStyleRecalculation[event.args.frame] = event;
+ break;
+
+ case recordTypes.RecalculateStyles:
+ event.initiator = this._lastScheduleStyleRecalculation[event.args.frame];
+ this._lastRecalculateStylesEvent = event;
+ break;
+
+ case recordTypes.InvalidateLayout:
+ // Consider style recalculation as a reason for layout invalidation,
+ // but only if we had no earlier layout invalidation records.
+ var layoutInitator = event;
+ var frameId = event.args.frame;
+ if (!this._layoutInvalidate[frameId] && this._lastRecalculateStylesEvent && this._lastRecalculateStylesEvent.endTime > event.startTime)
+ layoutInitator = this._lastRecalculateStylesEvent.initiator;
+ this._layoutInvalidate[frameId] = layoutInitator;
+ break;
+
+ case recordTypes.Layout:
+ var frameId = event.args["beginData"]["frame"];
+ event.initiator = this._layoutInvalidate[frameId];
+ event.backendNodeId = event.args["endData"]["rootNode"];
+ event.highlightQuad = event.args["endData"]["root"];
+ this._layoutInvalidate[frameId] = null;
+ if (this._currentScriptEvent)
+ event.warning = WebInspector.UIString("Forced synchronous layout is a possible performance bottleneck.");
+ break;
+
+ case recordTypes.WebSocketCreate:
+ this._webSocketCreateEvents[event.args.data["identifier"]] = event;
+ break;
+
+ case recordTypes.WebSocketSendHandshakeRequest:
+ case recordTypes.WebSocketReceiveHandshakeResponse:
+ case recordTypes.WebSocketDestroy:
+ event.initiator = this._webSocketCreateEvents[event.args.data["identifier"]];
+ break;
+
+ case recordTypes.EvaluateScript:
+ case recordTypes.FunctionCall:
+ if (!this._currentScriptEvent)
+ this._currentScriptEvent = event;
+ break;
+
+ case recordTypes.SetLayerTreeId:
+ this._inspectedTargetLayerTreeId = event.args["layerTreeId"];
+ break;
+
+ case recordTypes.Paint:
+ event.highlightQuad = event.args["data"]["clip"];
+ event.backendNodeId = event.args["data"]["nodeId"];
+ var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLayer);
+ if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== this._inspectedTargetLayerTreeId)
+ break;
+ this._lastPaintForLayer[layerUpdateEvent.args["layerId"]] = event;
+ break;
+
+ case recordTypes.PictureSnapshot:
+ var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLayer);
+ if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== this._inspectedTargetLayerTreeId)
+ break;
+ var paintEvent = this._lastPaintForLayer[layerUpdateEvent.args["layerId"]];
+ if (!paintEvent)
+ break;
+ paintEvent.picture = event.args["snapshot"]["skp64"];
+ break;
+
+ case recordTypes.ScrollLayer:
+ event.backendNodeId = event.args["data"]["nodeId"];
+ break;
+
+ case recordTypes.PaintImage:
+ event.backendNodeId = event.args["data"]["nodeId"];
+ event.imageURL = event.args["data"]["url"];
+ break;
+
+ case recordTypes.DecodeImage:
+ case recordTypes.ResizeImage:
+ var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage);
+ if (!paintImageEvent) {
+ var decodeLazyPixelRefEvent = this._findAncestorEvent(recordTypes.DecodeLazyPixelRef);
+ paintImageEvent = decodeLazyPixelRefEvent && this._paintImageEventByPixelRefId[decodeLazyPixelRefEvent.args["LazyPixelRef"]];
+ }
+ if (!paintImageEvent)
+ break;
+ event.backendNodeId = paintImageEvent.backendNodeId;
+ event.imageURL = paintImageEvent.imageURL;
+ break;
+
+ case recordTypes.DrawLazyPixelRef:
+ var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage);
+ if (!paintImageEvent)
+ break;
+ this._paintImageEventByPixelRefId[event.args["LazyPixelRef"]] = paintImageEvent;
+ event.backendNodeId = paintImageEvent.backendNodeId;
+ event.imageURL = paintImageEvent.imageURL;
+ break;
+ }
+ },
+
+ /**
+ * @param {string} name
+ * @return {?WebInspector.TracingModel.Event}
+ */
+ _findAncestorEvent: function(name)
+ {
+ for (var i = this._eventStack.length - 1; i >= 0; --i) {
+ var event = this._eventStack[i];
+ if (event.name === name)
+ return event;
+ }
+ return null;
+ },
+
+ __proto__: WebInspector.TimelineModel.prototype
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.TimelineModel.Record}
+ * @param {!WebInspector.TimelineModel} model
+ * @param {!WebInspector.TracingModel.Event} traceEvent
+ * @param {?WebInspector.TracingTimelineModel.TraceEventRecord} parentRecord
+ */
+WebInspector.TracingTimelineModel.TraceEventRecord = function(model, traceEvent, parentRecord)
+{
+ this._model = model;
+ this._event = traceEvent;
+ traceEvent._timelineRecord = this;
+ if (parentRecord) {
+ this.parent = parentRecord;
+ parentRecord._children.push(this);
+ }
+ this._children = [];
+}
+
+WebInspector.TracingTimelineModel.TraceEventRecord.prototype = {
+ /**
+ * @return {?Array.<!ConsoleAgent.CallFrame>}
+ */
+ callSiteStackTrace: function()
+ {
+ var initiator = this._event.initiator;
+ return initiator ? initiator.stackTrace : null;
+ },
+
+ /**
+ * @return {?WebInspector.TimelineModel.Record}
+ */
+ initiator: function()
+ {
+ var initiator = this._event.initiator;
+ return initiator ? initiator._timelineRecord : null;
+ },
+
+ /**
+ * @return {!WebInspector.Target}
+ */
+ target: function()
+ {
+ return this._model.target();
+ },
+
+ /**
+ * @return {number}
+ */
+ selfTime: function()
+ {
+ return this._event.selfTime;
+ },
+
+ /**
+ * @return {!Array.<!WebInspector.TimelineModel.Record>}
+ */
+ children: function()
+ {
+ return this._children;
+ },
+
+ /**
+ * @return {!WebInspector.TimelineCategory}
+ */
+ category: function()
+ {
+ var style = WebInspector.TracingTimelineUIUtils.styleForTraceEvent(this._event.name);
+ return style.category;
+ },
+
+ /**
+ * @return {number}
+ */
+ startTime: function()
+ {
+ return this._event.startTime;
+ },
+
+ /**
+ * @return {string|undefined}
+ */
+ thread: function()
+ {
+ return "CPU";
+ },
+
+ /**
+ * @return {number}
+ */
+ endTime: function()
+ {
+ return this._event.endTime || this._event.startTime;
+ },
+
+ /**
+ * @param {number} endTime
+ */
+ setEndTime: function(endTime)
+ {
+ throw new Error("Unsupported operation setEndTime");
+ },
+
+ /**
+ * @return {!Object}
+ */
+ data: function()
+ {
+ return this._event.args.data;
+ },
+
+ /**
+ * @return {string}
+ */
+ type: function()
+ {
+ return this._event.name;
+ },
+
+ /**
+ * @return {string}
+ */
+ frameId: function()
+ {
+ switch (this._event.name) {
+ case WebInspector.TracingTimelineModel.RecordType.ScheduleStyleRecalculation:
+ case WebInspector.TracingTimelineModel.RecordType.RecalculateStyles:
+ case WebInspector.TracingTimelineModel.RecordType.InvalidateLayout:
+ return this._event.args["frameId"];
+ case WebInspector.TracingTimelineModel.RecordType.Layout:
+ return this._event.args["beginData"]["frameId"];
+ default:
+ var data = this._event.args.data;
+ return (data && data["frame"]) || "";
+ }
+ },
+
+ /**
+ * @return {?Array.<!ConsoleAgent.CallFrame>}
+ */
+ stackTrace: function()
+ {
+ return this._event.stackTrace;
+ },
+
+ /**
+ * @param {string} key
+ * @return {?Object}
+ */
+ getUserObject: function(key)
+ {
+ if (key === "TimelineUIUtils::preview-element")
+ return this._event.previewElement;
+ throw new Error("Unexpected key: " + key);
+ },
+
+ /**
+ * @param {string} key
+ * @param {?Object|undefined} value
+ */
+ setUserObject: function(key, value)
+ {
+ if (key !== "TimelineUIUtils::preview-element")
+ throw new Error("Unexpected key: " + key);
+ this._event.previewElement = /** @type {?Element} */ (value);
+ },
+
+ /**
+ * @return {!Object.<string, number>}
+ */
+ aggregatedStats: function()
+ {
+ return {};
+ },
+
+ /**
+ * @return {?Array.<string>}
+ */
+ warnings: function()
+ {
+ if (this._event.warning)
+ return [this._event.warning];
+ return null;
+ },
+
+ /**
+ * @return {!WebInspector.TracingModel.Event}
+ */
+ traceEvent: function()
+ {
+ return this._event;
+ }
+}