blob: 0586f626f3e9d3e7ff7e9c9cb50b05c1e0c4bde0 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
/**
* @fileoverview Provides the Thread class.
*/
base.require('range');
base.require('guid');
base.require('model.slice');
base.require('model.slice_group');
base.require('model.async_slice_group');
base.require('model.sample');
base.exportTo('tracing.model', function() {
var Slice = tracing.model.Slice;
var SliceGroup = tracing.model.SliceGroup;
var AsyncSlice = tracing.model.AsyncSlice;
var AsyncSliceGroup = tracing.model.AsyncSliceGroup;
/**
* A ThreadSlice represents an interval of time on a thread resource
* with associated nestinged slice information.
*
* ThreadSlices are typically associated with a specific trace event pair on a
* specific thread.
* For example,
* TRACE_EVENT_BEGIN1("x","myArg", 7) at time=0.1ms
* TRACE_EVENT_END0() at time=0.3ms
* This results in a single slice from 0.1 with duration 0.2 on a
* specific thread.
*
* @constructor
*/
function ThreadSlice(cat, title, colorId, start, args, opt_duration) {
Slice.call(this, cat, title, colorId, start, args, opt_duration);
// Do not modify this directly.
// subSlices is configured by SliceGroup.rebuildSubRows_.
this.subSlices = [];
}
ThreadSlice.prototype = {
__proto__: Slice.prototype
};
/**
* A Thread stores all the trace events collected for a particular
* thread. We organize the synchronous slices on a thread by "subrows," where
* subrow 0 has all the root slices, subrow 1 those nested 1 deep, and so on.
* The asynchronous slices are stored in an AsyncSliceGroup object.
*
* The slices stored on a Thread should be instances of
* ThreadSlice.
*
* @constructor
*/
function Thread(parent, tid) {
SliceGroup.call(this, ThreadSlice);
this.guid_ = tracing.GUID.allocate();
if (!parent)
throw new Error('Parent must be provided.');
this.parent = parent;
this.tid = tid;
this.cpuSlices = undefined;
this.samples_ = [];
this.kernelSlices = new SliceGroup();
this.asyncSlices = new AsyncSliceGroup();
this.bounds = new base.Range();
}
Thread.prototype = {
__proto__: SliceGroup.prototype,
/*
* @return {Number} A globally unique identifier for this counter.
*/
get guid() {
return this.guid_;
},
compareTo: function(that) {
return Thread.compare(this, that);
},
toJSON: function() {
var obj = new Object();
var keys = Object.keys(this);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (typeof this[key] == 'function')
continue;
if (key == 'parent') {
obj[key] = this[key].guid;
continue;
}
obj[key] = this[key];
}
return obj;
},
/**
* Adds a new sample in the thread's samples.
*
* Calls to addSample must be made with non-monotonically-decreasing
* timestamps.
*
* @param {String} category Category of the sample to add.
* @param {String} title Title of the sample to add.
* @param {Number} ts The timetsamp of the sample, in milliseconds.
* @param {Object.<string, Object>} opt_args Arguments associated with
* the sample.
*/
addSample: function(category, title, ts, opt_args) {
if (this.samples_.length) {
var lastSample = this.samples_[this.samples_.length - 1];
if (ts < lastSample.start) {
throw new
Error('Samples must be added in increasing timestamp order.');
}
}
var colorId = tracing.getStringColorId(title);
var sample = new tracing.model.Sample(category, title, colorId, ts,
opt_args ? opt_args : {});
this.samples_.push(sample);
return sample;
},
/**
* Returns the array of samples added to this thread. If no samples
* have been added, an empty array is returned.
*
* @return {Array<Sample>} array of samples.
*/
get samples() {
return this.samples_;
},
/**
* Name of the thread, if present.
*/
name: undefined,
/**
* Shifts all the timestamps inside this thread forward by the amount
* specified.
*/
shiftTimestampsForward: function(amount) {
SliceGroup.prototype.shiftTimestampsForward.call(this, amount);
if (this.cpuSlices) {
for (var i = 0; i < this.cpuSlices.length; i++) {
var slice = this.cpuSlices[i];
slice.start += amount;
}
}
if (this.samples_.length) {
for (var i = 0; i < this.samples_.length; i++) {
var sample = this.samples_[i];
sample.start += amount;
}
}
this.kernelSlices.shiftTimestampsForward(amount);
this.asyncSlices.shiftTimestampsForward(amount);
},
/**
* Determins whether this thread is empty. If true, it usually implies
* that it should be pruned from the model.
*/
get isEmpty() {
if (this.slices.length)
return false;
if (this.openSliceCount)
return false;
if (this.cpuSlices && this.cpuSlices.length)
return false;
if (this.kernelSlices.length)
return false;
if (this.asyncSlices.length)
return false;
if (this.samples_.length)
return false;
return true;
},
/**
* Updates the bounds based on the
* current objects associated with the thread.
*/
updateBounds: function() {
SliceGroup.prototype.updateBounds.call(this);
this.kernelSlices.updateBounds();
this.bounds.addRange(this.kernelSlices.bounds);
this.asyncSlices.updateBounds();
this.bounds.addRange(this.asyncSlices.bounds);
if (this.cpuSlices && this.cpuSlices.length) {
this.bounds.addValue(this.cpuSlices[0].start);
this.bounds.addValue(
this.cpuSlices[this.cpuSlices.length - 1].end);
}
if (this.samples_.length) {
this.bounds.addValue(this.samples_[0].start);
this.bounds.addValue(
this.samples_[this.samples_.length - 1].end);
}
},
addCategoriesToDict: function(categoriesDict) {
for (var i = 0; i < this.slices.length; i++)
categoriesDict[this.slices[i].category] = true;
for (var i = 0; i < this.kernelSlices.length; i++)
categoriesDict[this.kernelSlices.slices[i].category] = true;
for (var i = 0; i < this.asyncSlices.length; i++)
categoriesDict[this.asyncSlices.slices[i].category] = true;
for (var i = 0; i < this.samples_.length; i++)
categoriesDict[this.samples_[i].category] = true;
},
mergeKernelWithUserland: function() {
if (this.kernelSlices.length > 0) {
var newSlices = SliceGroup.merge(this, this.kernelSlices);
this.slices = newSlices.slices;
this.kernelSlices = new SliceGroup();
this.updateBounds();
}
},
/**
* @return {String} A user-friendly name for this thread.
*/
get userFriendlyName() {
var tname = this.name || this.tid;
return this.parent.userFriendlyName + ': ' + tname;
},
/**
* @return {String} User friendly details about this thread.
*/
get userFriendlyDetails() {
return this.parent.userFriendlyDetails +
', tid: ' + this.tid +
(this.name ? ', name: ' + this.name : '');
}
};
/**
* Comparison between threads that orders first by parent.compareTo,
* then by names, then by tid.
*/
Thread.compare = function(x, y) {
var tmp = x.parent.compareTo(y.parent);
if (tmp != 0)
return tmp;
if (x.name && y.name) {
var tmp = x.name.localeCompare(y.name);
if (tmp == 0)
return x.tid - y.tid;
return tmp;
} else if (x.name) {
return -1;
} else if (y.name) {
return 1;
} else {
return x.tid - y.tid;
}
};
return {
ThreadSlice: ThreadSlice,
Thread: Thread
};
});