b334c6dcd7ee6f465aa2081b6775d262459e6428
[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     this.sliceGroup = new SliceGroup(ThreadSlice);
70     this.timeSlices = undefined;
71     this.samples_ = [];
72     this.kernelSliceGroup = new SliceGroup();
73     this.asyncSliceGroup = new AsyncSliceGroup();
74     this.bounds = new tvcm.Range();
75     this.ephemeralSettings = {};
76   }
77
78   Thread.prototype = {
79
80     /*
81      * @return {Number} A globally unique identifier for this counter.
82      */
83     get guid() {
84       return this.guid_;
85     },
86
87     compareTo: function(that) {
88       return Thread.compare(this, that);
89     },
90
91     toJSON: function() {
92       var obj = new Object();
93       var keys = Object.keys(this);
94       for (var i = 0; i < keys.length; i++) {
95         var key = keys[i];
96         if (typeof this[key] == 'function')
97           continue;
98         if (key == 'parent') {
99           obj[key] = this[key].guid;
100           continue;
101         }
102         obj[key] = this[key];
103       }
104       return obj;
105     },
106
107     /**
108      * Adds a new sample in the thread's samples.
109      *
110      * Calls to addSample must be made with non-monotonically-decreasing
111      * timestamps.
112      *
113      * @param {String} category Category of the sample to add.
114      * @param {String} title Title of the sample to add.
115      * @param {Number} ts The timetsamp of the sample, in milliseconds.
116      * @param {Object.<string, Object>=} opt_args Arguments associated with
117      * the sample.
118      */
119     addSample: function(category, title, ts, opt_args) {
120       if (this.samples_.length) {
121         var lastSample = this.samples_[this.samples_.length - 1];
122         if (ts < lastSample.start) {
123           throw new
124               Error('Samples must be added in increasing timestamp order.');
125         }
126       }
127       var colorId = tvcm.ui.getStringColorId(title);
128       var sample = new tracing.trace_model.Sample(category, title, colorId, ts,
129                                                   opt_args ? opt_args : {});
130       this.samples_.push(sample);
131       return sample;
132     },
133
134     /**
135      * Returns the array of samples added to this thread. If no samples
136      * have been added, an empty array is returned.
137      *
138      * @return {Array<Sample>} array of samples.
139      */
140     get samples() {
141       return this.samples_;
142     },
143
144     /**
145      * Name of the thread, if present.
146      */
147     name: undefined,
148
149     /**
150      * Shifts all the timestamps inside this thread forward by the amount
151      * specified.
152      */
153     shiftTimestampsForward: function(amount) {
154       this.sliceGroup.shiftTimestampsForward(amount);
155
156       if (this.timeSlices) {
157         for (var i = 0; i < this.timeSlices.length; i++) {
158           var slice = this.timeSlices[i];
159           slice.start += amount;
160         }
161       }
162
163       if (this.samples_.length) {
164         for (var i = 0; i < this.samples_.length; i++) {
165           var sample = this.samples_[i];
166           sample.start += amount;
167         }
168       }
169
170       this.kernelSliceGroup.shiftTimestampsForward(amount);
171       this.asyncSliceGroup.shiftTimestampsForward(amount);
172     },
173
174     /**
175      * Determines whether this thread is empty. If true, it usually implies
176      * that it should be pruned from the model.
177      */
178     get isEmpty() {
179       if (this.sliceGroup.length)
180         return false;
181       if (this.sliceGroup.openSliceCount)
182         return false;
183       if (this.timeSlices && this.timeSlices.length)
184         return false;
185       if (this.kernelSliceGroup.length)
186         return false;
187       if (this.asyncSliceGroup.length)
188         return false;
189       if (this.samples_.length)
190         return false;
191       return true;
192     },
193
194     /**
195      * Updates the bounds based on the
196      * current objects associated with the thread.
197      */
198     updateBounds: function() {
199       this.bounds.reset();
200
201       this.sliceGroup.updateBounds();
202       this.bounds.addRange(this.sliceGroup.bounds);
203
204       this.kernelSliceGroup.updateBounds();
205       this.bounds.addRange(this.kernelSliceGroup.bounds);
206
207       this.asyncSliceGroup.updateBounds();
208       this.bounds.addRange(this.asyncSliceGroup.bounds);
209
210       if (this.timeSlices && this.timeSlices.length) {
211         this.bounds.addValue(this.timeSlices[0].start);
212         this.bounds.addValue(
213             this.timeSlices[this.timeSlices.length - 1].end);
214       }
215       if (this.samples_.length) {
216         this.bounds.addValue(this.samples_[0].start);
217         this.bounds.addValue(
218             this.samples_[this.samples_.length - 1].end);
219       }
220     },
221
222     addCategoriesToDict: function(categoriesDict) {
223       for (var i = 0; i < this.sliceGroup.length; i++)
224         categoriesDict[this.sliceGroup.slices[i].category] = true;
225       for (var i = 0; i < this.kernelSliceGroup.length; i++)
226         categoriesDict[this.kernelSliceGroup.slices[i].category] = true;
227       for (var i = 0; i < this.asyncSliceGroup.length; i++)
228         categoriesDict[this.asyncSliceGroup.slices[i].category] = true;
229       for (var i = 0; i < this.samples_.length; i++)
230         categoriesDict[this.samples_[i].category] = true;
231     },
232
233     autoCloseOpenSlices: function(opt_maxTimestamp) {
234       this.sliceGroup.autoCloseOpenSlices(opt_maxTimestamp);
235       this.kernelSliceGroup.autoCloseOpenSlices(opt_maxTimestamp);
236     },
237
238     mergeKernelWithUserland: function() {
239       if (this.kernelSliceGroup.length > 0) {
240         var newSlices = SliceGroup.merge(
241             this.sliceGroup, this.kernelSliceGroup);
242         this.sliceGroup.slices = newSlices.slices;
243         this.kernelSliceGroup = new SliceGroup();
244         this.updateBounds();
245       }
246     },
247
248     createSubSlices: function() {
249       this.sliceGroup.createSubSlices();
250     },
251
252     /**
253      * @return {String} A user-friendly name for this thread.
254      */
255     get userFriendlyName() {
256       return this.name || this.tid;
257     },
258
259     /**
260      * @return {String} User friendly details about this thread.
261      */
262     get userFriendlyDetails() {
263       return 'tid: ' + this.tid +
264           (this.name ? ', name: ' + this.name : '');
265     },
266
267     getSettingsKey: function() {
268       if (!this.name)
269         return undefined;
270       var parentKey = this.parent.getSettingsKey();
271       if (!parentKey)
272         return undefined;
273       return parentKey + '.' + this.name;
274     },
275
276     /*
277      * Returns the index of the slice in the timeSlices array, or undefined.
278      */
279     indexOfTimeSlice: function(timeSlice) {
280       var i = tvcm.findLowIndexInSortedArray(
281           this.timeSlices,
282           function(slice) { return slice.start; },
283           timeSlice.start);
284       if (this.timeSlices[i] !== timeSlice)
285         return undefined;
286       return i;
287     },
288
289     iterateAllEvents: function(callback) {
290       this.sliceGroup.iterateAllEvents(callback);
291       this.kernelSliceGroup.iterateAllEvents(callback);
292       this.asyncSliceGroup.iterateAllEvents(callback);
293
294       if (this.timeSlices && this.timeSlices.length)
295         this.timeSlices.forEach(callback);
296
297       this.samples_.forEach(callback);
298     }
299   };
300
301   /**
302    * Comparison between threads that orders first by parent.compareTo,
303    * then by names, then by tid.
304    */
305   Thread.compare = function(x, y) {
306     var tmp = x.parent.compareTo(y.parent);
307     if (tmp)
308       return tmp;
309
310     tmp = x.sortIndex - y.sortIndex;
311     if (tmp)
312       return tmp;
313
314     tmp = tvcm.comparePossiblyUndefinedValues(
315         x.name, y.name,
316         function(x, y) { return x.localeCompare(y); });
317     if (tmp)
318       return tmp;
319
320     return x.tid - y.tid;
321   };
322
323   return {
324     ThreadSlice: ThreadSlice,
325     Thread: Thread
326   };
327 });