blob: 1cac3ff03c6136eb4deb80973581e10a121ca400 [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="stylesheet"
href="/ui/extras/system_stats/system_stats_instance_track.css">
<link rel="import" href="/base/sorted_array_utils.html">
<link rel="import" href="/ui/tracks/stacked_bars_track.html">
<link rel="import" href="/ui/tracks/object_instance_track.html">
<link rel="import" href="/ui/base/event_presenter.html">
<link rel="import" href="/ui/base/ui.html">
<script>
'use strict';
tr.exportTo('tr.ui.e.system_stats', function() {
var EventPresenter = tr.ui.b.EventPresenter;
var palette = tr.ui.b.getColorPalette();
var highlightIdBoost = tr.ui.b.getColorPaletteHighlightIdBoost();
var statCount;
var excludedStats = {'meminfo': {
'pswpin': 0,
'pswpout': 0,
'pgmajfault': 0},
'diskinfo': {
'io': 0,
'io_time': 0,
'read_time': 0,
'reads': 0,
'reads_merged': 0,
'sectors_read': 0,
'sectors_written': 0,
'weighted_io_time': 0,
'write_time': 0,
'writes': 0,
'writes_merged': 0},
'swapinfo': {}
};
/**
* Tracks that display system stats data.
*
* @constructor
* @extends {StackedBarsTrack}
*/
var SystemStatsInstanceTrack = tr.ui.b.define(
'tr-ui-e-system-stats-instance-track', tr.ui.tracks.StackedBarsTrack);
SystemStatsInstanceTrack.prototype = {
__proto__: tr.ui.tracks.StackedBarsTrack.prototype,
decorate: function(viewport) {
tr.ui.tracks.StackedBarsTrack.prototype.decorate.call(this, viewport);
this.classList.add('tr-ui-e-system-stats-instance-track');
this.objectInstance_ = null;
},
set objectInstances(objectInstances) {
if (!objectInstances) {
this.objectInstance_ = [];
return;
}
if (objectInstances.length != 1)
throw new Error('Bad object instance count.');
this.objectInstance_ = objectInstances[0];
if (this.objectInstance_ !== null) {
this.computeRates_(this.objectInstance_.snapshots);
this.maxStats_ = this.computeMaxStats_(
this.objectInstance_.snapshots);
}
},
computeRates_: function(snapshots) {
for (var i = 0; i < snapshots.length; i++) {
var snapshot = snapshots[i];
var stats = snapshot.getStats();
var prevSnapshot;
var prevStats;
if (i == 0) {
// Deltas will be zero.
prevSnapshot = snapshots[0];
} else {
prevSnapshot = snapshots[i - 1];
}
prevStats = prevSnapshot.getStats();
var timeIntervalSeconds = (snapshot.ts - prevSnapshot.ts) / 1000;
// Prevent divide by zero.
if (timeIntervalSeconds == 0)
timeIntervalSeconds = 1;
this.computeRatesRecursive_(prevStats, stats,
timeIntervalSeconds);
}
},
computeRatesRecursive_: function(prevStats, stats,
timeIntervalSeconds) {
for (var statName in stats) {
if (stats[statName] instanceof Object) {
this.computeRatesRecursive_(prevStats[statName],
stats[statName],
timeIntervalSeconds);
} else {
if (statName == 'sectors_read') {
stats['bytes_read_per_sec'] = (stats['sectors_read'] -
prevStats['sectors_read']) *
512 / timeIntervalSeconds;
}
if (statName == 'sectors_written') {
stats['bytes_written_per_sec'] =
(stats['sectors_written'] -
prevStats['sectors_written']) *
512 / timeIntervalSeconds;
}
if (statName == 'pgmajfault') {
stats['pgmajfault_per_sec'] = (stats['pgmajfault'] -
prevStats['pgmajfault']) /
timeIntervalSeconds;
}
if (statName == 'pswpin') {
stats['bytes_swpin_per_sec'] = (stats['pswpin'] -
prevStats['pswpin']) *
1000 / timeIntervalSeconds;
}
if (statName == 'pswpout') {
stats['bytes_swpout_per_sec'] = (stats['pswpout'] -
prevStats['pswpout']) *
1000 / timeIntervalSeconds;
}
}
}
},
computeMaxStats_: function(snapshots) {
var maxStats = new Object();
statCount = 0;
for (var i = 0; i < snapshots.length; i++) {
var snapshot = snapshots[i];
var stats = snapshot.getStats();
this.computeMaxStatsRecursive_(stats, maxStats,
excludedStats);
}
return maxStats;
},
computeMaxStatsRecursive_: function(stats, maxStats, excludedStats) {
for (var statName in stats) {
if (stats[statName] instanceof Object) {
if (!(statName in maxStats))
maxStats[statName] = new Object();
var excludedNested;
if (excludedStats && statName in excludedStats)
excludedNested = excludedStats[statName];
else
excludedNested = null;
this.computeMaxStatsRecursive_(stats[statName],
maxStats[statName],
excludedNested);
} else {
if (excludedStats && statName in excludedStats)
continue;
if (!(statName in maxStats)) {
maxStats[statName] = 0;
statCount++;
}
if (stats[statName] > maxStats[statName])
maxStats[statName] = stats[statName];
}
}
},
get height() {
return window.getComputedStyle(this).height;
},
set height(height) {
this.style.height = height;
},
draw: function(type, viewLWorld, viewRWorld) {
switch (type) {
case tr.ui.tracks.DrawType.GENERAL_EVENT:
this.drawStatBars_(viewLWorld, viewRWorld);
break;
}
},
drawStatBars_: function(viewLWorld, viewRWorld) {
var ctx = this.context();
var pixelRatio = window.devicePixelRatio || 1;
var bounds = this.getBoundingClientRect();
var width = bounds.width * pixelRatio;
var height = (bounds.height * pixelRatio) / statCount;
// Culling parameters.
var vp = this.viewport.currentDisplayTransform;
// Scale by the size of the largest snapshot.
var maxStats = this.maxStats_;
var objectSnapshots = this.objectInstance_.snapshots;
var lowIndex = tr.b.findLowIndexInSortedArray(
objectSnapshots,
function(snapshot) {
return snapshot.ts;
},
viewLWorld);
// Assure that the stack with the left edge off screen still gets drawn
if (lowIndex > 0)
lowIndex -= 1;
for (var i = lowIndex; i < objectSnapshots.length; ++i) {
var snapshot = objectSnapshots[i];
var trace = snapshot.getStats();
var currentY = height;
var left = snapshot.ts;
if (left > viewRWorld)
break;
var leftView = vp.xWorldToView(left);
if (leftView < 0)
leftView = 0;
// Compute the edges for the column graph bar.
var right;
if (i != objectSnapshots.length - 1) {
right = objectSnapshots[i + 1].ts;
} else {
// If this is the last snaphot of multiple snapshots, use the width of
// the previous snapshot for the width.
if (objectSnapshots.length > 1)
right = objectSnapshots[i].ts + (objectSnapshots[i].ts -
objectSnapshots[i - 1].ts);
else
// If there's only one snapshot, use max bounds as the width.
right = this.objectInstance_.parent.model.bounds.max;
}
var rightView = vp.xWorldToView(right);
if (rightView > width)
rightView = width;
// Floor the bounds to avoid a small gap between stacks.
leftView = Math.floor(leftView);
rightView = Math.floor(rightView);
// Descend into nested stats.
this.drawStatBarsRecursive_(snapshot,
leftView,
rightView,
height,
trace,
maxStats,
currentY);
if (i == lowIndex)
this.drawStatNames_(leftView, height, currentY, '', maxStats);
}
ctx.lineWidth = 1;
},
drawStatBarsRecursive_: function(snapshot,
leftView,
rightView,
height,
stats,
maxStats,
currentY) {
var ctx = this.context();
for (var statName in maxStats) {
if (stats[statName] instanceof Object) {
// Use the y-position returned from the recursive call.
currentY = this.drawStatBarsRecursive_(snapshot,
leftView,
rightView,
height,
stats[statName],
maxStats[statName],
currentY);
} else {
var maxStat = maxStats[statName];
// Draw a bar for the stat. The height of the bar is scaled
// against the largest value of the stat across all snapshots.
ctx.fillStyle = EventPresenter.getBarSnapshotColor(
snapshot, Math.round(currentY / height));
var barHeight;
if (maxStat > 0) {
barHeight = height * Math.max(stats[statName], 0) / maxStat;
} else {
barHeight = 0;
}
ctx.fillRect(leftView, currentY - barHeight,
Math.max(rightView - leftView, 1), barHeight);
currentY += height;
}
}
// Return the updated y-position.
return currentY;
},
drawStatNames_: function(leftView, height, currentY, prefix, maxStats) {
var ctx = this.context();
ctx.textAlign = 'end';
ctx.font = '12px Arial';
ctx.fillStyle = '#000000';
for (var statName in maxStats) {
if (maxStats[statName] instanceof Object) {
currentY = this.drawStatNames_(leftView, height, currentY,
statName, maxStats[statName]);
} else {
var fullname = statName;
if (prefix != '')
fullname = prefix + ' :: ' + statName;
ctx.fillText(fullname, leftView - 10, currentY - height / 4);
currentY += height;
}
}
return currentY;
}
};
tr.ui.tracks.ObjectInstanceTrack.register(
SystemStatsInstanceTrack,
{typeName: 'base::TraceEventSystemStatsMonitor::SystemStats'});
return {
SystemStatsInstanceTrack: SystemStatsInstanceTrack
};
});
</script>