Upstream version 7.35.139.0
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / trace_viewer / tracing / trace_model / thread.js
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 'use strict';
6
7 /**
8  * @fileoverview Provides the Thread class.
9  */
10 tvcm.require('tvcm.guid');
11 tvcm.require('tvcm.range');
12 tvcm.require('tracing.trace_model.slice');
13 tvcm.require('tracing.trace_model.slice_group');
14 tvcm.require('tracing.trace_model.async_slice_group');
15 tvcm.require('tracing.trace_model.sample');
16
17 tvcm.exportTo('tracing.trace_model', function() {
18
19   var Slice = tracing.trace_model.Slice;
20   var SliceGroup = tracing.trace_model.SliceGroup;
21   var AsyncSlice = tracing.trace_model.AsyncSlice;
22   var AsyncSliceGroup = tracing.trace_model.AsyncSliceGroup;
23
24   /**
25    * A ThreadSlice represents an interval of time on a thread resource
26    * with associated nestinged slice information.
27    *
28    * ThreadSlices are typically associated with a specific trace event pair on a
29    * specific thread.
30    * For example,
31    *   TRACE_EVENT_BEGIN1("x","myArg", 7) at time=0.1ms
32    *   TRACE_EVENT_END0()                 at time=0.3ms
33    * This results in a single slice from 0.1 with duration 0.2 on a
34    * specific thread.
35    *
36    * @constructor
37    */
38   function ThreadSlice(cat, title, colorId, start, args, opt_duration,
39                        opt_threadStart, opt_threadDuration) {
40     Slice.call(this, cat, title, colorId, start, args, opt_duration,
41                opt_threadStart, opt_threadDuration);
42     // Do not modify this directly.
43     // subSlices is configured by SliceGroup.rebuildSubRows_.
44     this.subSlices = [];
45   }
46
47   ThreadSlice.prototype = {
48     __proto__: Slice.prototype
49   };
50
51   /**
52    * A Thread stores all the trace events collected for a particular
53    * thread. We organize the synchronous slices on a thread by "subrows," where
54    * subrow 0 has all the root slices, subrow 1 those nested 1 deep, and so on.
55    * The asynchronous slices are stored in an AsyncSliceGroup object.
56    *
57    * The slices stored on a Thread should be instances of
58    * ThreadSlice.
59    *
60    * @constructor
61    */
62   function Thread(parent, tid) {
63     this.guid_ = tvcm.GUID.allocate();
64     if (!parent)
65       throw new Error('Parent must be provided.');
66     this.parent = parent;
67     this.sortIndex = 0;
68     this.tid = tid;
69
70     var that = this;
71     function ThreadSliceForThisThread(
72         cat, title, colorId, start, args, opt_duration,
73         opt_threadStart, opt_threadDuration) {
74       ThreadSlice.call(this, cat, title, colorId, start, args, opt_duration,
75                        opt_threadStart, opt_threadDuration);
76       this.parentThread = that;
77     }
78     ThreadSliceForThisThread.prototype = {
79       __proto__: ThreadSlice.prototype
80     };
81
82     this.sliceGroup = new SliceGroup(ThreadSliceForThisThread);
83     this.timeSlices = undefined;
84     this.samples_ = [];
85     this.kernelSliceGroup = new SliceGroup();
86     this.asyncSliceGroup = new AsyncSliceGroup();
87     this.bounds = new tvcm.Range();
88     this.ephemeralSettings = {};
89   }
90
91   Thread.prototype = {
92
93     /*
94      * @return {Number} A globally unique identifier for this counter.
95      */
96     get guid() {
97       return this.guid_;
98     },
99
100     compareTo: function(that) {
101       return Thread.compare(this, that);
102     },
103
104     toJSON: function() {
105       var obj = new Object();
106       var keys = Object.keys(this);
107       for (var i = 0; i < keys.length; i++) {
108         var key = keys[i];
109         if (typeof this[key] == 'function')
110           continue;
111         if (key == 'parent') {
112           obj[key] = this[key].guid;
113           continue;
114         }
115         obj[key] = this[key];
116       }
117       return obj;
118     },
119
120     /**
121      * Adds a new sample in the thread's samples.
122      *
123      * Calls to addSample must be made with non-monotonically-decreasing
124      * timestamps.
125      *
126      * @param {String} category Category of the sample to add.
127      * @param {String} title Title of the sample to add.
128      * @param {Number} ts The timetsamp of the sample, in milliseconds.
129      * @param {Object.<string, Object>=} opt_args Arguments associated with
130      * the sample.
131      */
132     addSample: function(category, title, ts, opt_args) {
133       if (this.samples_.length) {
134         var lastSample = this.samples_[this.samples_.length - 1];
135         if (ts < lastSample.start) {
136           throw new
137               Error('Samples must be added in increasing timestamp order.');
138         }
139       }
140       var colorId = tvcm.ui.getStringColorId(title);
141       var sample = new tracing.trace_model.Sample(category, title, colorId, ts,
142                                                   opt_args ? opt_args : {});
143       this.samples_.push(sample);
144       return sample;
145     },
146
147     /**
148      * Returns the array of samples added to this thread. If no samples
149      * have been added, an empty array is returned.
150      *
151      * @return {Array<Sample>} array of samples.
152      */
153     get samples() {
154       return this.samples_;
155     },
156
157     /**
158      * Name of the thread, if present.
159      */
160     name: undefined,
161
162     /**
163      * Shifts all the timestamps inside this thread forward by the amount
164      * specified.
165      */
166     shiftTimestampsForward: function(amount) {
167       this.sliceGroup.shiftTimestampsForward(amount);
168
169       if (this.timeSlices) {
170         for (var i = 0; i < this.timeSlices.length; i++) {
171           var slice = this.timeSlices[i];
172           slice.start += amount;
173         }
174       }
175
176       if (this.samples_.length) {
177         for (var i = 0; i < this.samples_.length; i++) {
178           var sample = this.samples_[i];
179           sample.start += amount;
180         }
181       }
182
183       this.kernelSliceGroup.shiftTimestampsForward(amount);
184       this.asyncSliceGroup.shiftTimestampsForward(amount);
185     },
186
187     /**
188      * Determines whether this thread is empty. If true, it usually implies
189      * that it should be pruned from the model.
190      */
191     get isEmpty() {
192       if (this.sliceGroup.length)
193         return false;
194       if (this.sliceGroup.openSliceCount)
195         return false;
196       if (this.timeSlices && this.timeSlices.length)
197         return false;
198       if (this.kernelSliceGroup.length)
199         return false;
200       if (this.asyncSliceGroup.length)
201         return false;
202       if (this.samples_.length)
203         return false;
204       return true;
205     },
206
207     /**
208      * Updates the bounds based on the
209      * current objects associated with the thread.
210      */
211     updateBounds: function() {
212       this.bounds.reset();
213
214       this.sliceGroup.updateBounds();
215       this.bounds.addRange(this.sliceGroup.bounds);
216
217       this.kernelSliceGroup.updateBounds();
218       this.bounds.addRange(this.kernelSliceGroup.bounds);
219
220       this.asyncSliceGroup.updateBounds();
221       this.bounds.addRange(this.asyncSliceGroup.bounds);
222
223       if (this.timeSlices && this.timeSlices.length) {
224         this.bounds.addValue(this.timeSlices[0].start);
225         this.bounds.addValue(
226             this.timeSlices[this.timeSlices.length - 1].end);
227       }
228       if (this.samples_.length) {
229         this.bounds.addValue(this.samples_[0].start);
230         this.bounds.addValue(
231             this.samples_[this.samples_.length - 1].end);
232       }
233     },
234
235     addCategoriesToDict: function(categoriesDict) {
236       for (var i = 0; i < this.sliceGroup.length; i++)
237         categoriesDict[this.sliceGroup.slices[i].category] = true;
238       for (var i = 0; i < this.kernelSliceGroup.length; i++)
239         categoriesDict[this.kernelSliceGroup.slices[i].category] = true;
240       for (var i = 0; i < this.asyncSliceGroup.length; i++)
241         categoriesDict[this.asyncSliceGroup.slices[i].category] = true;
242       for (var i = 0; i < this.samples_.length; i++)
243         categoriesDict[this.samples_[i].category] = true;
244     },
245
246     autoCloseOpenSlices: function(opt_maxTimestamp) {
247       this.sliceGroup.autoCloseOpenSlices(opt_maxTimestamp);
248       this.kernelSliceGroup.autoCloseOpenSlices(opt_maxTimestamp);
249     },
250
251     mergeKernelWithUserland: function() {
252       if (this.kernelSliceGroup.length > 0) {
253         var newSlices = SliceGroup.merge(
254             this.sliceGroup, this.kernelSliceGroup);
255         this.sliceGroup.slices = newSlices.slices;
256         this.kernelSliceGroup = new SliceGroup();
257         this.updateBounds();
258       }
259     },
260
261     createSubSlices: function() {
262       this.sliceGroup.createSubSlices();
263     },
264
265     /**
266      * @return {String} A user-friendly name for this thread.
267      */
268     get userFriendlyName() {
269       return this.name || this.tid;
270     },
271
272     /**
273      * @return {String} User friendly details about this thread.
274      */
275     get userFriendlyDetails() {
276       return 'tid: ' + this.tid +
277           (this.name ? ', name: ' + this.name : '');
278     },
279
280     getSettingsKey: function() {
281       if (!this.name)
282         return undefined;
283       var parentKey = this.parent.getSettingsKey();
284       if (!parentKey)
285         return undefined;
286       return parentKey + '.' + this.name;
287     },
288
289     /*
290      * Returns the index of the slice in the timeSlices array, or undefined.
291      */
292     indexOfTimeSlice: function(timeSlice) {
293       var i = tvcm.findLowIndexInSortedArray(
294           this.timeSlices,
295           function(slice) { return slice.start; },
296           timeSlice.start);
297       if (this.timeSlices[i] !== timeSlice)
298         return undefined;
299       return i;
300     },
301
302     iterateAllEvents: function(callback, opt_this) {
303       this.sliceGroup.iterateAllEvents(callback, opt_this);
304       this.kernelSliceGroup.iterateAllEvents(callback, opt_this);
305       this.asyncSliceGroup.iterateAllEvents(callback, opt_this);
306
307       if (this.timeSlices && this.timeSlices.length)
308         this.timeSlices.forEach(callback, opt_this);
309
310       this.samples_.forEach(callback, opt_this);
311     }
312   };
313
314   /**
315    * Comparison between threads that orders first by parent.compareTo,
316    * then by names, then by tid.
317    */
318   Thread.compare = function(x, y) {
319     var tmp = x.parent.compareTo(y.parent);
320     if (tmp)
321       return tmp;
322
323     tmp = x.sortIndex - y.sortIndex;
324     if (tmp)
325       return tmp;
326
327     tmp = tvcm.comparePossiblyUndefinedValues(
328         x.name, y.name,
329         function(x, y) { return x.localeCompare(y); });
330     if (tmp)
331       return tmp;
332
333     return x.tid - y.tid;
334   };
335
336   return {
337     ThreadSlice: ThreadSlice,
338     Thread: Thread
339   };
340 });