/**
* @constructor
+ * @param {!WebInspector.TracingManager} tracingManager
* @param {!WebInspector.TracingModel} tracingModel
* @param {!WebInspector.TimelineModel.Filter} recordFilter
* @extends {WebInspector.TimelineModel}
*/
-WebInspector.TracingTimelineModel = function(tracingModel, recordFilter)
+WebInspector.TracingTimelineModel = function(tracingManager, tracingModel, recordFilter)
{
WebInspector.TimelineModel.call(this);
+
+ this._tracingManager = tracingManager;
this._tracingModel = tracingModel;
- this._inspectedTargetEvents = [];
this._recordFilter = recordFilter;
- this._tracingModel.addEventListener(WebInspector.TracingModel.Events.TracingStarted, this._onTracingStarted, this);
- this._tracingModel.addEventListener(WebInspector.TracingModel.Events.TracingComplete, this._onTracingComplete, this);
+ this._tracingManager.addEventListener(WebInspector.TracingManager.Events.TracingStarted, this._onTracingStarted, this);
+ this._tracingManager.addEventListener(WebInspector.TracingManager.Events.EventsCollected, this._onEventsCollected, this);
+ this._tracingManager.addEventListener(WebInspector.TracingManager.Events.TracingComplete, this._onTracingComplete, this);
this.reset();
}
CallStack: "CallStack",
SetLayerTreeId: "SetLayerTreeId",
TracingStartedInPage: "TracingStartedInPage",
- TracingStartedInWorker: "TracingStartedInWorker",
+ TracingSessionIdForWorker: "TracingSessionIdForWorker",
DecodeImage: "Decode Image",
ResizeImage: "Resize Image",
this.name = name;
/** @type {!Array.<!WebInspector.TracingModel.Event>} */
this.events = [];
+ /** @type {!Array.<!Array.<!WebInspector.TracingModel.Event>>} */
+ this.asyncEvents = [];
}
WebInspector.TracingTimelineModel.prototype = {
{
return "disabled-by-default-" + category;
}
- var categoriesArray = ["-*", disabledByDefault("devtools.timeline"), disabledByDefault("devtools.timeline.frame")];
+ var categoriesArray = [
+ "-*",
+ disabledByDefault("devtools.timeline"),
+ disabledByDefault("devtools.timeline.frame"),
+ WebInspector.TracingModel.ConsoleEventCategory
+ ];
if (captureStacks) {
categoriesArray.push(disabledByDefault("devtools.timeline.stack"));
- if (WebInspector.experimentsSettings.timelineJSCPUProfile.isEnabled()) {
+ if (Runtime.experiments.isEnabled("timelineJSCPUProfile")) {
this._jsProfilerStarted = true;
this._currentTarget = WebInspector.context.flavor(WebInspector.Target);
this._configureCpuProfilerSamplingInterval();
this._currentTarget.profilerAgent().stop(this._stopCallbackBarrier.createCallback(this._didStopRecordingJSSamples.bind(this)));
this._jsProfilerStarted = false;
}
- this._tracingModel.stop();
+ this._tracingManager.stop();
},
/**
- * @param {string} sessionId
- * @param {!Array.<!WebInspector.TracingModel.EventPayload>} events
+ * @param {!Array.<!WebInspector.TracingManager.EventPayload>} events
*/
- setEventsForTest: function(sessionId, events)
+ setEventsForTest: function(events)
{
this._onTracingStarted();
- this._tracingModel.setEventsForTest(sessionId, events);
+ this._tracingModel.addEvents(events);
this._onTracingComplete();
},
*/
_startRecordingWithCategories: function(categories)
{
- this.reset();
- this._tracingModel.start(categories, "");
+ this._tracingManager.start(categories, "");
},
_onTracingStarted: function()
{
this.reset();
+ this._tracingModel.reset();
this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordingStarted);
},
+ /**
+ * @param {!WebInspector.Event} event
+ */
+ _onEventsCollected: function(event)
+ {
+ var traceEvents = /** @type {!Array.<!WebInspector.TracingManager.EventPayload>} */ (event.data);
+ this._tracingModel.addEvents(traceEvents);
+ },
+
_onTracingComplete: function()
{
+ this._tracingModel.tracingComplete();
if (this._stopCallbackBarrier)
this._stopCallbackBarrier.callWhenDone(this._didStopRecordingTraceEvents.bind(this));
else
this._stopCallbackBarrier = null;
var events = this._tracingModel.devtoolsPageMetadataEvents();
var workerMetadataEvents = this._tracingModel.devtoolsWorkerMetadataEvents();
- events.sort(WebInspector.TracingModel.Event.compareStartTime);
this._resetProcessingState();
for (var i = 0, length = events.length; i < length; i++) {
var threads = process.sortedThreads();
for (var j = 0; j < threads.length; j++) {
var thread = threads[j];
- if (thread.name() === "WebCore: Worker" && !workerMetadataEvents.some(function(e) { return e.thread === thread; }))
+ if (thread.name() === "WebCore: Worker" && !workerMetadataEvents.some(function(e) { return e.args["data"]["workerThreadId"] === thread.id(); }))
continue;
this._processThreadEvents(startTime, endTime, event.thread, thread);
}
},
/**
+ * @return {!Array.<!Array.<!WebInspector.TracingModel.Event>>}
+ */
+ mainThreadAsyncEvents: function()
+ {
+ return this._mainThreadAsyncEvents;
+ },
+
+ /**
* @return {!Array.<!WebInspector.TracingTimelineModel.VirtualThread>}
*/
virtualThreads: function()
writeToStream: function(stream)
{
var saver = new WebInspector.TracingTimelineSaver(stream);
- var events = this._tracingModel.rawEvents();
- saver.save(events);
+ this._tracingModel.writeToStream(stream, saver);
},
reset: function()
{
this._virtualThreads = [];
this._mainThreadEvents = [];
+ this._mainThreadAsyncEvents = [];
this._inspectedTargetEvents = [];
WebInspector.TimelineModel.prototype.reset.call(this);
},
{
var recordStack = [];
var mainThreadEvents = this.mainThreadEvents();
+
+ /**
+ * @param {!WebInspector.TracingTimelineModel.TraceEventRecord} record
+ */
+ function copyChildrenToParent(record)
+ {
+ var parent = record.parent;
+ var parentChildren = parent.children();
+ var children = record.children();
+ for (var j = 0; j < children.length; ++j)
+ children[j].parent = parent;
+ parentChildren.splice.apply(parentChildren, [parentChildren.indexOf(record), 1].concat(children));
+ }
+
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)
+ // When we've got a not-yet-complete async event at the top of the stack,
+ // see if we can close it by a matching end event. If this doesn't happen
+ // before end of top-level event (presumably, a "Program"), pretend the
+ // async event never happened.
+ if (!top._event.endTime) {
+ if (event.phase !== WebInspector.TracingModel.Phase.AsyncEnd && recordStack[0]._event.endTime >= event.startTime)
+ break;
+ if (event.phase === WebInspector.TracingModel.Phase.AsyncEnd) {
+ if (top._event.name === event.name) {
+ top.setEndTime(event.startTime);
+ recordStack.pop();
+ }
+ break;
+ }
+ // Delete incomplete async record from parent and adopt its children.
+ recordStack.pop();
+ copyChildrenToParent(top);
+ continue;
+ } else if (top._event.endTime >= event.startTime) {
break;
+ }
recordStack.pop();
if (!recordStack.length)
this._addTopLevelRecord(top);
}
+ if (event.phase === WebInspector.TracingModel.Phase.AsyncEnd)
+ continue;
var record = new WebInspector.TracingTimelineModel.TraceEventRecord(this, event);
if (WebInspector.TracingTimelineUIUtils.isMarkerEvent(event))
this._eventDividerRecords.push(record);
var parentRecord = recordStack.peekLast();
if (parentRecord)
parentRecord._addChild(record);
- if (event.endTime)
+ if (event.endTime || (event.phase === WebInspector.TracingModel.Phase.AsyncBegin && parentRecord))
recordStack.push(record);
}
+
+ // Close all remaining incomplete async events.
+ while (recordStack.length > 1) {
+ var top = recordStack.pop();
+ if (!top._event.endTime) {
+ // Delete incomplete async record from parent and adopt its children.
+ copyChildrenToParent(top);
+ }
+ }
+
if (recordStack.length)
this._addTopLevelRecord(recordStack[0]);
},
var threadEvents;
if (thread === mainThread) {
threadEvents = this._mainThreadEvents;
+ this._mainThreadAsyncEvents = this._mainThreadAsyncEvents.concat(thread.asyncEvents());
} else {
var virtualThread = new WebInspector.TracingTimelineModel.VirtualThread(thread.name());
threadEvents = virtualThread.events;
+ virtualThread.asyncEvents = virtualThread.asyncEvents.concat(thread.asyncEvents());
this._virtualThreads.push(virtualThread);
}
if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== this._inspectedTargetLayerTreeId)
break;
var paintEvent = this._lastPaintForLayer[layerUpdateEvent.args["layerId"]];
- if (!paintEvent || !event.args["snapshot"] || !event.args["snapshot"]["params"])
- break;
- paintEvent.picture = event.args["snapshot"]["skp64"];
- paintEvent.layerRect = event.args["snapshot"]["params"]["layer_rect"];
+ if (paintEvent)
+ paintEvent.picture = event;
break;
case recordTypes.ScrollLayer:
*/
accept: function(event)
{
- return !!this._eventNames[event.name];
+ return event.category === WebInspector.TracingModel.ConsoleEventCategory || !!this._eventNames[event.name];
},
__proto__: WebInspector.TracingTimelineModel.EventNameFilter.prototype
}
*/
endTime: function()
{
- return this._event.endTime || this._event.startTime;
+ return this._endTime || this._event.endTime || this._event.startTime;
},
/**
*/
setEndTime: function(endTime)
{
- throw new Error("Unsupported operation setEndTime");
+ this._endTime = endTime;
},
/**
*/
type: function()
{
+ if (this._event.category === WebInspector.TracingModel.ConsoleEventCategory)
+ return WebInspector.TracingTimelineModel.RecordType.ConsoleTime;
return this._event.name;
},
return;
if (this._firstChunk) {
- this._model.reset();
+ this._model._onTracingStarted();
} else {
var commaIndex = json.indexOf(",");
if (commaIndex !== -1)
var items;
try {
- items = /** @type {!Array.<!WebInspector.TracingModel.EventPayload>} */ (JSON.parse(json));
+ items = /** @type {!Array.<!WebInspector.TracingManager.EventPayload>} */ (JSON.parse(json));
} catch (e) {
this._reportErrorAndCancelLoading("Malformed timeline data: " + e);
return;
_reportErrorAndCancelLoading: function(messsage)
{
WebInspector.console.error(messsage);
+ this._model._onTracingComplete();
this._model.reset();
this._reader.cancel();
this._progress.done();
close: function()
{
this._loader.finish();
+ this._model._onTracingComplete();
}
}
/**
* @constructor
* @param {!WebInspector.OutputStream} stream
+ * @implements {WebInspector.OutputStreamDelegate}
*/
WebInspector.TracingTimelineSaver = function(stream)
{
}
WebInspector.TracingTimelineSaver.prototype = {
- /**
- * @param {!Array.<*>} payloads
- */
- save: function(payloads)
+ onTransferStarted: function()
{
- this._payloads = payloads;
- this._recordIndex = 0;
- this._writeNextChunk(this._stream);
+ this._stream.write("[");
},
- _writeNextChunk: function(stream)
+ onTransferFinished: function()
{
- const separator = ",\n";
- var data = [];
- var length = 0;
-
- if (this._recordIndex === 0)
- data.push("[");
- while (this._recordIndex < this._payloads.length) {
- var item = JSON.stringify(this._payloads[this._recordIndex]);
- var itemLength = item.length + separator.length;
- if (length + itemLength > WebInspector.TimelineModelImpl.TransferChunkLengthBytes)
- break;
- length += itemLength;
- if (this._recordIndex > 0)
- data.push(separator);
- data.push(item);
- ++this._recordIndex;
- }
- if (this._recordIndex === this._payloads.length)
- data.push("]");
- stream.write(data.join(""), this._didWriteNextChunk.bind(this, stream));
+ this._stream.write("]");
},
- _didWriteNextChunk: function(stream)
- {
- if (this._recordIndex === this._payloads.length)
- stream.close();
- else
- this._writeNextChunk(stream);
- }
+ /**
+ * @param {!WebInspector.ChunkedReader} reader
+ */
+ onChunkTransferred: function(reader) { },
+
+ /**
+ * @param {!WebInspector.ChunkedReader} reader
+ * @param {!Event} event
+ */
+ onError: function(reader, event) { },
}