blob: 49ae0b455954bd024170f43b09e1c11984494022 [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="/core/analysis/analysis_results.html">
<link rel="import" href="/core/analysis/analysis_sub_view.html">
<link rel="import" href="/core/analysis/util.html">
<link rel="import" href="/base/base.html">
<link rel="import" href="/base/ui.html">
<link rel="import" href="/base/ui/sortable_table.html">
<polymer-element name="tv-c-multi-slice-sub-view"
extends="tracing-analysis-sub-view">
<script>
'use strict';
Polymer({
created: function() {
this.currentSelection_ = undefined;
this.requiresTallView_ = false;
},
set selection(selection) {
if (selection.length <= 1)
throw new Error('Only supports multiple items');
if (!selection.every(
function(x) { return x instanceof tv.c.trace_model.Slice; })) {
throw new Error('Only supports slices');
}
this.setSelectionWithoutErrorChecks(selection);
},
get selection() {
return this.currentSelection_;
},
get requiresTallView() {
return this.requiresTallView_;
},
setSelectionWithoutErrorChecks: function(selection) {
this.currentSelection_ = selection;
this.textContent = '';
this.requiresTallView_ = false;
// TODO(nduca): This is a gross hack for cc Frame Viewer, but its only
// the frame viewer that needs this feature, so ~shrug~.
if (window.RasterTaskView !== undefined) { // May not have been imported.
if (tv.e.cc.RasterTaskSelection.supports(selection)) {
var ltvSelection = new tv.e.cc.RasterTaskSelection(selection);
var ltv = new tv.e.cc.LayerTreeHostImplSnapshotView();
ltv.objectSnapshot = ltvSelection.containingSnapshot;
ltv.selection = ltvSelection;
ltv.extraHighlightsByLayerId = ltvSelection.extraHighlightsByLayerId;
this.appendChild(ltv);
this.style.display = 'flex';
this.requiresTallView_ = true;
return;
}
}
this.style.display = '';
var results = new tv.c.analysis.AnalysisResults();
this.appendChild(results);
this.analyzeMultipleSlices_(results, selection);
},
analyzeSingleTypeSlices_: function(results, sliceGroup, hasCpuDuration) {
results.appendInfo('Title: ', sliceGroup[0].title);
results.appendInfo('Category: ', sliceGroup[0].category);
var table = results.appendTable('analysis-slice-table',
4 + hasCpuDuration);
var row = results.appendHeadRow(table);
results.appendTableCell(table, row, 'Start');
results.appendTableCell(table, row, 'Wall Duration (ms)');
if (hasCpuDuration)
results.appendTableCell(table, row, 'CPU Duration (ms)');
results.appendTableCell(table, row, 'Self Time (ms)');
results.appendTableCell(table, row, 'Args');
var numSlices = 0;
var totalArg = {};
var totalDuration = 0;
var totalSelfTime = 0;
var totalCpuDuration = 0;
tv.b.iterItems(sliceGroup, function(title, slice) {
numSlices++;
results.appendDetailsRow(table, slice.start, slice.duration,
slice.selfTime ? slice.selfTime : slice.duration, slice.args,
function() {
return new tv.c.Selection([slice]);
}, slice.cpuDuration, false);
// Track numeric totals to report at the foot.
totalDuration += slice.duration;
totalSelfTime += slice.selfTime ? slice.selfTime : slice.duration;
if (hasCpuDuration)
totalCpuDuration += slice.cpuDuration;
for (var argName in slice.args) {
var argVal = slice.args[argName];
var type = (typeof argVal);
if (type == 'number') {
if (totalArg[argName] == null)
totalArg[argName] = 0;
totalArg[argName] += argVal;
}
}
});
if (numSlices > 1) {
results.appendDetailsRow(table, undefined, totalDuration, totalSelfTime,
totalArg, undefined, hasCpuDuration ? totalCpuDuration : undefined,
true);
tv.b.ui.SortableTable.decorate(table);
}
},
analyzeMultipleSlices_: function(results, slices) {
var tsLo = slices.bounds.min;
var tsHi = slices.bounds.max;
var numTitles = 0;
var sliceGroups = {};
var hasCpuDuration = false;
for (var i = 0; i < slices.length; i++) {
var slice = slices[i];
if (sliceGroups[slice.title] === undefined) {
sliceGroups[slice.title] = [];
numTitles++;
}
if (slice.cpuDuration)
hasCpuDuration = true;
var sliceGroup = sliceGroups[slice.title];
sliceGroup.push(slices[i]);
}
var table = results.appendTable('analysis-slice-table',
4 + hasCpuDuration);
var row = results.appendHeadRow(table);
results.appendTableCell(table, row, 'Name');
results.appendTableCell(table, row, 'Wall Duration (ms)');
if (hasCpuDuration)
results.appendTableCell(table, row, 'CPU Duration (ms)');
results.appendTableCell(table, row, 'Self Time (ms)');
if (hasCpuDuration)
results.appendTableCell(table, row, 'CPU Self Time (ms)');
results.appendTableCell(table, row, 'Occurrences');
var thisComponent = this;
var totalDuration = 0;
var totalCpuDuration = 0;
var totalSelfTime = 0;
var totalCpuSelfTime = 0;
tv.b.iterItems(sliceGroups, function(sliceGroupTitle, sliceGroup) {
var duration = 0;
var cpuDuration = 0;
var selfTime = 0;
var cpuSelfTime = 0;
var avg = 0;
var startOfFirstOccurrence = Number.MAX_VALUE;
var startOfLastOccurrence = -Number.MAX_VALUE;
var min = Number.MAX_VALUE;
var max = -Number.MAX_VALUE;
for (var i = 0; i < sliceGroup.length; i++) {
var slice = sliceGroup[i];
duration += slice.duration;
if (slice.cpuDuration) {
cpuDuration += slice.cpuDuration;
cpuSelfTime += slice.cpuSelfTime ? slice.cpuSelfTime :
slice.cpuDuration;
}
selfTime += slice.selfTime ? slice.selfTime : slice.duration;
startOfFirstOccurrence = Math.min(
slice.start, startOfFirstOccurrence);
startOfLastOccurrence = Math.max(
slice.start, startOfLastOccurrence);
min = Math.min(slice.duration, min);
max = Math.max(slice.duration, max);
}
totalDuration += duration;
totalCpuDuration += cpuDuration;
totalSelfTime += selfTime;
totalCpuSelfTime += cpuSelfTime;
if (sliceGroup.length == 0)
avg = 0;
avg = duration / sliceGroup.length;
var statistics = {
min: min,
max: max,
avg: avg,
avg_stddev: undefined,
frequency: undefined,
frequency_stddev: undefined
};
// Compute the stddev of the slice durations.
var sumOfSquaredDistancesToMean = 0;
for (var i = 0; i < sliceGroup.length; i++) {
var signedDistance = statistics.avg - sliceGroup[i].duration;
sumOfSquaredDistancesToMean += signedDistance * signedDistance;
}
statistics.avg_stddev =
Math.sqrt(sumOfSquaredDistancesToMean / (sliceGroup.length - 1));
// We require at least 3 samples to compute the stddev.
var elapsed = startOfLastOccurrence - startOfFirstOccurrence;
if (sliceGroup.length > 2 && elapsed > 0) {
var numDistances = sliceGroup.length - 1;
statistics.frequency = (1000 * numDistances) / elapsed;
// Compute the stddev.
sumOfSquaredDistancesToMean = 0;
for (var i = 1; i < sliceGroup.length; i++) {
var currentFrequency =
1000 / (sliceGroup[i].start - sliceGroup[i - 1].start);
var signedDistance = statistics.frequency - currentFrequency;
sumOfSquaredDistancesToMean += signedDistance * signedDistance;
}
statistics.frequency_stddev =
Math.sqrt(sumOfSquaredDistancesToMean / (numDistances - 1));
}
results.appendDataRow(table, sliceGroupTitle, duration,
hasCpuDuration ? (cpuDuration > 0 ?
cpuDuration : '') : null,
selfTime,
hasCpuDuration ? (cpuSelfTime > 0 ?
cpuSelfTime : '') : null,
sliceGroup.length, null, statistics, function() {
return new tv.c.Selection(sliceGroup);
});
// The whole selection is a single type so list out the information
// for each sub slice.
if (numTitles === 1)
thisComponent.analyzeSingleTypeSlices_(results, sliceGroup,
hasCpuDuration);
});
// Only one row so we already know the totals.
if (numTitles !== 1) {
results.appendDataRow(table, 'Totals', totalDuration,
hasCpuDuration ? totalCpuDuration : null,
totalSelfTime,
hasCpuDuration ? totalCpuSelfTime : null,
slices.length,
null, null, null, true);
results.appendSpacingRow(table, true);
tv.b.ui.SortableTable.decorate(table);
}
results.appendInfoRowTime(table, 'Selection start', tsLo, true);
results.appendInfoRowTime(table, 'Selection extent', tsHi - tsLo, true);
}
});
</script>
</polymer>