blob: e921d670b40a57a08af084beee6aa310460612de [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright (c) 2013 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.
-->
<link rel="import" href="/base/range.html">
<link rel="import" href="/model/counter.html">
<link rel="import" href="/model/cpu_slice.html">
<link rel="import" href="/model/thread_time_slice.html">
<script>
'use strict';
/**
* @fileoverview Provides the Cpu class.
*/
tr.exportTo('tr.model', function() {
var Counter = tr.model.Counter;
var Slice = tr.model.Slice;
var CpuSlice = tr.model.CpuSlice;
/**
* The Cpu represents a Cpu from the kernel's point of view.
* @constructor
*/
function Cpu(kernel, number) {
if (kernel === undefined || number === undefined)
throw new Error('Missing arguments');
this.kernel = kernel;
this.cpuNumber = number;
this.slices = [];
this.counters = {};
this.bounds = new tr.b.Range();
this.samples_ = undefined; // Set during createSubSlices
// Start timestamp of the last active thread.
this.lastActiveTimestamp_ = undefined;
// Identifier of the last active thread. On Linux, it's a pid while on
// Windows it's a thread id.
this.lastActiveThread_ = undefined;
// Name and arguments of the last active thread.
this.lastActiveName_ = undefined;
this.lastActiveArgs_ = undefined;
};
Cpu.prototype = {
get samples() {
return this.samples_;
},
get userFriendlyName() {
return 'CPU ' + this.cpuNumber;
},
iterateAllEventsInThisContainer: function(eventTypePredicate,
callback, opt_this) {
if (eventTypePredicate.call(opt_this, tr.model.CpuSlice))
this.slices.forEach(callback, opt_this);
if (this.samples_) {
if (eventTypePredicate.call(opt_this, tr.model.Sample))
this.samples_.forEach(callback, opt_this);
}
},
iterateAllChildEventContainers: function(callback, opt_this) {
for (var id in this.counters)
callback.call(opt_this, this.counters[id]);
},
/**
* @return {Counter} The counter on this CPU with the given category/name
* combination, creating it if it doesn't exist.
*/
getOrCreateCounter: function(cat, name) {
var id = cat + '.' + name;
if (!this.counters[id])
this.counters[id] = new Counter(this, id, cat, name);
return this.counters[id];
},
/**
* Shifts all the timestamps inside this CPU forward by the amount
* specified.
*/
shiftTimestampsForward: function(amount) {
for (var sI = 0; sI < this.slices.length; sI++)
this.slices[sI].start = (this.slices[sI].start + amount);
for (var id in this.counters)
this.counters[id].shiftTimestampsForward(amount);
},
/**
* Updates the range based on the current slices attached to the cpu.
*/
updateBounds: function() {
this.bounds.reset();
if (this.slices.length) {
this.bounds.addValue(this.slices[0].start);
this.bounds.addValue(this.slices[this.slices.length - 1].end);
}
for (var id in this.counters) {
this.counters[id].updateBounds();
this.bounds.addRange(this.counters[id].bounds);
}
if (this.samples_ && this.samples_.length) {
this.bounds.addValue(this.samples_[0].start);
this.bounds.addValue(
this.samples_[this.samples_.length - 1].end);
}
},
createSubSlices: function() {
this.samples_ = this.kernel.model.samples.filter(function(sample) {
return sample.cpu == this;
}, this);
},
addCategoriesToDict: function(categoriesDict) {
for (var i = 0; i < this.slices.length; i++)
categoriesDict[this.slices[i].category] = true;
for (var id in this.counters)
categoriesDict[this.counters[id].category] = true;
for (var i = 0; i < this.samples_.length; i++)
categoriesDict[this.samples_[i].category] = true;
},
/*
* Returns the index of the slice in the CPU's slices, or undefined.
*/
indexOf: function(cpuSlice) {
var i = tr.b.findLowIndexInSortedArray(
this.slices,
function(slice) { return slice.start; },
cpuSlice.start);
if (this.slices[i] !== cpuSlice)
return undefined;
return i;
},
/**
* Closes the thread running on the CPU. |end_timestamp| is the timestamp
* at which the thread was unscheduled. |args| is merged with the arguments
* specified when the thread was initially scheduled.
*/
closeActiveThread: function(end_timestamp, args) {
// Don't generate a slice if the last active thread is the idle task.
if (this.lastActiveThread_ == undefined || this.lastActiveThread_ == 0)
return;
if (end_timestamp < this.lastActiveTimestamp_) {
throw new Error('The end timestamp of a thread running on CPU ' +
this.cpuNumber + ' is before its start timestamp.');
}
// Merge |args| with |this.lastActiveArgs_|. If a key is in both
// dictionaries, the value from |args| is used.
for (var key in args) {
this.lastActiveArgs_[key] = args[key];
}
var duration = end_timestamp - this.lastActiveTimestamp_;
var slice = new tr.model.CpuSlice(
'', this.lastActiveName_,
tr.ui.b.getColorIdForGeneralPurposeString(this.lastActiveName_),
this.lastActiveTimestamp_,
this.lastActiveArgs_,
duration);
slice.cpu = this;
this.slices.push(slice);
// Clear the last state.
this.lastActiveTimestamp_ = undefined;
this.lastActiveThread_ = undefined;
this.lastActiveName_ = undefined;
this.lastActiveArgs_ = undefined;
},
switchActiveThread: function(timestamp, old_thread_args, new_thread_id,
new_thread_name, new_thread_args) {
// Close the previous active thread and generate a slice.
this.closeActiveThread(timestamp, old_thread_args);
// Keep track of the new thread.
this.lastActiveTimestamp_ = timestamp;
this.lastActiveThread_ = new_thread_id;
this.lastActiveName_ = new_thread_name;
this.lastActiveArgs_ = new_thread_args;
}
};
/**
* Comparison between processes that orders by cpuNumber.
*/
Cpu.compare = function(x, y) {
return x.cpuNumber - y.cpuNumber;
};
return {
Cpu: Cpu
};
});
</script>