Upstream version 10.38.222.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.SDKObject}
10  */
11 WebInspector.TracingModel = function(target)
12 {
13     WebInspector.SDKObject.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     "TracingStarted": "TracingStarted",
22     "TracingStopped": "TracingStopped",
23     "TracingComplete": "TracingComplete"
24 }
25
26 /** @typedef {!{
27         cat: string,
28         pid: number,
29         tid: number,
30         ts: number,
31         ph: string,
32         name: string,
33         args: !Object,
34         dur: number,
35         id: number,
36         s: string
37     }}
38  */
39 WebInspector.TracingModel.EventPayload;
40
41 /**
42  * @enum {string}
43  */
44 WebInspector.TracingModel.Phase = {
45     Begin: "B",
46     End: "E",
47     Complete: "X",
48     Instant: "i",
49     AsyncBegin: "S",
50     AsyncStepInto: "T",
51     AsyncStepPast: "p",
52     AsyncEnd: "F",
53     FlowBegin: "s",
54     FlowStep: "t",
55     FlowEnd: "f",
56     Metadata: "M",
57     Counter: "C",
58     Sample: "P",
59     CreateObject: "N",
60     SnapshotObject: "O",
61     DeleteObject: "D"
62 };
63
64 WebInspector.TracingModel.MetadataEvent = {
65     ProcessSortIndex: "process_sort_index",
66     ProcessName: "process_name",
67     ThreadSortIndex: "thread_sort_index",
68     ThreadName: "thread_name"
69 }
70
71 WebInspector.TracingModel.DevToolsMetadataEventCategory = "disabled-by-default-devtools.timeline";
72
73 WebInspector.TracingModel.FrameLifecycleEventCategory = "cc,devtools";
74
75 WebInspector.TracingModel.DevToolsMetadataEvent = {
76     TracingStartedInPage: "TracingStartedInPage",
77     TracingStartedInWorker: "TracingStartedInWorker",
78 };
79
80 WebInspector.TracingModel.prototype = {
81     /**
82      * @return {!Array.<!WebInspector.TracingModel.Event>}
83      */
84     devtoolsPageMetadataEvents: function()
85     {
86         return this._devtoolsPageMetadataEvents;
87     },
88
89     /**
90      * @return {!Array.<!WebInspector.TracingModel.Event>}
91      */
92     devtoolsWorkerMetadataEvents: function()
93     {
94         return this._devtoolsWorkerMetadataEvents;
95     },
96
97     /**
98      * @param {string} categoryFilter
99      * @param {string} options
100      * @param {function(?string)=} callback
101      */
102     start: function(categoryFilter, options, callback)
103     {
104         WebInspector.profilingLock().acquire();
105         this.reset();
106         var bufferUsageReportingIntervalMs = 500;
107         TracingAgent.start(categoryFilter, options, bufferUsageReportingIntervalMs, callback);
108         this._active = true;
109     },
110
111     stop: function()
112     {
113         if (!this._active)
114             return;
115         TracingAgent.end(this._onStop.bind(this));
116         WebInspector.profilingLock().release();
117     },
118
119     /**
120      * @return {?string}
121      */
122     sessionId: function()
123     {
124         return this._sessionId;
125     },
126
127     /**
128      * @param {string} sessionId
129      * @param {!Array.<!WebInspector.TracingModel.EventPayload>} events
130      */
131     setEventsForTest: function(sessionId, events)
132     {
133         this.reset();
134         this._sessionId = sessionId;
135         this._eventsCollected(events);
136     },
137
138     /**
139      * @param {number} usage
140      */
141     _bufferUsage: function(usage)
142     {
143         this.dispatchEventToListeners(WebInspector.TracingModel.Events.BufferUsage, usage);
144     },
145
146     /**
147      * @param {!Array.<!WebInspector.TracingModel.EventPayload>} events
148      */
149     _eventsCollected: function(events)
150     {
151         for (var i = 0; i < events.length; ++i) {
152             this._addEvent(events[i]);
153             this._rawEvents.push(events[i]);
154         }
155     },
156
157     _tracingComplete: function()
158     {
159         this._active = false;
160         this.dispatchEventToListeners(WebInspector.TracingModel.Events.TracingComplete);
161     },
162
163     /**
164      * @param {string} sessionId
165      */
166     _tracingStarted: function(sessionId)
167     {
168         this.reset();
169         this._active = true;
170         this._sessionId = sessionId;
171         this.dispatchEventToListeners(WebInspector.TracingModel.Events.TracingStarted);
172     },
173
174     _onStop: function()
175     {
176         this.dispatchEventToListeners(WebInspector.TracingModel.Events.TracingStopped);
177         this._active = false;
178     },
179
180     reset: function()
181     {
182         this._processById = {};
183         this._minimumRecordTime = 0;
184         this._maximumRecordTime = 0;
185         this._sessionId = null;
186         this._devtoolsPageMetadataEvents = [];
187         this._devtoolsWorkerMetadataEvents = [];
188         this._rawEvents = [];
189     },
190
191     /**
192       * @return {!Array.<!WebInspector.TracingModel.EventPayload>}
193       */
194     rawEvents: function()
195     {
196         return this._rawEvents;
197     },
198
199     /**
200       * @param {!WebInspector.TracingModel.EventPayload} payload
201       */
202     _addEvent: function(payload)
203     {
204         var process = this._processById[payload.pid];
205         if (!process) {
206             process = new WebInspector.TracingModel.Process(payload.pid);
207             this._processById[payload.pid] = process;
208         }
209         var thread = process.threadById(payload.tid);
210         if (payload.ph !== WebInspector.TracingModel.Phase.Metadata) {
211             var timestamp = payload.ts / 1000;
212             // We do allow records for unrelated threads to arrive out-of-order,
213             // so there's a chance we're getting records from the past.
214             if (timestamp && (!this._minimumRecordTime || timestamp < this._minimumRecordTime))
215                 this._minimumRecordTime = timestamp;
216             if (!this._maximumRecordTime || timestamp > this._maximumRecordTime)
217                 this._maximumRecordTime = timestamp;
218             var event = thread.addEvent(payload);
219             if (payload.ph === WebInspector.TracingModel.Phase.SnapshotObject)
220                 process.addObject(event);
221             if (event && event.name === WebInspector.TracingModel.DevToolsMetadataEvent.TracingStartedInPage &&
222                 event.category === WebInspector.TracingModel.DevToolsMetadataEventCategory &&
223                 event.args["sessionId"] === this._sessionId)
224                 this._devtoolsPageMetadataEvents.push(event);
225             if (event && event.name === WebInspector.TracingModel.DevToolsMetadataEvent.TracingStartedInWorker &&
226                 event.category === WebInspector.TracingModel.DevToolsMetadataEventCategory &&
227                 event.args["sessionId"] === this._sessionId)
228                 this._devtoolsWorkerMetadataEvents.push(event);
229             return;
230         }
231         switch (payload.name) {
232         case WebInspector.TracingModel.MetadataEvent.ProcessSortIndex:
233             process._setSortIndex(payload.args["sort_index"]);
234             break;
235         case WebInspector.TracingModel.MetadataEvent.ProcessName:
236             process._setName(payload.args["name"]);
237             break;
238         case WebInspector.TracingModel.MetadataEvent.ThreadSortIndex:
239             thread._setSortIndex(payload.args["sort_index"]);
240             break;
241         case WebInspector.TracingModel.MetadataEvent.ThreadName:
242             thread._setName(payload.args["name"]);
243             break;
244         }
245     },
246
247     /**
248      * @return {number}
249      */
250     minimumRecordTime: function()
251     {
252         return this._minimumRecordTime;
253     },
254
255     /**
256      * @return {number}
257      */
258     maximumRecordTime: function()
259     {
260         return this._maximumRecordTime;
261     },
262
263     /**
264      * @return {!Array.<!WebInspector.TracingModel.Process>}
265      */
266     sortedProcesses: function()
267     {
268         return WebInspector.TracingModel.NamedObject._sort(Object.values(this._processById));
269     },
270
271     __proto__: WebInspector.SDKObject.prototype
272 }
273
274
275 /**
276  * @constructor
277  * @param {!WebInspector.TracingModel} tracingModel
278  */
279 WebInspector.TracingModel.Loader = function(tracingModel)
280 {
281     this._tracingModel = tracingModel;
282     this._events = [];
283     this._sessionIdFound = false;
284 }
285
286 WebInspector.TracingModel.Loader.prototype = {
287     /**
288      * @param {!Array.<!WebInspector.TracingModel.EventPayload>} events
289      */
290     loadNextChunk: function(events) {
291         if (this._sessionIdFound) {
292             this._tracingModel._eventsCollected(events);
293             return;
294         }
295
296         var sessionId = null;
297         for (var i = 0, length = events.length; i < length; i++) {
298             var event = events[i];
299             this._events.push(event);
300
301             if (event.name === WebInspector.TracingModel.DevToolsMetadataEvent.TracingStartedInPage &&
302                 event.cat.indexOf(WebInspector.TracingModel.DevToolsMetadataEventCategory) !== -1 &&
303                 !this._sessionIdFound) {
304                 sessionId = event.args["sessionId"];
305                 this._sessionIdFound = true;
306             }
307         }
308
309         if (this._sessionIdFound) {
310             this._tracingModel._tracingStarted(sessionId);
311             this._tracingModel._eventsCollected(this._events);
312         }
313     },
314
315     finish: function()
316     {
317         if (this._sessionIdFound)
318             this._tracingModel._tracingComplete();
319         else
320             WebInspector.console.error(WebInspector.UIString("Trace event %s not found while loading tracing model.", WebInspector.TracingModel.DevToolsMetadataEvent.TracingStartedInPage));
321     }
322 }
323
324
325 /**
326  * @constructor
327  * @param {!WebInspector.TracingModel.EventPayload} payload
328  * @param {number} level
329  * @param {?WebInspector.TracingModel.Thread} thread
330  */
331 WebInspector.TracingModel.Event = function(payload, level, thread)
332 {
333     this.name = payload.name;
334     this.category = payload.cat;
335     this.startTime = payload.ts / 1000;
336     if (payload.args) {
337         // Create a new object to avoid modifying original payload which may be saved to file.
338         this.args = {};
339         for (var name in payload.args)
340             this.args[name] = payload.args[name];
341     }
342     this.phase = payload.ph;
343     this.level = level;
344
345     if (typeof payload.dur === "number")
346         this._setEndTime((payload.ts + payload.dur) / 1000);
347
348     if (payload.id)
349         this.id = payload.id;
350
351     this.thread = thread;
352
353     /** @type {?string} */
354     this.warning = null;
355     /** @type {?WebInspector.TracingModel.Event} */
356     this.initiator = null;
357     /** @type {?Array.<!ConsoleAgent.CallFrame>} */
358     this.stackTrace = null;
359     /** @type {?Element} */
360     this.previewElement = null;
361     /** @type {?string} */
362     this.imageURL = null;
363     /** @type {number} */
364     this.backendNodeId = 0;
365
366     /** @type {number} */
367     this.selfTime = 0;
368 }
369
370 WebInspector.TracingModel.Event.prototype = {
371     /**
372      * @param {number} endTime
373      */
374     _setEndTime: function(endTime)
375     {
376         if (endTime < this.startTime) {
377             console.assert(false, "Event out of order: " + this.name);
378             return;
379         }
380         this.endTime = endTime;
381         this.duration = endTime - this.startTime;
382     },
383
384     /**
385      * @param {!WebInspector.TracingModel.EventPayload} payload
386      */
387     _complete: function(payload)
388     {
389         if (this.name !== payload.name) {
390             console.assert(false, "Open/close event mismatch: " + this.name + " vs. " + payload.name + " at " + (payload.ts / 1000));
391             return;
392         }
393         if (payload.args) {
394             for (var name in payload.args) {
395                 if (name in this.args)
396                     console.error("Same argument name (" + name +  ") is used for begin and end phases of " + this.name);
397                 this.args[name] = payload.args[name];
398             }
399         }
400         this._setEndTime(payload.ts / 1000);
401     }
402 }
403
404 /**
405  * @param {!WebInspector.TracingModel.Event} a
406  * @param {!WebInspector.TracingModel.Event} b
407  * @return {number}
408  */
409 WebInspector.TracingModel.Event.compareStartTime = function (a, b)
410 {
411     return a.startTime - b.startTime;
412 }
413
414 /**
415  * @param {!WebInspector.TracingModel.Event} a
416  * @param {!WebInspector.TracingModel.Event} b
417  * @return {number}
418  */
419 WebInspector.TracingModel.Event.orderedCompareStartTime = function (a, b)
420 {
421     // Array.mergeOrdered coalesces objects if comparator returns 0.
422     // To change this behavior this comparator return -1 in the case events
423     // startTime's are equal, so both events got placed into the result array.
424     return a.startTime - b.startTime || -1;
425 }
426
427 /**
428  * @constructor
429  */
430 WebInspector.TracingModel.NamedObject = function()
431 {
432 }
433
434 WebInspector.TracingModel.NamedObject.prototype =
435 {
436     /**
437      * @param {string} name
438      */
439     _setName: function(name)
440     {
441         this._name = name;
442     },
443
444     /**
445      * @return {string}
446      */
447     name: function()
448     {
449         return this._name;
450     },
451
452     /**
453      * @param {number} sortIndex
454      */
455     _setSortIndex: function(sortIndex)
456     {
457         this._sortIndex = sortIndex;
458     },
459 }
460
461 /**
462  * @param {!Array.<!WebInspector.TracingModel.NamedObject>} array
463  */
464 WebInspector.TracingModel.NamedObject._sort = function(array)
465 {
466     /**
467      * @param {!WebInspector.TracingModel.NamedObject} a
468      * @param {!WebInspector.TracingModel.NamedObject} b
469      */
470     function comparator(a, b)
471     {
472         return a._sortIndex !== b._sortIndex ? a._sortIndex - b._sortIndex : a.name().localeCompare(b.name());
473     }
474     return array.sort(comparator);
475 }
476
477 /**
478  * @constructor
479  * @extends {WebInspector.TracingModel.NamedObject}
480  * @param {number} id
481  */
482 WebInspector.TracingModel.Process = function(id)
483 {
484     WebInspector.TracingModel.NamedObject.call(this);
485     this._setName("Process " + id);
486     this._threads = {};
487     this._objects = {};
488 }
489
490 WebInspector.TracingModel.Process.prototype = {
491     /**
492      * @param {number} id
493      * @return {!WebInspector.TracingModel.Thread}
494      */
495     threadById: function(id)
496     {
497         var thread = this._threads[id];
498         if (!thread) {
499             thread = new WebInspector.TracingModel.Thread(this, id);
500             this._threads[id] = thread;
501         }
502         return thread;
503     },
504
505     /**
506      * @param {!WebInspector.TracingModel.Event} event
507      */
508     addObject: function(event)
509     {
510         this.objectsByName(event.name).push(event);
511     },
512
513     /**
514      * @param {string} name
515      * @return {!Array.<!WebInspector.TracingModel.Event>}
516      */
517     objectsByName: function(name)
518     {
519         var objects = this._objects[name];
520         if (!objects) {
521             objects = [];
522             this._objects[name] = objects;
523         }
524         return objects;
525     },
526
527     /**
528      * @return {!Array.<string>}
529      */
530     sortedObjectNames: function()
531     {
532         return Object.keys(this._objects).sort();
533     },
534
535     /**
536      * @return {!Array.<!WebInspector.TracingModel.Thread>}
537      */
538     sortedThreads: function()
539     {
540         return WebInspector.TracingModel.NamedObject._sort(Object.values(this._threads));
541     },
542
543     __proto__: WebInspector.TracingModel.NamedObject.prototype
544 }
545
546 /**
547  * @constructor
548  * @extends {WebInspector.TracingModel.NamedObject}
549  * @param {!WebInspector.TracingModel.Process} process
550  * @param {number} id
551  */
552 WebInspector.TracingModel.Thread = function(process, id)
553 {
554     WebInspector.TracingModel.NamedObject.call(this);
555     this._process = process;
556     this._setName("Thread " + id);
557     this._events = [];
558     this._stack = [];
559     this._maxStackDepth = 0;
560 }
561
562 WebInspector.TracingModel.Thread.prototype = {
563
564     /**
565      * @return {?WebInspector.Target}
566      */
567     target: function()
568     {
569         //FIXME: correctly specify target
570         return WebInspector.targetManager.targets()[0];
571     },
572
573     /**
574      * @param {!WebInspector.TracingModel.EventPayload} payload
575      * @return {?WebInspector.TracingModel.Event} event
576      */
577     addEvent: function(payload)
578     {
579         var timestamp = payload.ts / 1000;
580         for (var top = this._stack.peekLast(); top;) {
581             // For B/E pairs, ignore time and look for top matching B event,
582             // otherwise, only pop event if it's definitely is in the past.
583             if (payload.ph === WebInspector.TracingModel.Phase.End) {
584                 if (payload.name === top.name) {
585                     top._complete(payload);
586                     this._stack.pop();
587                     return null;
588                 }
589             } else if (top.phase === WebInspector.TracingModel.Phase.Begin || (top.endTime && (top.endTime > timestamp))) {
590                 break;
591             }
592             this._stack.pop();
593             top = this._stack.peekLast();
594         }
595         // Quietly ignore unbalanced close events, they're legit (we could have missed start one).
596         if (payload.ph === WebInspector.TracingModel.Phase.End)
597             return null;
598
599         var event = new WebInspector.TracingModel.Event(payload, this._stack.length, this);
600         if (payload.ph === WebInspector.TracingModel.Phase.Begin || payload.ph === WebInspector.TracingModel.Phase.Complete) {
601             this._stack.push(event);
602             if (this._maxStackDepth < this._stack.length)
603                 this._maxStackDepth = this._stack.length;
604         }
605         if (this._events.length && this._events.peekLast().startTime > event.startTime)
606             console.assert(false, "Event is our of order: " + event.name);
607         this._events.push(event);
608         return event;
609     },
610
611     /**
612      * @return {!WebInspector.TracingModel.Process}
613      */
614     process: function()
615     {
616         return this._process;
617     },
618
619     /**
620      * @return {!Array.<!WebInspector.TracingModel.Event>}
621      */
622     events: function()
623     {
624         return this._events;
625     },
626
627     /**
628      * @return {number}
629      */
630     maxStackDepth: function()
631     {
632         // Reserve one for non-container events.
633         return this._maxStackDepth + 1;
634     },
635
636     __proto__: WebInspector.TracingModel.NamedObject.prototype
637 }
638
639
640 /**
641  * @constructor
642  * @implements {TracingAgent.Dispatcher}
643  * @param {!WebInspector.TracingModel} tracingModel
644  */
645 WebInspector.TracingDispatcher = function(tracingModel)
646 {
647     this._tracingModel = tracingModel;
648 }
649
650 WebInspector.TracingDispatcher.prototype = {
651     /**
652      * @param {number} usage
653      */
654     bufferUsage: function(usage)
655     {
656         this._tracingModel._bufferUsage(usage);
657     },
658
659     /**
660      * @param {!Array.<!WebInspector.TracingModel.EventPayload>} data
661      */
662     dataCollected: function(data)
663     {
664         this._tracingModel._eventsCollected(data);
665     },
666
667     tracingComplete: function()
668     {
669         this._tracingModel._tracingComplete();
670     },
671
672     /**
673      * @param {boolean} consoleTimeline
674      * @param {string} sessionId
675      */
676     started: function(consoleTimeline, sessionId)
677     {
678         this._tracingModel._tracingStarted(sessionId);
679     },
680
681     stopped: function()
682     {
683         this._tracingModel._onStop();
684     }
685 }