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.TargetAwareObject}
11 WebInspector.TracingModel = function(target)
13 WebInspector.TargetAwareObject.call(this, target);
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",
76 WebInspector.TracingModel.prototype = {
78 * @return {!Array.<!WebInspector.TracingModel.Event>}
80 devtoolsMetadataEvents: function()
82 return this._devtoolsMetadataEvents;
86 * @param {string} categoryFilter
87 * @param {string} options
88 * @param {function(?string)=} callback
90 start: function(categoryFilter, options, callback)
93 var bufferUsageReportingIntervalMs = 500;
95 * @param {?string} error
96 * @param {string} sessionId
97 * @this {WebInspector.TracingModel}
99 function callbackWrapper(error, sessionId)
101 this._sessionId = sessionId;
105 TracingAgent.start(categoryFilter, options, bufferUsageReportingIntervalMs, callbackWrapper.bind(this));
110 * @param {function()} callback
112 stop: function(callback)
118 this._pendingStopCallback = callback;
125 sessionId: function()
127 return this._sessionId;
131 * @param {string} sessionId
132 * @param {!Array.<!WebInspector.TracingModel.EventPayload>} events
134 setEventsForTest: function(sessionId, events)
137 this._sessionId = sessionId;
138 this._eventsCollected(events);
139 this._tracingComplete();
143 * @param {number} usage
145 _bufferUsage: function(usage)
147 this.dispatchEventToListeners(WebInspector.TracingModel.Events.BufferUsage, usage);
151 * @param {!Array.<!WebInspector.TracingModel.EventPayload>} events
153 _eventsCollected: function(events)
155 for (var i = 0; i < events.length; ++i)
156 this._addEvent(events[i]);
159 _tracingComplete: function()
161 this._active = false;
162 if (!this._pendingStopCallback)
164 this._pendingStopCallback();
165 this._pendingStopCallback = null;
170 this._processById = {};
171 this._minimumRecordTime = 0;
172 this._maximumRecordTime = 0;
173 this._sessionId = null;
174 this._devtoolsMetadataEvents = [];
178 * @param {!WebInspector.TracingModel.EventPayload} payload
180 _addEvent: function(payload)
182 var process = this._processById[payload.pid];
184 process = new WebInspector.TracingModel.Process(payload.pid);
185 this._processById[payload.pid] = process;
187 var thread = process.threadById(payload.tid);
188 if (payload.ph !== WebInspector.TracingModel.Phase.Metadata) {
189 var timestamp = payload.ts / 1000;
190 // We do allow records for unrelated threads to arrive out-of-order,
191 // so there's a chance we're getting records from the past.
192 if (timestamp && (!this._minimumRecordTime || timestamp < this._minimumRecordTime))
193 this._minimumRecordTime = timestamp;
194 if (!this._maximumRecordTime || timestamp > this._maximumRecordTime)
195 this._maximumRecordTime = timestamp;
196 var event = thread.addEvent(payload);
197 if (payload.ph === WebInspector.TracingModel.Phase.SnapshotObject)
198 process.addObject(event);
199 if (event && event.name === WebInspector.TracingModel.DevToolsMetadataEvent.TracingStartedInPage &&
200 event.category === WebInspector.TracingModel.DevToolsMetadataEventCategory &&
201 event.args["sessionId"] === this._sessionId)
202 this._devtoolsMetadataEvents.push(event);
205 switch (payload.name) {
206 case WebInspector.TracingModel.MetadataEvent.ProcessSortIndex:
207 process._setSortIndex(payload.args["sort_index"]);
209 case WebInspector.TracingModel.MetadataEvent.ProcessName:
210 process._setName(payload.args["name"]);
212 case WebInspector.TracingModel.MetadataEvent.ThreadSortIndex:
213 thread._setSortIndex(payload.args["sort_index"]);
215 case WebInspector.TracingModel.MetadataEvent.ThreadName:
216 thread._setName(payload.args["name"]);
224 minimumRecordTime: function()
226 return this._minimumRecordTime;
232 maximumRecordTime: function()
234 return this._maximumRecordTime;
238 * @return {!Array.<!WebInspector.TracingModel.Process>}
240 sortedProcesses: function()
242 return WebInspector.TracingModel.NamedObject._sort(Object.values(this._processById));
245 __proto__: WebInspector.TargetAwareObject.prototype
250 * @param {!WebInspector.TracingModel.EventPayload} payload
251 * @param {number} level
252 * @param {?WebInspector.TracingModel.Thread} thread
254 WebInspector.TracingModel.Event = function(payload, level, thread)
256 this.name = payload.name;
257 this.category = payload.cat;
258 this.startTime = payload.ts / 1000;
259 this.args = payload.args;
260 this.phase = payload.ph;
264 this._setEndTime((payload.ts + payload.dur) / 1000);
267 this.id = payload.id;
269 this.thread = thread;
271 /** @type {?string} */
273 /** @type {?WebInspector.TracingModel.Event} */
274 this.initiator = null;
275 /** @type {?Array.<!ConsoleAgent.CallFrame>} */
276 this.stackTrace = null;
277 /** @type {?Element} */
278 this.previewElement = null;
279 /** @type {?string} */
280 this.imageURL = null;
281 /** @type {number} */
282 this.backendNodeId = 0;
284 /** @type {number} */
288 WebInspector.TracingModel.Event.prototype = {
290 * @param {number} endTime
292 _setEndTime: function(endTime)
294 if (endTime < this.startTime) {
295 console.assert(false, "Event out of order: " + this.name);
298 this.endTime = endTime;
299 this.duration = endTime - this.startTime;
303 * @param {!WebInspector.TracingModel.EventPayload} payload
305 _complete: function(payload)
307 if (this.name !== payload.name) {
308 console.assert(false, "Open/close event mismatch: " + this.name + " vs. " + payload.name);
312 for (var name in payload.args) {
313 if (name in this.args)
314 console.error("Same argument name (" + name + ") is used for begin and end phases of " + this.name);
315 this.args[name] = payload.args[name];
318 this._setEndTime(payload.ts / 1000);
323 * @param {!WebInspector.TracingModel.Event} a
324 * @param {!WebInspector.TracingModel.Event} b
327 WebInspector.TracingModel.Event.compareStartTime = function (a, b)
329 return a.startTime - b.startTime;
335 WebInspector.TracingModel.NamedObject = function()
339 WebInspector.TracingModel.NamedObject.prototype =
342 * @param {string} name
344 _setName: function(name)
358 * @param {number} sortIndex
360 _setSortIndex: function(sortIndex)
362 this._sortIndex = sortIndex;
367 * @param {!Array.<!WebInspector.TracingModel.NamedObject>} array
369 WebInspector.TracingModel.NamedObject._sort = function(array)
372 * @param {!WebInspector.TracingModel.NamedObject} a
373 * @param {!WebInspector.TracingModel.NamedObject} b
375 function comparator(a, b)
377 return a._sortIndex !== b._sortIndex ? a._sortIndex - b._sortIndex : a.name().localeCompare(b.name());
379 return array.sort(comparator);
384 * @extends {WebInspector.TracingModel.NamedObject}
387 WebInspector.TracingModel.Process = function(id)
389 WebInspector.TracingModel.NamedObject.call(this);
390 this._setName("Process " + id);
395 WebInspector.TracingModel.Process.prototype = {
398 * @return {!WebInspector.TracingModel.Thread}
400 threadById: function(id)
402 var thread = this._threads[id];
404 thread = new WebInspector.TracingModel.Thread(this, id);
405 this._threads[id] = thread;
411 * @param {!WebInspector.TracingModel.Event} event
413 addObject: function(event)
415 this.objectsByName(event.name).push(event);
419 * @param {string} name
420 * @return {!Array.<!WebInspector.TracingModel.Event>}
422 objectsByName: function(name)
424 var objects = this._objects[name];
427 this._objects[name] = objects;
433 * @return {!Array.<string>}
435 sortedObjectNames: function()
437 return Object.keys(this._objects).sort();
441 * @return {!Array.<!WebInspector.TracingModel.Thread>}
443 sortedThreads: function()
445 return WebInspector.TracingModel.NamedObject._sort(Object.values(this._threads));
448 __proto__: WebInspector.TracingModel.NamedObject.prototype
453 * @extends {WebInspector.TracingModel.NamedObject}
454 * @param {!WebInspector.TracingModel.Process} process
457 WebInspector.TracingModel.Thread = function(process, id)
459 WebInspector.TracingModel.NamedObject.call(this);
460 this._process = process;
461 this._setName("Thread " + id);
464 this._maxStackDepth = 0;
467 WebInspector.TracingModel.Thread.prototype = {
469 * @param {!WebInspector.TracingModel.EventPayload} payload
470 * @return {?WebInspector.TracingModel.Event} event
472 addEvent: function(payload)
474 for (var top = this._stack.peekLast(); top && top.endTime && top.endTime <= payload.ts / 1000;) {
476 top = this._stack.peekLast();
478 if (payload.ph === WebInspector.TracingModel.Phase.End) {
479 var openEvent = this._stack.pop();
480 // Quietly ignore unbalanced close events, they're legit (we could have missed start one).
482 openEvent._complete(payload);
486 var event = new WebInspector.TracingModel.Event(payload, this._stack.length, this);
487 if (payload.ph === WebInspector.TracingModel.Phase.Begin || payload.ph === WebInspector.TracingModel.Phase.Complete) {
488 this._stack.push(event);
489 if (this._maxStackDepth < this._stack.length)
490 this._maxStackDepth = this._stack.length;
492 if (this._events.length && this._events.peekLast().startTime > event.startTime)
493 console.assert(false, "Event is our of order: " + event.name);
494 this._events.push(event);
499 * @return {!WebInspector.TracingModel.Process}
503 return this._process;
507 * @return {!Array.<!WebInspector.TracingModel.Event>}
517 maxStackDepth: function()
519 // Reserve one for non-container events.
520 return this._maxStackDepth + 1;
523 __proto__: WebInspector.TracingModel.NamedObject.prototype
529 * @implements {TracingAgent.Dispatcher}
530 * @param {!WebInspector.TracingModel} tracingModel
532 WebInspector.TracingDispatcher = function(tracingModel)
534 this._tracingModel = tracingModel;
537 WebInspector.TracingDispatcher.prototype = {
539 * @param {number} usage
541 bufferUsage: function(usage)
543 this._tracingModel._bufferUsage(usage);
547 * @param {!Array.<!WebInspector.TracingModel.EventPayload>} data
549 dataCollected: function(data)
551 this._tracingModel._eventsCollected(data);
554 tracingComplete: function()
556 this._tracingModel._tracingComplete();