2 * Copyright 2014 The Chromium Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
9 * @extends {WebInspector.Object}
11 WebInspector.TracingModel = function()
13 WebInspector.Object.call(this);
16 InspectorBackend.registerTracingDispatcher(new WebInspector.TracingDispatcher(this));
19 WebInspector.TracingModel.Events = {
20 "BufferUsage": "BufferUsage"
36 WebInspector.TracingModel.EventPayload;
41 WebInspector.TracingModel.Phase = {
61 WebInspector.TracingModel.MetadataEvent = {
62 ProcessSortIndex: "process_sort_index",
63 ProcessName: "process_name",
64 ThreadSortIndex: "thread_sort_index",
65 ThreadName: "thread_name"
68 WebInspector.TracingModel.DevToolsMetadataEventCategory = "disabled-by-default-devtools.timeline";
70 WebInspector.TracingModel.FrameLifecycleEventCategory = "cc,devtools";
72 WebInspector.TracingModel.DevToolsMetadataEvent = {
73 TracingStartedInPage: "TracingStartedInPage",
74 SetLayerTreeId: "SetLayerTreeId"
77 WebInspector.TracingModel.TraceEventName = {
78 ActivateLayerTree: "ActivateLayerTree",
79 BeginFrame: "BeginFrame",
80 BeginMainThreadFrame: "BeginMainThreadFrame",
81 CompositeLayers: "CompositeLayers",
82 DrawFrame: "DrawFrame",
83 PaintSetup: "PaintSetup",
84 RasterTask: "RasterTask",
85 RequestMainThreadFrame: "RequestMainThreadFrame"
88 WebInspector.TracingModel.prototype = {
90 * @return {!Array.<!WebInspector.TracingModel.Event>}
92 inspectedTargetMainThreadEvents: function()
94 return this._inspectedTargetMainThreadEvents;
98 * @return {!Array.<!WebInspector.TracingModel.Event>}
100 frameLifecycleEvents: function()
103 * @param {!WebInspector.TracingModel.Event} a
104 * @param {!WebInspector.TracingModel.Event} b
106 function compareStartTime(a, b)
108 return a.startTime - b.startTime;
110 return this._frameLifecycleEvents.sort(compareStartTime);
114 * @param {string} categoryFilter
115 * @param {string} options
116 * @param {function(?string)=} callback
118 start: function(categoryFilter, options, callback)
121 var bufferUsageReportingIntervalMs = 500;
123 * @param {?string} error
124 * @param {string} sessionId
125 * @this {WebInspector.TracingModel}
127 function callbackWrapper(error, sessionId)
129 this._sessionId = sessionId;
133 TracingAgent.start(categoryFilter, options, bufferUsageReportingIntervalMs, callbackWrapper.bind(this));
138 * @param {function()} callback
140 stop: function(callback)
146 this._pendingStopCallback = callback;
153 sessionId: function()
155 return this._sessionId;
159 * @param {number} usage
161 _bufferUsage: function(usage)
163 this.dispatchEventToListeners(WebInspector.TracingModel.Events.BufferUsage, usage);
167 * @param {!Array.<!WebInspector.TracingModel.EventPayload>} events
169 _eventsCollected: function(events)
171 for (var i = 0; i < events.length; ++i)
172 this._addEvent(events[i]);
175 _tracingComplete: function()
177 this._active = false;
178 if (!this._pendingStopCallback)
180 this._pendingStopCallback();
181 this._pendingStopCallback = null;
186 this._processById = {};
187 this._minimumRecordTime = null;
188 this._maximumRecordTime = null;
189 this._sessionId = null;
190 this._inspectedTargetProcessId = null;
191 this._inspectedTargetMainThread = null;
192 this._inspectedTargetMainThreadEvents = [];
193 this._inspectedTargetLayerTreeHostId = 0;
194 this._frameLifecycleEvents = [];
198 * @param {!WebInspector.TracingModel.EventPayload} payload
200 _addEvent: function(payload)
202 var process = this._processById[payload.pid];
204 process = new WebInspector.TracingModel.Process(payload.pid);
205 this._processById[payload.pid] = process;
207 if (payload.ph === WebInspector.TracingModel.Phase.SnapshotObject) {
208 process.addObject(payload);
211 var thread = process.threadById(payload.tid);
212 if (payload.ph !== WebInspector.TracingModel.Phase.Metadata) {
213 var timestamp = payload.ts;
214 // We do allow records for unrelated threads to arrive out-of-order,
215 // so there's a chance we're getting records from the past.
216 if (timestamp && (!this._minimumRecordTime || timestamp < this._minimumRecordTime))
217 this._minimumRecordTime = timestamp;
218 if (!this._maximumRecordTime || timestamp > this._maximumRecordTime)
219 this._maximumRecordTime = timestamp;
220 if (payload.cat === WebInspector.TracingModel.DevToolsMetadataEventCategory)
221 this._processDevToolsMetadataEvent(payload);
222 var event = thread.addEvent(payload);
225 if (thread === this._inspectedTargetMainThread)
226 this._inspectedTargetMainThreadEvents.push(event);
227 if (payload.cat === WebInspector.TracingModel.FrameLifecycleEventCategory && payload.pid === this._inspectedTargetProcessId &&
228 payload.args && payload.args["layerTreeId"] === this._inspectedTargetLayerTreeId) {
229 this._frameLifecycleEvents.push(event);
233 switch (payload.name) {
234 case WebInspector.TracingModel.MetadataEvent.ProcessSortIndex:
235 process._setSortIndex(payload.args["sort_index"]);
237 case WebInspector.TracingModel.MetadataEvent.ProcessName:
238 process._setName(payload.args["name"]);
240 case WebInspector.TracingModel.MetadataEvent.ThreadSortIndex:
241 thread._setSortIndex(payload.args["sort_index"]);
243 case WebInspector.TracingModel.MetadataEvent.ThreadName:
244 thread._setName(payload.args["name"]);
250 * @param {!WebInspector.TracingModel.EventPayload} payload
252 _processDevToolsMetadataEvent: function(payload)
254 if (payload.args["sessionId"] !== this._sessionId)
256 if (payload.name === WebInspector.TracingModel.DevToolsMetadataEvent.TracingStartedInPage) {
257 var thread = this._processById[payload.pid].threadById(payload.tid)
258 this._inspectedTargetProcessId = payload.pid;
259 this._inspectedTargetMainThread = thread;
260 this._inspectedTargetMainThreadEvents = this._inspectedTargetMainThreadEvents.concat(thread.events());
261 } else if (payload.name === WebInspector.TracingModel.DevToolsMetadataEvent.SetLayerTreeId) {
262 this._inspectedTargetLayerTreeId = payload.args["layerTreeId"];
269 minimumRecordTime: function()
271 return this._minimumRecordTime;
277 maximumRecordTime: function()
279 return this._maximumRecordTime;
283 * @return {!Array.<!WebInspector.TracingModel.Process>}
285 sortedProcesses: function()
287 return WebInspector.TracingModel.NamedObject._sort(Object.values(this._processById));
290 __proto__: WebInspector.Object.prototype
295 * @param {!WebInspector.TracingModel.EventPayload} payload
296 * @param {number} level
298 WebInspector.TracingModel.Event = function(payload, level)
300 this.name = payload.name;
301 this.category = payload.cat;
302 this.startTime = payload.ts;
303 this.args = payload.args;
304 this.phase = payload.ph;
308 WebInspector.TracingModel.Event.prototype = {
310 * @param {number} duration
312 _setDuration: function(duration)
314 this.endTime = this.startTime + duration;
315 this.duration = duration;
319 * @param {!WebInspector.TracingModel.EventPayload} payload
321 _complete: function(payload)
323 if (this.name !== payload.name) {
324 console.assert(false, "Open/close event mismatch: " + this.name + " vs. " + payload.name);
327 var duration = payload.ts - this.startTime;
329 console.assert(false, "Event out of order: " + this.name);
332 this._setDuration(duration);
339 WebInspector.TracingModel.NamedObject = function()
343 WebInspector.TracingModel.NamedObject.prototype =
346 * @param {string} name
348 _setName: function(name)
362 * @param {number} sortIndex
364 _setSortIndex: function(sortIndex)
366 this._sortIndex = sortIndex;
371 * @param {!Array.<!WebInspector.TracingModel.NamedObject>} array
373 WebInspector.TracingModel.NamedObject._sort = function(array)
376 * @param {!WebInspector.TracingModel.NamedObject} a
377 * @param {!WebInspector.TracingModel.NamedObject} b
379 function comparator(a, b)
381 return a._sortIndex !== b._sortIndex ? a._sortIndex - b._sortIndex : a.name().localeCompare(b.name());
383 return array.sort(comparator);
388 * @extends {WebInspector.TracingModel.NamedObject}
391 WebInspector.TracingModel.Process = function(id)
393 WebInspector.TracingModel.NamedObject.call(this);
394 this._setName("Process " + id);
399 WebInspector.TracingModel.Process.prototype = {
402 * @return {!WebInspector.TracingModel.Thread}
404 threadById: function(id)
406 var thread = this._threads[id];
408 thread = new WebInspector.TracingModel.Thread(id);
409 this._threads[id] = thread;
415 * @param {!WebInspector.TracingModel.EventPayload} event
417 addObject: function(event)
419 this.objectsByName(event.name).push(new WebInspector.TracingModel.Event(event, 0));
423 * @param {string} name
424 * @return {!Array.<!WebInspector.TracingModel.Event>}
426 objectsByName: function(name)
428 var objects = this._objects[name];
431 this._objects[name] = objects;
437 * @return {!Array.<string>}
439 sortedObjectNames: function()
441 return Object.keys(this._objects).sort();
445 * @return {!Array.<!WebInspector.TracingModel.Thread>}
447 sortedThreads: function()
449 return WebInspector.TracingModel.NamedObject._sort(Object.values(this._threads));
452 __proto__: WebInspector.TracingModel.NamedObject.prototype
457 * @extends {WebInspector.TracingModel.NamedObject}
460 WebInspector.TracingModel.Thread = function(id)
462 WebInspector.TracingModel.NamedObject.call(this);
463 this._setName("Thread " + id);
466 this._maxStackDepth = 0;
469 WebInspector.TracingModel.Thread.prototype = {
471 * @param {!WebInspector.TracingModel.EventPayload} payload
472 * @return {?WebInspector.TracingModel.Event} event
474 addEvent: function(payload)
476 for (var top = this._stack.peekLast(); top && top.endTime && top.endTime <= payload.ts;) {
478 top = this._stack.peekLast();
480 if (payload.ph === WebInspector.TracingModel.Phase.End) {
481 var openEvent = this._stack.pop();
482 // Quietly ignore unbalanced close events, they're legit (we could have missed start one).
484 openEvent._complete(payload);
488 var event = new WebInspector.TracingModel.Event(payload, this._stack.length);
489 if (payload.ph === WebInspector.TracingModel.Phase.Begin || payload.ph === WebInspector.TracingModel.Phase.Complete) {
490 if (payload.ph === WebInspector.TracingModel.Phase.Complete)
491 event._setDuration(payload.dur);
492 this._stack.push(event);
493 if (this._maxStackDepth < this._stack.length)
494 this._maxStackDepth = this._stack.length;
496 if (this._events.length && this._events.peekLast().startTime > event.startTime)
497 console.assert(false, "Event is our of order: " + event.name);
498 this._events.push(event);
503 * @return {!Array.<!WebInspector.TracingModel.Event>}
513 maxStackDepth: function()
515 // Reserve one for non-container events.
516 return this._maxStackDepth + 1;
519 __proto__: WebInspector.TracingModel.NamedObject.prototype
525 * @implements {TracingAgent.Dispatcher}
526 * @param {!WebInspector.TracingModel} tracingModel
528 WebInspector.TracingDispatcher = function(tracingModel)
530 this._tracingModel = tracingModel;
533 WebInspector.TracingDispatcher.prototype = {
535 * @param {number} usage
537 bufferUsage: function(usage)
539 this._tracingModel._bufferUsage(usage);
543 * @param {!Array.<!WebInspector.TracingModel.EventPayload>} data
545 dataCollected: function(data)
547 this._tracingModel._eventsCollected(data);
550 tracingComplete: function()
552 this._tracingModel._tracingComplete();