Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / timeline / 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.TargetAwareObject}
10  */
11 WebInspector.TracingModel = function(target)
12 {
13     WebInspector.TargetAwareObject.call(this, target);
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 };
75
76 WebInspector.TracingModel.prototype = {
77     /**
78      * @return {!Array.<!WebInspector.TracingModel.Event>}
79      */
80     devtoolsMetadataEvents: function()
81     {
82         return this._devtoolsMetadataEvents;
83     },
84
85     /**
86      * @param {string} categoryFilter
87      * @param {string} options
88      * @param {function(?string)=} callback
89      */
90     start: function(categoryFilter, options, callback)
91     {
92         this.reset();
93         var bufferUsageReportingIntervalMs = 500;
94         /**
95          * @param {?string} error
96          * @param {string} sessionId
97          * @this {WebInspector.TracingModel}
98          */
99         function callbackWrapper(error, sessionId)
100         {
101             this._sessionId = sessionId;
102             if (callback)
103                 callback(error);
104         }
105         TracingAgent.start(categoryFilter, options, bufferUsageReportingIntervalMs, callbackWrapper.bind(this));
106         this._active = true;
107     },
108
109     /**
110      * @param {function()} callback
111      */
112     stop: function(callback)
113     {
114         if (!this._active) {
115             callback();
116             return;
117         }
118         this._pendingStopCallback = callback;
119         TracingAgent.end();
120     },
121
122     /**
123      * @return {?string}
124      */
125     sessionId: function()
126     {
127         return this._sessionId;
128     },
129
130     /**
131      * @param {string} sessionId
132      * @param {!Array.<!WebInspector.TracingModel.EventPayload>} events
133      */
134     setEventsForTest: function(sessionId, events)
135     {
136         this.reset();
137         this._sessionId = sessionId;
138         this._eventsCollected(events);
139         this._tracingComplete();
140     },
141
142     /**
143      * @param {number} usage
144      */
145     _bufferUsage: function(usage)
146     {
147         this.dispatchEventToListeners(WebInspector.TracingModel.Events.BufferUsage, usage);
148     },
149
150     /**
151      * @param {!Array.<!WebInspector.TracingModel.EventPayload>} events
152      */
153     _eventsCollected: function(events)
154     {
155         for (var i = 0; i < events.length; ++i)
156             this._addEvent(events[i]);
157     },
158
159     _tracingComplete: function()
160     {
161         this._active = false;
162         if (!this._pendingStopCallback)
163             return;
164         this._pendingStopCallback();
165         this._pendingStopCallback = null;
166     },
167
168     reset: function()
169     {
170         this._processById = {};
171         this._minimumRecordTime = 0;
172         this._maximumRecordTime = 0;
173         this._sessionId = null;
174         this._devtoolsMetadataEvents = [];
175     },
176
177     /**
178       * @param {!WebInspector.TracingModel.EventPayload} payload
179       */
180     _addEvent: function(payload)
181     {
182         var process = this._processById[payload.pid];
183         if (!process) {
184             process = new WebInspector.TracingModel.Process(payload.pid);
185             this._processById[payload.pid] = process;
186         }
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);
203             return;
204         }
205         switch (payload.name) {
206         case WebInspector.TracingModel.MetadataEvent.ProcessSortIndex:
207             process._setSortIndex(payload.args["sort_index"]);
208             break;
209         case WebInspector.TracingModel.MetadataEvent.ProcessName:
210             process._setName(payload.args["name"]);
211             break;
212         case WebInspector.TracingModel.MetadataEvent.ThreadSortIndex:
213             thread._setSortIndex(payload.args["sort_index"]);
214             break;
215         case WebInspector.TracingModel.MetadataEvent.ThreadName:
216             thread._setName(payload.args["name"]);
217             break;
218         }
219     },
220
221     /**
222      * @return {number}
223      */
224     minimumRecordTime: function()
225     {
226         return this._minimumRecordTime;
227     },
228
229     /**
230      * @return {number}
231      */
232     maximumRecordTime: function()
233     {
234         return this._maximumRecordTime;
235     },
236
237     /**
238      * @return {!Array.<!WebInspector.TracingModel.Process>}
239      */
240     sortedProcesses: function()
241     {
242         return WebInspector.TracingModel.NamedObject._sort(Object.values(this._processById));
243     },
244
245     __proto__: WebInspector.TargetAwareObject.prototype
246 }
247
248 /**
249  * @constructor
250  * @param {!WebInspector.TracingModel.EventPayload} payload
251  * @param {number} level
252  * @param {?WebInspector.TracingModel.Thread} thread
253  */
254 WebInspector.TracingModel.Event = function(payload, level, thread)
255 {
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;
261     this.level = level;
262
263     if (payload.dur)
264         this._setEndTime((payload.ts + payload.dur) / 1000);
265
266     if (payload.id)
267         this.id = payload.id;
268
269     this.thread = thread;
270
271     /** @type {?string} */
272     this.warning = null;
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;
283
284     /** @type {number} */
285     this.selfTime = 0;
286 }
287
288 WebInspector.TracingModel.Event.prototype = {
289     /**
290      * @param {number} endTime
291      */
292     _setEndTime: function(endTime)
293     {
294         if (endTime < this.startTime) {
295             console.assert(false, "Event out of order: " + this.name);
296             return;
297         }
298         this.endTime = endTime;
299         this.duration = endTime - this.startTime;
300     },
301
302     /**
303      * @param {!WebInspector.TracingModel.EventPayload} payload
304      */
305     _complete: function(payload)
306     {
307         if (this.name !== payload.name) {
308             console.assert(false, "Open/close event mismatch: " + this.name + " vs. " + payload.name);
309             return;
310         }
311         if (payload.args) {
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];
316             }
317         }
318         this._setEndTime(payload.ts / 1000);
319     }
320 }
321
322 /**
323  * @param {!WebInspector.TracingModel.Event} a
324  * @param {!WebInspector.TracingModel.Event} b
325  * @return {number}
326  */
327 WebInspector.TracingModel.Event.compareStartTime = function (a, b)
328 {
329     return a.startTime - b.startTime;
330 }
331
332 /**
333  * @constructor
334  */
335 WebInspector.TracingModel.NamedObject = function()
336 {
337 }
338
339 WebInspector.TracingModel.NamedObject.prototype =
340 {
341     /**
342      * @param {string} name
343      */
344     _setName: function(name)
345     {
346         this._name = name;
347     },
348
349     /**
350      * @return {string}
351      */
352     name: function()
353     {
354         return this._name;
355     },
356
357     /**
358      * @param {number} sortIndex
359      */
360     _setSortIndex: function(sortIndex)
361     {
362         this._sortIndex = sortIndex;
363     },
364 }
365
366 /**
367  * @param {!Array.<!WebInspector.TracingModel.NamedObject>} array
368  */
369 WebInspector.TracingModel.NamedObject._sort = function(array)
370 {
371     /**
372      * @param {!WebInspector.TracingModel.NamedObject} a
373      * @param {!WebInspector.TracingModel.NamedObject} b
374      */
375     function comparator(a, b)
376     {
377         return a._sortIndex !== b._sortIndex ? a._sortIndex - b._sortIndex : a.name().localeCompare(b.name());
378     }
379     return array.sort(comparator);
380 }
381
382 /**
383  * @constructor
384  * @extends {WebInspector.TracingModel.NamedObject}
385  * @param {number} id
386  */
387 WebInspector.TracingModel.Process = function(id)
388 {
389     WebInspector.TracingModel.NamedObject.call(this);
390     this._setName("Process " + id);
391     this._threads = {};
392     this._objects = {};
393 }
394
395 WebInspector.TracingModel.Process.prototype = {
396     /**
397      * @param {number} id
398      * @return {!WebInspector.TracingModel.Thread}
399      */
400     threadById: function(id)
401     {
402         var thread = this._threads[id];
403         if (!thread) {
404             thread = new WebInspector.TracingModel.Thread(this, id);
405             this._threads[id] = thread;
406         }
407         return thread;
408     },
409
410     /**
411      * @param {!WebInspector.TracingModel.Event} event
412      */
413     addObject: function(event)
414     {
415         this.objectsByName(event.name).push(event);
416     },
417
418     /**
419      * @param {string} name
420      * @return {!Array.<!WebInspector.TracingModel.Event>}
421      */
422     objectsByName: function(name)
423     {
424         var objects = this._objects[name];
425         if (!objects) {
426             objects = [];
427             this._objects[name] = objects;
428         }
429         return objects;
430     },
431
432     /**
433      * @return {!Array.<string>}
434      */
435     sortedObjectNames: function()
436     {
437         return Object.keys(this._objects).sort();
438     },
439
440     /**
441      * @return {!Array.<!WebInspector.TracingModel.Thread>}
442      */
443     sortedThreads: function()
444     {
445         return WebInspector.TracingModel.NamedObject._sort(Object.values(this._threads));
446     },
447
448     __proto__: WebInspector.TracingModel.NamedObject.prototype
449 }
450
451 /**
452  * @constructor
453  * @extends {WebInspector.TracingModel.NamedObject}
454  * @param {!WebInspector.TracingModel.Process} process
455  * @param {number} id
456  */
457 WebInspector.TracingModel.Thread = function(process, id)
458 {
459     WebInspector.TracingModel.NamedObject.call(this);
460     this._process = process;
461     this._setName("Thread " + id);
462     this._events = [];
463     this._stack = [];
464     this._maxStackDepth = 0;
465 }
466
467 WebInspector.TracingModel.Thread.prototype = {
468     /**
469      * @param {!WebInspector.TracingModel.EventPayload} payload
470      * @return {?WebInspector.TracingModel.Event} event
471      */
472     addEvent: function(payload)
473     {
474         for (var top = this._stack.peekLast(); top && top.endTime && top.endTime <= payload.ts / 1000;) {
475             this._stack.pop();
476             top = this._stack.peekLast();
477         }
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).
481             if (openEvent)
482                 openEvent._complete(payload);
483             return null;
484         }
485
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;
491         }
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);
495         return event;
496     },
497
498     /**
499      * @return {!WebInspector.TracingModel.Process}
500      */
501     process: function()
502     {
503         return this._process;
504     },
505
506     /**
507      * @return {!Array.<!WebInspector.TracingModel.Event>}
508      */
509     events: function()
510     {
511         return this._events;
512     },
513
514     /**
515      * @return {number}
516      */
517     maxStackDepth: function()
518     {
519         // Reserve one for non-container events.
520         return this._maxStackDepth + 1;
521     },
522
523     __proto__: WebInspector.TracingModel.NamedObject.prototype
524 }
525
526
527 /**
528  * @constructor
529  * @implements {TracingAgent.Dispatcher}
530  * @param {!WebInspector.TracingModel} tracingModel
531  */
532 WebInspector.TracingDispatcher = function(tracingModel)
533 {
534     this._tracingModel = tracingModel;
535 }
536
537 WebInspector.TracingDispatcher.prototype = {
538     /**
539      * @param {number} usage
540      */
541     bufferUsage: function(usage)
542     {
543         this._tracingModel._bufferUsage(usage);
544     },
545
546     /**
547      * @param {!Array.<!WebInspector.TracingModel.EventPayload>} data
548      */
549     dataCollected: function(data)
550     {
551         this._tracingModel._eventsCollected(data);
552     },
553
554     tracingComplete: function()
555     {
556         this._tracingModel._tracingComplete();
557     }
558 }