| <!DOCTYPE html> |
| <!-- |
| Copyright (c) 2015 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="/perf_insights/mappers/slice_cost.html"> |
| <link rel="import" href="/perf_insights/ui/caching_column.html"> |
| <link rel="import" href="/perf_insights/ui/reports/pi_report.html"> |
| <link rel="import" href="/tracing/ui/base/dom_helpers.html"> |
| <link rel="import" href="/tracing/ui/base/grouping_table.html"> |
| <link rel="import" href="/tracing/ui/base/info_bar_group.html"> |
| <link rel="import" href="/tracing/ui/base/overlay.html"> |
| <link rel="import" href="/tracing/ui/base/table.html"> |
| <link rel="import" href="/tracing/value/ui/histogram_span.html"> |
| <link rel="import" href="/tracing/value/ui/scalar_span.html"> |
| <link rel="import" href="/tracing/value/unit.html"> |
| |
| <polymer-element name="pi-ui-r-startup-report" |
| extends="pi-ui-r-pi-report" |
| map-function-href="/perf_insights/mappers/startup_map_function.html" |
| map-function-name="startupMapFunction" |
| <template> |
| <style> |
| :host { |
| display: flex; |
| flex-direction: column; |
| } |
| |
| top-controls { |
| display: flex; |
| flex: 0 0 auto; |
| flex-flow: wrap; |
| background-color: rgb(236, 236, 236); |
| border-bottom: 1px solid #8e8e8e; |
| padding: 4px; |
| } |
| |
| content-pane { |
| min-height: 0; |
| display: flex; |
| flex-direction: row; |
| } |
| |
| table-container { |
| flex: 1 1 60%; |
| display: flex; |
| overflow: auto; |
| } |
| #tableLeft { |
| flex: 1 1 60%; |
| } |
| #tableRight { |
| flex: 1 1 60%; |
| } |
| |
| pane { |
| border-left: 1px solid black; |
| display: flex; |
| flex-direction: column; |
| flex: 1 1 40%; |
| } |
| |
| #links { |
| min-height: 0; |
| overflow: auto; |
| } |
| </style> |
| <top-controls> |
| Slow trace threshold : <input id="threshold"></input>ms |
| </top-controls> |
| <content-pane> |
| <pane> |
| <div>Fast startup traces (<span id="numFast"></span> traces)</div> |
| <table-container> |
| <tr-ui-b-grouping-table id="tableFast"></tr-ui-b-grouping-table> |
| </table-container> |
| </pane> |
| <pane> |
| <div>Slow startup traces (<span id="numSlow"></span> traces)</div> |
| <table-container> |
| <tr-ui-b-grouping-table id="tableSlow"></tr-ui-b-grouping-table> |
| </table-container> |
| </pane> |
| </content-pane> |
| </template> |
| </polymer-element> |
| |
| |
| <script> |
| 'use strict'; |
| |
| (function() { |
| var DEFAULT_SLOW_THRESHOLD_MS = 3500; |
| |
| Polymer('pi-ui-r-startup-report', { |
| created: function() { |
| this.mapResults_ = undefined; |
| this.slowThresholdMs_ = DEFAULT_SLOW_THRESHOLD_MS; |
| this.copyingState_ = false; // True when copying state between tables. |
| }, |
| |
| ready: function() { |
| this.$.threshold.value = DEFAULT_SLOW_THRESHOLD_MS; |
| |
| this.$.tableFast.addEventListener('selection-changed', |
| this.copySelectedRow_.bind(this, this.$.tableFast, this.$.tableSlow)); |
| this.$.tableSlow.addEventListener('selection-changed', |
| this.copySelectedRow_.bind(this, this.$.tableSlow, this.$.tableFast)); |
| this.$.tableFast.addEventListener('sort-column-changed', |
| this.copySortingState_.bind(this, this.$.tableSlow)); |
| this.$.tableSlow.addEventListener('sort-column-changed', |
| this.copySortingState_.bind(this, this.$.tableFast)); |
| this.$.threshold.addEventListener( |
| 'change', this.onThresholdChanged_.bind(this)); |
| }, |
| |
| get mapResults() { |
| return this.mapResults_; |
| }, |
| |
| set mapResults(mapResults) { |
| this.mapResults_ = mapResults; |
| this.updateContents_(); |
| }, |
| |
| |
| // Looks at the selected row in |fromTable| and select the matching one in |
| // |toTable|. |
| copySelectedRow_: function(fromTable, toTable) { |
| // Block reentrant calls. |
| if (this.copyingState_) |
| return; |
| this.copyingState_ = true; |
| var rowToSelect = undefined; |
| var selectedRow = fromTable.selectedTableRow; |
| if (selectedRow !== undefined) { |
| toTable.tableRows.forEach(function(row) { |
| if (row.title === selectedRow.title) |
| rowToSelect = row; |
| }); |
| } |
| toTable.selectedTableRow = rowToSelect; |
| this.copyingState_ = false; |
| }, |
| |
| // Apply the sorting state of |fromTable| to |toTable|. |
| copySortingState_: function(toTable, event) { |
| // Block reentrant calls. |
| if (this.copyingState_) |
| return; |
| this.copyingState_ = true; |
| if (toTable.tableColumns.length >= event.sortColumnIndex) { |
| toTable.sortColumnIndex = event.sortColumnIndex; |
| toTable.sortDescending = event.sortDescending; |
| } |
| this.copyingState_ = false; |
| }, |
| |
| onThresholdChanged_: function(event) { |
| var value = parseInt(this.$.threshold.value); |
| if (!Number.isInteger(value)) |
| value = DEFAULT_SLOW_THRESHOLD_MS; |
| this.$.threshold.value = value; |
| this.slowThresholdMs_ = value; |
| this.updateContents_(); |
| }, |
| |
| updateContents_: function() { |
| this.updateTable_(this.$.tableFast, this.$.numFast, function(value) { |
| return value.startupDuration < this.slowThresholdMs_; |
| }.bind(this)); |
| this.updateTable_(this.$.tableSlow, this.$.numSlow, function(value) { |
| return value.startupDuration >= this.slowThresholdMs_; |
| }.bind(this)); |
| }, |
| |
| updateTable_: function(table, numTracesSpan, filter) { |
| var results = this.mapResults_; |
| if (!results) |
| results = []; |
| |
| var allSliceCosts = []; |
| var numTraces = 0; |
| var totalStartupDuration = 0; |
| results.forEach(function(result) { |
| var sr = result.pairs.sr; |
| if (sr === undefined) |
| return; |
| numTraces++; |
| totalStartupDuration += sr.startupDuration; |
| sr.sliceCosts.forEach(function(item) { |
| var sliceCostInfo = pi.m.SliceCostInfo.fromDict(item); |
| allSliceCosts.push({ |
| canonicalUrl: result.pairs.canonical_url, |
| sliceCostInfo: sliceCostInfo |
| }); |
| }); |
| }); |
| numTracesSpan.innerText = numTraces; |
| |
| var columns = this.createColumns_(totalStartupDuration); |
| table.tableColumns = columns; |
| table.sortColumnIndex = 2; |
| table.sortDescending = true; |
| |
| var groupBy = []; |
| groupBy.push(function(datum) { |
| return datum.sliceCostInfo.title; |
| }); |
| table.selectionMode = tr.ui.b.TableFormat.SelectionMode.ROW; |
| table.groupBy = groupBy; |
| table.dataToGroup = allSliceCosts; |
| table.rebuild(); |
| }, |
| |
| createColumns_: function(totalStartupDuration) { |
| var columns = [ |
| { |
| title: 'Title', |
| value: function(row) { |
| return row.title; |
| }, |
| cmp: function(a, b) { |
| return a.title.localeCompare(b.title); |
| }, |
| width: '500px' |
| }, |
| this.createCachingColumn_('Relative self time', totalStartupDuration, |
| function(datum) { |
| return datum.sliceCostInfo.selfTime; |
| }), |
| this.createCachingColumn_('Relative CPU time', totalStartupDuration, |
| function(datum) { |
| return datum.sliceCostInfo.cpuSelfTime; |
| }) |
| ]; |
| return columns; |
| }, |
| |
| createCachingColumn_(title, totalStartupDuration, getDataFunction) { |
| function computeStats(sliceCostInfo) { |
| var mean = tr.b.Statistics.sum(sliceCostInfo, getDataFunction); |
| if (mean == undefined || totalStartupDuration == 0) |
| return undefined; |
| mean /= totalStartupDuration; |
| var span = tr.ui.units.createScalarSpan(mean); |
| span.unit = tr.v.Unit.byName.normalizedPercentage; |
| span.percentage = mean; |
| return span; |
| } |
| |
| var column = new pi.ui.CachingColumn(title, computeStats); |
| column.textAlign = 'right'; |
| column.cmp = function(row0, row1) { |
| var value0 = column.value(row0); |
| var value1 = column.value(row1); |
| return tr.b.comparePossiblyUndefinedValues(value0, value1, |
| function(v0, v1) { |
| return v0.value - v1.value; |
| }); |
| }; |
| return column; |
| } |
| }); |
| })(); |
| </script> |