Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / sdk / TracingModel.js
1 /*
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.
5  */
6
7 /**
8  * @constructor
9  * @extends {WebInspector.Object}
10  */
11 WebInspector.TracingModel = function()
12 {
13     WebInspector.Object.call(this);
14     this.reset();
15     this._active = false;
16     InspectorBackend.registerTracingDispatcher(new WebInspector.TracingDispatcher(this));
17 }
18
19 WebInspector.TracingModel.Events = {
20     "BufferUsage": "BufferUsage"
21 }
22
23 /** @typedef {!{
24         cat: string,
25         pid: number,
26         tid: number,
27         ts: number,
28         ph: string,
29         name: string,
30         args: !Object,
31         dur: number,
32         id: number,
33         s: string
34     }}
35  */
36 WebInspector.TracingModel.EventPayload;
37
38 /**
39  * @enum {string}
40  */
41 WebInspector.TracingModel.Phase = {
42     Begin: "B",
43     End: "E",
44     Complete: "X",
45     Instant: "i",
46     AsyncBegin: "S",
47     AsyncStepInto: "T",
48     AsyncStepPast: "p",
49     AsyncEnd: "F",
50     FlowBegin: "s",
51     FlowStep: "t",
52     FlowEnd: "f",
53     Metadata: "M",
54     Counter: "C",
55     Sample: "P",
56     CreateObject: "N",
57     SnapshotObject: "O",
58     DeleteObject: "D"
59 };
60
61 WebInspector.TracingModel.MetadataEvent = {
62     ProcessSortIndex: "process_sort_index",
63     ProcessName: "process_name",
64     ThreadSortIndex: "thread_sort_index",
65     ThreadName: "thread_name"
66 }
67
68 WebInspector.TracingModel.DevToolsMetadataEventCategory = "disabled-by-default-devtools.timeline";
69
70 WebInspector.TracingModel.FrameLifecycleEventCategory = "cc,devtools";
71
72 WebInspector.TracingModel.DevToolsMetadataEvent = {
73     TracingStartedInPage: "TracingStartedInPage",
74     SetLayerTreeId: "SetLayerTreeId"
75 };
76
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"
86 };
87
88 WebInspector.TracingModel.prototype = {
89     /**
90      * @return {!Array.<!WebInspector.TracingModel.Event>}
91      */
92     inspectedTargetMainThreadEvents: function()
93     {
94         return this._inspectedTargetMainThreadEvents;
95     },
96
97     /**
98      * @return {!Array.<!WebInspector.TracingModel.Event>}
99      */
100     frameLifecycleEvents: function()
101     {
102         /**
103          * @param {!WebInspector.TracingModel.Event} a
104          * @param {!WebInspector.TracingModel.Event} b
105          */
106         function compareStartTime(a, b)
107         {
108             return a.startTime - b.startTime;
109         }
110         return this._frameLifecycleEvents.sort(compareStartTime);
111     },
112
113     /**
114      * @param {string} categoryFilter
115      * @param {string} options
116      * @param {function(?string)=} callback
117      */
118     start: function(categoryFilter, options, callback)
119     {
120         this.reset();
121         var bufferUsageReportingIntervalMs = 500;
122         /**
123          * @param {?string} error
124          * @param {string} sessionId
125          * @this {WebInspector.TracingModel}
126          */
127         function callbackWrapper(error, sessionId)
128         {
129             this._sessionId = sessionId;
130             if (callback)
131                 callback(error);
132         }
133         TracingAgent.start(categoryFilter, options, bufferUsageReportingIntervalMs, callbackWrapper.bind(this));
134         this._active = true;
135     },
136
137     /**
138      * @param {function()} callback
139      */
140     stop: function(callback)
141     {
142         if (!this._active) {
143             callback();
144             return;
145         }
146         this._pendingStopCallback = callback;
147         TracingAgent.end();
148     },
149
150     /**
151      * @return {?string}
152      */
153     sessionId: function()
154     {
155         return this._sessionId;
156     },
157
158     /**
159      * @param {number} usage
160      */
161     _bufferUsage: function(usage)
162     {
163         this.dispatchEventToListeners(WebInspector.TracingModel.Events.BufferUsage, usage);
164     },
165
166     /**
167      * @param {!Array.<!WebInspector.TracingModel.EventPayload>} events
168      */
169     _eventsCollected: function(events)
170     {
171         for (var i = 0; i < events.length; ++i)
172             this._addEvent(events[i]);
173     },
174
175     _tracingComplete: function()
176     {
177         this._active = false;
178         if (!this._pendingStopCallback)
179             return;
180         this._pendingStopCallback();
181         this._pendingStopCallback = null;
182     },
183
184     reset: function()
185     {
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 = [];
195     },
196
197     /**
198       * @param {!WebInspector.TracingModel.EventPayload} payload
199       */
200     _addEvent: function(payload)
201     {
202         var process = this._processById[payload.pid];
203         if (!process) {
204             process = new WebInspector.TracingModel.Process(payload.pid);
205             this._processById[payload.pid] = process;
206         }
207         if (payload.ph === WebInspector.TracingModel.Phase.SnapshotObject) {
208             process.addObject(payload);
209             return;
210         }
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);
223             if (!event)
224                 return;
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);
230             }
231             return;
232         }
233         switch (payload.name) {
234         case WebInspector.TracingModel.MetadataEvent.ProcessSortIndex:
235             process._setSortIndex(payload.args["sort_index"]);
236             break;
237         case WebInspector.TracingModel.MetadataEvent.ProcessName:
238             process._setName(payload.args["name"]);
239             break;
240         case WebInspector.TracingModel.MetadataEvent.ThreadSortIndex:
241             thread._setSortIndex(payload.args["sort_index"]);
242             break;
243         case WebInspector.TracingModel.MetadataEvent.ThreadName:
244             thread._setName(payload.args["name"]);
245             break;
246         }
247     },
248
249      /**
250       * @param {!WebInspector.TracingModel.EventPayload} payload
251       */
252     _processDevToolsMetadataEvent: function(payload)
253     {
254         if (payload.args["sessionId"] !== this._sessionId)
255             return;
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"];
263         }
264     },
265
266     /**
267      * @return {?number}
268      */
269     minimumRecordTime: function()
270     {
271         return this._minimumRecordTime;
272     },
273
274     /**
275      * @return {?number}
276      */
277     maximumRecordTime: function()
278     {
279         return this._maximumRecordTime;
280     },
281
282     /**
283      * @return {!Array.<!WebInspector.TracingModel.Process>}
284      */
285     sortedProcesses: function()
286     {
287         return WebInspector.TracingModel.NamedObject._sort(Object.values(this._processById));
288     },
289
290     __proto__: WebInspector.Object.prototype
291 }
292
293 /**
294  * @constructor
295  * @param {!WebInspector.TracingModel.EventPayload} payload
296  * @param {number} level
297  */
298 WebInspector.TracingModel.Event = function(payload, level)
299 {
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;
305     this.level = level;
306 }
307
308 WebInspector.TracingModel.Event.prototype = {
309     /**
310      * @param {number} duration
311      */
312     _setDuration: function(duration)
313     {
314         this.endTime = this.startTime + duration;
315         this.duration = duration;
316     },
317
318     /**
319      * @param {!WebInspector.TracingModel.EventPayload} payload
320      */
321     _complete: function(payload)
322     {
323         if (this.name !== payload.name) {
324             console.assert(false, "Open/close event mismatch: " + this.name + " vs. " + payload.name);
325             return;
326         }
327         var duration = payload.ts - this.startTime;
328         if (duration < 0) {
329             console.assert(false, "Event out of order: " + this.name);
330             return;
331         }
332         this._setDuration(duration);
333     }
334 }
335
336 /**
337  * @constructor
338  */
339 WebInspector.TracingModel.NamedObject = function()
340 {
341 }
342
343 WebInspector.TracingModel.NamedObject.prototype =
344 {
345     /**
346      * @param {string} name
347      */
348     _setName: function(name)
349     {
350         this._name = name;
351     },
352
353     /**
354      * @return {string}
355      */
356     name: function()
357     {
358         return this._name;
359     },
360
361     /**
362      * @param {number} sortIndex
363      */
364     _setSortIndex: function(sortIndex)
365     {
366         this._sortIndex = sortIndex;
367     },
368 }
369
370 /**
371  * @param {!Array.<!WebInspector.TracingModel.NamedObject>} array
372  */
373 WebInspector.TracingModel.NamedObject._sort = function(array)
374 {
375     /**
376      * @param {!WebInspector.TracingModel.NamedObject} a
377      * @param {!WebInspector.TracingModel.NamedObject} b
378      */
379     function comparator(a, b)
380     {
381         return a._sortIndex !== b._sortIndex ? a._sortIndex - b._sortIndex : a.name().localeCompare(b.name());
382     }
383     return array.sort(comparator);
384 }
385
386 /**
387  * @constructor
388  * @extends {WebInspector.TracingModel.NamedObject}
389  * @param {number} id
390  */
391 WebInspector.TracingModel.Process = function(id)
392 {
393     WebInspector.TracingModel.NamedObject.call(this);
394     this._setName("Process " + id);
395     this._threads = {};
396     this._objects = {};
397 }
398
399 WebInspector.TracingModel.Process.prototype = {
400     /**
401      * @param {number} id
402      * @return {!WebInspector.TracingModel.Thread}
403      */
404     threadById: function(id)
405     {
406         var thread = this._threads[id];
407         if (!thread) {
408             thread = new WebInspector.TracingModel.Thread(id);
409             this._threads[id] = thread;
410         }
411         return thread;
412     },
413
414     /**
415      * @param {!WebInspector.TracingModel.EventPayload} event
416      */
417     addObject: function(event)
418     {
419         this.objectsByName(event.name).push(new WebInspector.TracingModel.Event(event, 0));
420     },
421
422     /**
423      * @param {string} name
424      * @return {!Array.<!WebInspector.TracingModel.Event>}
425      */
426     objectsByName: function(name)
427     {
428         var objects = this._objects[name];
429         if (!objects) {
430             objects = [];
431             this._objects[name] = objects;
432         }
433         return objects;
434     },
435
436     /**
437      * @return {!Array.<string>}
438      */
439     sortedObjectNames: function()
440     {
441         return Object.keys(this._objects).sort();
442     },
443
444     /**
445      * @return {!Array.<!WebInspector.TracingModel.Thread>}
446      */
447     sortedThreads: function()
448     {
449         return WebInspector.TracingModel.NamedObject._sort(Object.values(this._threads));
450     },
451
452     __proto__: WebInspector.TracingModel.NamedObject.prototype
453 }
454
455 /**
456  * @constructor
457  * @extends {WebInspector.TracingModel.NamedObject}
458  * @param {number} id
459  */
460 WebInspector.TracingModel.Thread = function(id)
461 {
462     WebInspector.TracingModel.NamedObject.call(this);
463     this._setName("Thread " + id);
464     this._events = [];
465     this._stack = [];
466     this._maxStackDepth = 0;
467 }
468
469 WebInspector.TracingModel.Thread.prototype = {
470     /**
471      * @param {!WebInspector.TracingModel.EventPayload} payload
472      * @return {?WebInspector.TracingModel.Event} event
473      */
474     addEvent: function(payload)
475     {
476         for (var top = this._stack.peekLast(); top && top.endTime && top.endTime <= payload.ts;) {
477             this._stack.pop();
478             top = this._stack.peekLast();
479         }
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).
483             if (openEvent)
484                 openEvent._complete(payload);
485             return null;
486         }
487
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;
495         }
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);
499         return event;
500     },
501
502     /**
503      * @return {!Array.<!WebInspector.TracingModel.Event>}
504      */
505     events: function()
506     {
507         return this._events;
508     },
509
510     /**
511      * @return {number}
512      */
513     maxStackDepth: function()
514     {
515         // Reserve one for non-container events.
516         return this._maxStackDepth + 1;
517     },
518
519     __proto__: WebInspector.TracingModel.NamedObject.prototype
520 }
521
522
523 /**
524  * @constructor
525  * @implements {TracingAgent.Dispatcher}
526  * @param {!WebInspector.TracingModel} tracingModel
527  */
528 WebInspector.TracingDispatcher = function(tracingModel)
529 {
530     this._tracingModel = tracingModel;
531 }
532
533 WebInspector.TracingDispatcher.prototype = {
534     /**
535      * @param {number} usage
536      */
537     bufferUsage: function(usage)
538     {
539         this._tracingModel._bufferUsage(usage);
540     },
541
542     /**
543      * @param {!Array.<!WebInspector.TracingModel.EventPayload>} data
544      */
545     dataCollected: function(data)
546     {
547         this._tracingModel._eventsCollected(data);
548     },
549
550     tracingComplete: function()
551     {
552         this._tracingModel._tracingComplete();
553     }
554 }