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.
8 * @fileoverview Provides the Thread class.
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');
17 tvcm.exportTo('tracing.trace_model', function() {
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;
25 * A ThreadSlice represents an interval of time on a thread resource
26 * with associated nestinged slice information.
28 * ThreadSlices are typically associated with a specific trace event pair on a
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
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_.
47 ThreadSlice.prototype = {
48 __proto__: Slice.prototype
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.
57 * The slices stored on a Thread should be instances of
62 function Thread(parent, tid) {
63 this.guid_ = tvcm.GUID.allocate();
65 throw new Error('Parent must be provided.');
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;
78 ThreadSliceForThisThread.prototype = {
79 __proto__: ThreadSlice.prototype
82 this.sliceGroup = new SliceGroup(ThreadSliceForThisThread);
83 this.timeSlices = undefined;
85 this.kernelSliceGroup = new SliceGroup();
86 this.asyncSliceGroup = new AsyncSliceGroup();
87 this.bounds = new tvcm.Range();
88 this.ephemeralSettings = {};
94 * @return {Number} A globally unique identifier for this counter.
100 compareTo: function(that) {
101 return Thread.compare(this, that);
105 var obj = new Object();
106 var keys = Object.keys(this);
107 for (var i = 0; i < keys.length; i++) {
109 if (typeof this[key] == 'function')
111 if (key == 'parent') {
112 obj[key] = this[key].guid;
115 obj[key] = this[key];
121 * Adds a new sample in the thread's samples.
123 * Calls to addSample must be made with non-monotonically-decreasing
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
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) {
137 Error('Samples must be added in increasing timestamp order.');
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);
148 * Returns the array of samples added to this thread. If no samples
149 * have been added, an empty array is returned.
151 * @return {Array<Sample>} array of samples.
154 return this.samples_;
158 * Name of the thread, if present.
163 * Shifts all the timestamps inside this thread forward by the amount
166 shiftTimestampsForward: function(amount) {
167 this.sliceGroup.shiftTimestampsForward(amount);
169 if (this.timeSlices) {
170 for (var i = 0; i < this.timeSlices.length; i++) {
171 var slice = this.timeSlices[i];
172 slice.start += amount;
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;
183 this.kernelSliceGroup.shiftTimestampsForward(amount);
184 this.asyncSliceGroup.shiftTimestampsForward(amount);
188 * Determines whether this thread is empty. If true, it usually implies
189 * that it should be pruned from the model.
192 if (this.sliceGroup.length)
194 if (this.sliceGroup.openSliceCount)
196 if (this.timeSlices && this.timeSlices.length)
198 if (this.kernelSliceGroup.length)
200 if (this.asyncSliceGroup.length)
202 if (this.samples_.length)
208 * Updates the bounds based on the
209 * current objects associated with the thread.
211 updateBounds: function() {
214 this.sliceGroup.updateBounds();
215 this.bounds.addRange(this.sliceGroup.bounds);
217 this.kernelSliceGroup.updateBounds();
218 this.bounds.addRange(this.kernelSliceGroup.bounds);
220 this.asyncSliceGroup.updateBounds();
221 this.bounds.addRange(this.asyncSliceGroup.bounds);
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);
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);
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;
246 autoCloseOpenSlices: function(opt_maxTimestamp) {
247 this.sliceGroup.autoCloseOpenSlices(opt_maxTimestamp);
248 this.kernelSliceGroup.autoCloseOpenSlices(opt_maxTimestamp);
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();
261 createSubSlices: function() {
262 this.sliceGroup.createSubSlices();
266 * @return {String} A user-friendly name for this thread.
268 get userFriendlyName() {
269 return this.name || this.tid;
273 * @return {String} User friendly details about this thread.
275 get userFriendlyDetails() {
276 return 'tid: ' + this.tid +
277 (this.name ? ', name: ' + this.name : '');
280 getSettingsKey: function() {
283 var parentKey = this.parent.getSettingsKey();
286 return parentKey + '.' + this.name;
290 * Returns the index of the slice in the timeSlices array, or undefined.
292 indexOfTimeSlice: function(timeSlice) {
293 var i = tvcm.findLowIndexInSortedArray(
295 function(slice) { return slice.start; },
297 if (this.timeSlices[i] !== timeSlice)
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);
307 if (this.timeSlices && this.timeSlices.length)
308 this.timeSlices.forEach(callback, opt_this);
310 this.samples_.forEach(callback, opt_this);
315 * Comparison between threads that orders first by parent.compareTo,
316 * then by names, then by tid.
318 Thread.compare = function(x, y) {
319 var tmp = x.parent.compareTo(y.parent);
323 tmp = x.sortIndex - y.sortIndex;
327 tmp = tvcm.comparePossiblyUndefinedValues(
329 function(x, y) { return x.localeCompare(y); });
333 return x.tid - y.tid;
337 ThreadSlice: ThreadSlice,