| <!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> |