summaryrefslogtreecommitdiff
path: root/deps/v8/tools/callstats.html
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/tools/callstats.html')
-rw-r--r--deps/v8/tools/callstats.html1809
1 files changed, 1809 insertions, 0 deletions
diff --git a/deps/v8/tools/callstats.html b/deps/v8/tools/callstats.html
new file mode 100644
index 0000000000..76cc8c686d
--- /dev/null
+++ b/deps/v8/tools/callstats.html
@@ -0,0 +1,1809 @@
+<html>
+<!--
+Copyright 2016 the V8 project authors. All rights reserved. Use of this source
+code is governed by a BSD-style license that can be found in the LICENSE file.
+-->
+
+<head>
+ <meta charset="UTF-8">
+ <style>
+ body {
+ font-family: arial;
+ }
+
+ table {
+ display: table;
+ border-spacing: 0px;
+ }
+
+ tr {
+ border-spacing: 0px;
+ padding: 10px;
+ }
+
+ td,
+ th {
+ padding: 3px 10px 3px 5px;
+ }
+
+ .inline {
+ display: inline-block;
+ vertical-align: top;
+ }
+
+ h2,
+ h3 {
+ margin-bottom: 0px;
+ }
+
+ .hidden {
+ display: none;
+ }
+
+ .view {
+ display: table;
+ }
+
+ .column {
+ display: table-cell;
+ border-right: 1px black dotted;
+ min-width: 200px;
+ }
+
+ .column .header {
+ padding: 0 10px 0 10px
+ }
+
+ #column {
+ display: none;
+ }
+
+ .list {
+ width: 100%;
+ }
+
+ select {
+ width: 100%
+ }
+
+ .list tbody {
+ cursor: pointer;
+ }
+
+ .list tr:nth-child(even) {
+ background-color: #EFEFEF;
+ }
+
+ .list tr:nth-child(even).selected {
+ background-color: #DDD;
+ }
+
+ .list tr.child {
+ display: none;
+ }
+
+ .list tr.child.visible {
+ display: table-row;
+ }
+
+ .list .child .name {
+ padding-left: 20px;
+ }
+
+ .list .parent td {
+ border-top: 1px solid #AAA;
+ }
+
+ .list .total {
+ font-weight: bold
+ }
+
+ .list tr.parent {
+ background-color: #FFF;
+ }
+
+ .list tr.parent.selected {
+ background-color: #DDD;
+ }
+
+ tr.selected {
+ background-color: #DDD;
+ }
+
+ .codeSearch {
+ display: block-inline;
+ float: right;
+ border-radius: 5px;
+ background-color: #EEE;
+ width: 1em;
+ text-align: center;
+ }
+
+ .list .position {
+ text-align: right;
+ display: none;
+ }
+
+ .list div.toggle {
+ cursor: pointer;
+ }
+
+ #column_0 .position {
+ display: table-cell;
+ }
+
+ #column_0 .name {
+ display: table-cell;
+ }
+
+ .list .name {
+ display: none;
+ white-space: nowrap;
+ }
+
+ .value {
+ text-align: right;
+ }
+
+ .selectedVersion {
+ font-weight: bold;
+ }
+
+ #baseline {
+ width: auto;
+ }
+
+ .compareSelector {
+ padding-bottom: 20px;
+ }
+
+ .pageDetailTable tbody {
+ cursor: pointer
+ }
+
+ .pageDetailTable tfoot td {
+ border-top: 1px grey solid;
+ }
+
+ #popover {
+ position: absolute;
+ transform: translateY(-50%) translateX(40px);
+ box-shadow: -2px 10px 44px -10px #000;
+ border-radius: 5px;
+ z-index: 1;
+ background-color: #FFF;
+ display: none;
+ white-space: nowrap;
+ }
+
+ #popover table {
+ position: relative;
+ z-index: 1;
+ text-align: right;
+ margin: 10px;
+ }
+ #popover td {
+ padding: 3px 0px 3px 5px;
+ white-space: nowrap;
+ }
+
+ .popoverArrow {
+ background-color: #FFF;
+ position: absolute;
+ width: 30px;
+ height: 30px;
+ transform: translateY(-50%)rotate(45deg);
+ top: 50%;
+ left: -10px;
+ z-index: 0;
+ }
+
+ #popover .name {
+ padding: 5px;
+ font-weight: bold;
+ text-align: center;
+ }
+
+ #popover table .compare {
+ display: none
+ }
+
+ #popover table.compare .compare {
+ display: table-cell;
+ }
+
+ #popover .compare .time,
+ #popover .compare .version {
+ padding-left: 10px;
+ }
+ .graph,
+ .graph .content {
+ width: 100%;
+ }
+
+ .diff .hideDiff {
+ display: none;
+ }
+ .noDiff .hideNoDiff {
+ display: none;
+ }
+ </style>
+ <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
+ <script type="text/javascript">
+ "use strict"
+ google.charts.load('current', {packages: ['corechart']});
+
+ // Did anybody say monkeypatching?
+ if (!NodeList.prototype.forEach) {
+ NodeList.prototype.forEach = function(func) {
+ for (var i = 0; i < this.length; i++) {
+ func(this[i]);
+ }
+ }
+ }
+
+ var versions;
+ var pages;
+ var selectedPage;
+ var baselineVersion;
+ var selectedEntry;
+
+ function initialize() {
+ var original = $("column");
+ var view = document.createElement('div');
+ view.id = 'view';
+ var i = 0;
+ versions.forEach((version) => {
+ if (!version.enabled) return;
+ // add column
+ var column = original.cloneNode(true);
+ column.id = "column_" + i;
+ // Fill in all versions
+ var select = column.querySelector(".version");
+ select.id = "selectVersion_" + i;
+ // add all select options
+ versions.forEach((version) => {
+ if (!version.enabled) return;
+ var option = document.createElement("option");
+ option.textContent = version.name;
+ option.version = version;
+ select.appendChild(option);
+ });
+ // Fill in all page versions
+ select = column.querySelector(".pageVersion");
+ select.id = "select_" + i;
+ // add all pages
+ versions.forEach((version) => {
+ if (!version.enabled) return;
+ var optgroup = document.createElement("optgroup");
+ optgroup.label = version.name;
+ optgroup.version = version;
+ version.forEachPage((page) => {
+ var option = document.createElement("option");
+ option.textContent = page.name;
+ option.page = page;
+ optgroup.appendChild(option);
+ });
+ select.appendChild(optgroup);
+ });
+ view.appendChild(column);
+ i++;
+ });
+ var oldView = $('view');
+ oldView.parentNode.replaceChild(view, oldView);
+
+ var select = $('baseline');
+ removeAllChildren(select);
+ select.appendChild(document.createElement('option'));
+ versions.forEach((version) => {
+ var option = document.createElement("option");
+ option.textContent = version.name;
+ option.version = version;
+ select.appendChild(option);
+ });
+ initializeToggleList(versions.versions, $('versionSelector'));
+ initializeToggleList(pages.values(), $('pageSelector'));
+ initializeToggleContentVisibility();
+ }
+
+ function initializeToggleList(items, node) {
+ var list = node.querySelector('ul');
+ removeAllChildren(list);
+ items = Array.from(items);
+ items.sort(NameComparator);
+ items.forEach((item) => {
+ var li = document.createElement('li');
+ var checkbox = document.createElement('input');
+ checkbox.type = 'checkbox';
+ checkbox.checked = item.enabled;
+ checkbox.item = item;
+ checkbox.addEventListener('click', handleToggleVersionEnable);
+ li.appendChild(checkbox);
+ li.appendChild(document.createTextNode(item.name));
+ list.appendChild(li);
+ });
+ $('results').querySelectorAll('#results > .hidden').forEach((node) => {
+ toggleCssClass(node, 'hidden', false);
+ })
+ }
+
+ function initializeToggleContentVisibility() {
+ var nodes = document.querySelectorAll('.toggleContentVisibility');
+ nodes.forEach((node) => {
+ var content = node.querySelector('.content');
+ var header = node.querySelector('h1,h2,h3');
+ if (content === undefined || header === undefined) return;
+ if (header.querySelector('input') != undefined) return;
+ var checkbox = document.createElement('input');
+ checkbox.type = 'checkbox';
+ checkbox.checked = content.className.indexOf('hidden') == -1;
+ checkbox.contentNode = content;
+ checkbox.addEventListener('click', handleToggleContentVisibility);
+ header.insertBefore(checkbox, header.childNodes[0]);
+ });
+ }
+
+ function showPage(firstPage) {
+ var changeSelectedEntry = selectedEntry !== undefined
+ && selectedEntry.page === selectedPage;
+ selectedPage = firstPage;
+ selectedPage.sort();
+ showPageInColumn(firstPage, 0);
+ // Show the other versions of this page in the following columns.
+ var pageVersions = versions.getPageVersions(firstPage);
+ var index = 1;
+ pageVersions.forEach((page) => {
+ if (page !== firstPage) {
+ showPageInColumn(page, index);
+ index++;
+ }
+ });
+ if (changeSelectedEntry) {
+ showEntryDetail(selectedPage.getEntry(selectedEntry));
+ } else {
+ showImpactList(selectedPage);
+ }
+ }
+
+ function showPageInColumn(page, columnIndex) {
+ page.sort();
+ var showDiff = (baselineVersion === undefined && columnIndex !== 0) ||
+ (baselineVersion !== undefined && page.version !== baselineVersion);
+ var diffStatus = (td, a, b) => {};
+ if (showDiff) {
+ if (baselineVersion !== undefined) {
+ diffStatus = (td, a, b) => {
+ if (a == 0) return;
+ td.style.color = a < 0 ? '#FF0000' : '#00BB00';
+ };
+ } else {
+ diffStatus = (td, a, b) => {
+ if (a == b) return;
+ var color;
+ var ratio = a / b;
+ if (ratio > 1) {
+ ratio = Math.min(Math.round((ratio - 1) * 255 * 10), 200);
+ color = '#' + ratio.toString(16) + "0000";
+ } else {
+ ratio = Math.min(Math.round((1 - ratio) * 255 * 10), 200);
+ color = '#00' + ratio.toString(16) + "00";
+ }
+ td.style.color = color;
+ }
+ }
+ }
+
+ var column = $('column_' + columnIndex);
+ var select = $('select_' + columnIndex);
+ // Find the matching option
+ selectOption(select, (i, option) => {
+ return option.page == page
+ });
+ var table = column.querySelector("table");
+ var oldTbody = table.querySelector('tbody');
+ var tbody = document.createElement('tbody');
+ var referencePage = selectedPage;
+ page.forEachSorted(selectedPage, (parentEntry, entry, referenceEntry) => {
+ // Filter out entries that do not exist in the first column for the default
+ // view.
+ if (baselineVersion === undefined && referenceEntry &&
+ referenceEntry.time == 0) {
+ return;
+ }
+ var tr = document.createElement('tr');
+ tbody.appendChild(tr);
+ tr.entry = entry;
+ tr.parentEntry = parentEntry;
+ tr.className = parentEntry === undefined ? 'parent' : 'child';
+ // Don't show entries that do not exist on the current page or if we
+ // compare against the current page
+ if (entry !== undefined && page.version !== baselineVersion) {
+ // If we show a diff, use the baselineVersion as the referenceEntry
+ if (baselineVersion !== undefined) {
+ var baselineEntry = baselineVersion.getEntry(entry);
+ if (baselineEntry !== undefined) referenceEntry = baselineEntry
+ }
+ if (!parentEntry) {
+ var node = td(tr, '<div class="toggle">►</div>', 'position');
+ node.firstChild.addEventListener('click', handleToggleGroup);
+ } else {
+ td(tr, entry.position == 0 ? '' : entry.position, 'position');
+ }
+ addCodeSearchButton(entry,
+ td(tr, entry.name, 'name ' + entry.cssClass()));
+
+ diffStatus(
+ td(tr, ms(entry.time), 'value time'),
+ entry.time, referenceEntry.time);
+ diffStatus(
+ td(tr, percent(entry.timePercent), 'value time'),
+ entry.time, referenceEntry.time);
+ diffStatus(
+ td(tr, count(entry.count), 'value count'),
+ entry.count, referenceEntry.count);
+ } else if (baselineVersion !== undefined && referenceEntry
+ && page.version !== baselineVersion) {
+ // Show comparison of entry that does not exist on the current page.
+ tr.entry = new Entry(0, referenceEntry.name);
+ tr.entry.page = page;
+ td(tr, '-', 'position');
+ td(tr, referenceEntry.name, 'name');
+ diffStatus(
+ td(tr, ms(-referenceEntry.time), 'value time'),
+ -referenceEntry.time, 0);
+ diffStatus(
+ td(tr, percent(-referenceEntry.timePercent), 'value time'),
+ -referenceEntry.timePercent, 0);
+ diffStatus(
+ td(tr, count(-referenceEntry.count), 'value count'),
+ -referenceEntry.count, 0);
+ } else {
+ // Display empty entry / baseline entry
+ var showBaselineEntry = entry !== undefined;
+ if (showBaselineEntry) {
+ if (!parentEntry) {
+ var node = td(tr, '<div class="toggle">►</div>', 'position');
+ node.firstChild.addEventListener('click', handleToggleGroup);
+ } else {
+ td(tr, entry.position == 0 ? '' : entry.position, 'position');
+ }
+ td(tr, entry.name, 'name');
+ td(tr, ms(entry.time, false), 'value time');
+ td(tr, percent(entry.timePercent, false), 'value time');
+ td(tr, count(entry.count, false), 'value count');
+ } else {
+ td(tr, '-', 'position');
+ td(tr, '-', 'name');
+ td(tr, '-', 'value time');
+ td(tr, '-', 'value time');
+ td(tr, '-', 'value count');
+ }
+ }
+ });
+ table.replaceChild(tbody, oldTbody);
+ var versionSelect = column.querySelector('select.version');
+ selectOption(versionSelect, (index, option) => {
+ return option.version == page.version
+ });
+ }
+
+ function selectEntry(entry, updateSelectedPage) {
+ if (updateSelectedPage) {
+ entry = selectedPage.version.getEntry(entry);
+ }
+ var rowIndex = 0;
+ var needsPageSwitch = updateSelectedPage && entry.page != selectedPage;
+ // If clicked in the detail row change the first column to that page.
+ if (needsPageSwitch) showPage(entry.page);
+ var childNodes = $('column_0').querySelector('.list tbody').childNodes;
+ for (var i = 0; i < childNodes.length; i++) {
+ if (childNodes[i].entry.name == entry.name) {
+ rowIndex = i;
+ break;
+ }
+ }
+ var firstEntry = childNodes[rowIndex].entry;
+ if (rowIndex) {
+ if (firstEntry.parent) showGroup(firstEntry.parent);
+ }
+ // Deselect all
+ $('view').querySelectorAll('.list tbody tr').forEach((tr) => {
+ toggleCssClass(tr, 'selected', false);
+ });
+ // Select the entry row
+ $('view').querySelectorAll("tbody").forEach((body) => {
+ var row = body.childNodes[rowIndex];
+ if (!row) return;
+ toggleCssClass(row, 'selected', row.entry && row.entry.name ==
+ firstEntry.name);
+ });
+ if (updateSelectedPage) {
+ entry = selectedEntry.page.version.getEntry(entry);
+ }
+ selectedEntry = entry;
+ showEntryDetail(entry);
+ }
+
+ function showEntryDetail(entry) {
+ showVersionDetails(entry);
+ showPageDetails(entry);
+ showImpactList(entry.page);
+ showGraphs(entry.page);
+ }
+
+ function showVersionDetails(entry) {
+ var table, tbody, entries;
+ table = $('detailView').querySelector('.versionDetailTable');
+ tbody = document.createElement('tbody');
+ if (entry !== undefined) {
+ $('detailView').querySelector('.versionDetail h3 span').innerHTML =
+ entry.name + ' in ' + entry.page.name;
+ entries = versions.getPageVersions(entry.page).map(
+ (page) => {
+ return page.get(entry.name)
+ });
+ entries.sort((a, b) => {
+ return a.time - b.time
+ });
+ entries.forEach((pageEntry) => {
+ if (pageEntry === undefined) return;
+ var tr = document.createElement('tr');
+ if (pageEntry == entry) tr.className += 'selected';
+ tr.entry = pageEntry;
+ var isBaselineEntry = pageEntry.page.version == baselineVersion;
+ td(tr, pageEntry.page.version.name, 'version');
+ td(tr, ms(pageEntry.time, !isBaselineEntry), 'value time');
+ td(tr, percent(pageEntry.timePercent, !isBaselineEntry), 'value time');
+ td(tr, count(pageEntry.count, !isBaselineEntry), 'value count');
+ tbody.appendChild(tr);
+ });
+ }
+ table.replaceChild(tbody, table.querySelector('tbody'));
+ }
+
+ function showPageDetails(entry) {
+ var table, tbody, entries;
+ table = $('detailView').querySelector('.pageDetailTable');
+ tbody = document.createElement('tbody');
+ if (entry === undefined) {
+ table.replaceChild(tbody, table.querySelector('tbody'));
+ return;
+ }
+ var version = entry.page.version;
+ var showDiff = version !== baselineVersion;
+ $('detailView').querySelector('.pageDetail h3 span').innerHTML =
+ version.name;
+ entries = version.pages.map((page) => {
+ if (!page.enabled) return;
+ return page.get(entry.name)
+ });
+ entries.sort((a, b) => {
+ var cmp = b.timePercent - a.timePercent;
+ if (cmp.toFixed(1) == 0) return b.time - a.time;
+ return cmp
+ });
+ entries.forEach((pageEntry) => {
+ if (pageEntry === undefined) return;
+ var tr = document.createElement('tr');
+ if (pageEntry === entry) tr.className += 'selected';
+ tr.entry = pageEntry;
+ td(tr, pageEntry.page.name, 'name');
+ td(tr, ms(pageEntry.time, showDiff), 'value time');
+ td(tr, percent(pageEntry.timePercent, showDiff), 'value time');
+ td(tr, percent(pageEntry.timePercentPerEntry, showDiff),
+ 'value time hideNoDiff');
+ td(tr, count(pageEntry.count, showDiff), 'value count');
+ tbody.appendChild(tr);
+ });
+ // show the total for all pages
+ var tds = table.querySelectorAll('tfoot td');
+ tds[1].innerHTML = ms(entry.getTimeImpact(), showDiff);
+ // Only show the percentage total if we are in diff mode:
+ tds[2].innerHTML = percent(entry.getTimePercentImpact(), showDiff);
+ tds[3].innerHTML = '';
+ tds[4].innerHTML = count(entry.getCountImpact(), showDiff);
+ table.replaceChild(tbody, table.querySelector('tbody'));
+ }
+
+ function showImpactList(page) {
+ var impactView = $('detailView').querySelector('.impactView');
+ impactView.querySelector('h3 span').innerHTML = page.version.name;
+
+ var table = impactView.querySelector('table');
+ var tbody = document.createElement('tbody');
+ var version = page.version;
+ var entries = version.allEntries();
+ if (selectedEntry !== undefined && selectedEntry.isGroup) {
+ impactView.querySelector('h3 span').innerHTML += " " + selectedEntry.name;
+ entries = entries.filter((entry) => {
+ return entry.name == selectedEntry.name ||
+ (entry.parent && entry.parent.name == selectedEntry.name)
+ });
+ }
+ var isCompareView = baselineVersion !== undefined;
+ entries = entries.filter((entry) => {
+ if (isCompareView) {
+ var impact = entry.getTimeImpact();
+ return impact < -1 || 1 < impact
+ }
+ return entry.getTimePercentImpact() > 0.1;
+ });
+ entries.sort((a, b) => {
+ var cmp = b.getTimePercentImpact() - a.getTimePercentImpact();
+ if (isCompareView || cmp.toFixed(1) == 0) {
+ return b.getTimeImpact() - a.getTimeImpact();
+ }
+ return cmp
+ });
+ entries.forEach((entry) => {
+ var tr = document.createElement('tr');
+ tr.entry = entry;
+ td(tr, entry.name, 'name');
+ td(tr, ms(entry.getTimeImpact()), 'value time');
+ var percentImpact = entry.getTimePercentImpact();
+ td(tr, percentImpact > 1000 ? '-' : percent(percentImpact), 'value time');
+ var topPages = entry.getPagesByPercentImpact().slice(0, 3)
+ .map((each) => {
+ return each.name + ' (' + percent(each.getEntry(entry).timePercent) +
+ ')'
+ });
+ td(tr, topPages.join(', '), 'name');
+ tbody.appendChild(tr);
+ });
+ table.replaceChild(tbody, table.querySelector('tbody'));
+ }
+
+ function showGraphs(page) {
+ var groups = page.groups.slice();
+ // Sort groups by the biggest impact
+ groups.sort((a, b) => {
+ return b.getTimeImpact() - a.getTimeImpact();
+ });
+ if (selectedGroup == undefined) {
+ selectedGroup = groups[0];
+ } else {
+ groups = groups.filter(each => each.name != selectedGroup.name);
+ groups.unshift(selectedGroup);
+ }
+ showPageGraph(groups, page);
+ showVersionGraph(groups, page);
+ showPageVersionGraph(groups, page);
+ }
+
+ function getGraphDataTable(groups) {
+ var dataTable = new google.visualization.DataTable();
+ dataTable.addColumn('string', 'Name');
+ groups.forEach(group => {
+ var column = dataTable.addColumn('number', group.name.substring(6));
+ dataTable.setColumnProperty(column, 'group', group);
+ });
+ return dataTable;
+ }
+
+ var selectedGroup;
+ function showPageGraph(groups, page) {
+ var isDiffView = baselineVersion !== undefined;
+ var dataTable = getGraphDataTable(groups);
+ // Calculate the average row
+ var row = ['Average'];
+ groups.forEach((group) => {
+ if (isDiffView) {
+ row.push(group.isTotal ? 0 : group.getAverageTimeImpact());
+ } else {
+ row.push(group.isTotal ? 0 : group.getTimeImpact());
+ }
+ });
+ dataTable.addRow(row);
+ // Sort the pages by the selected group.
+ var pages = page.version.pages.filter(page => page.enabled);
+ function sumDiff(page) {
+ var sum = 0;
+ groups.forEach(group => {
+ var value = group.getTimePercentImpact() -
+ page.getEntry(group).timePercent;
+ sum += value * value;
+ });
+ return sum;
+ }
+ if (isDiffView) {
+ pages.sort((a, b) => {
+ return b.getEntry(selectedGroup).time-
+ a.getEntry(selectedGroup).time;
+ });
+ } else {
+ pages.sort((a, b) => {
+ return b.getEntry(selectedGroup).timePercent -
+ a.getEntry(selectedGroup).timePercent;
+ });
+ }
+ // Sort by sum of squared distance to the average.
+ // pages.sort((a, b) => {
+ // return a.distanceFromTotalPercent() - b.distanceFromTotalPercent();
+ // });
+ // Calculate the entries for the pages
+ pages.forEach((page) => {
+ row = [page.name];
+ groups.forEach((group) => {
+ row.push(group.isTotal ? 0 : page.getEntry(group).time);
+ });
+ var rowIndex = dataTable.addRow(row);
+ dataTable.setRowProperty(rowIndex, 'page', page);
+ });
+ renderGraph('Pages for ' + page.version.name, groups, dataTable,
+ 'pageGraph', isDiffView ? true : 'percent');
+ }
+
+ function showVersionGraph(groups, page) {
+ var dataTable = getGraphDataTable(groups);
+ var row;
+ var vs = versions.versions.filter(version => version.enabled);
+ vs.sort((a, b) => {
+ return b.getEntry(selectedGroup).getTimeImpact() -
+ a.getEntry(selectedGroup).getTimeImpact();
+ });
+ // Calculate the entries for the versions
+ vs.forEach((version) => {
+ row = [version.name];
+ groups.forEach((group) => {
+ row.push(group.isTotal ? 0 : version.getEntry(group).getTimeImpact());
+ });
+ var rowIndex = dataTable.addRow(row);
+ dataTable.setRowProperty(rowIndex, 'page', page);
+ });
+ renderGraph('Versions Total Time over all Pages', groups, dataTable,
+ 'versionGraph', true);
+ }
+
+ function showPageVersionGraph(groups, page) {
+ var dataTable = getGraphDataTable(groups);
+ var row;
+ var vs = versions.getPageVersions(page);
+ vs.sort((a, b) => {
+ return b.getEntry(selectedGroup).time - a.getEntry(selectedGroup).time;
+ });
+ // Calculate the entries for the versions
+ vs.forEach((page) => {
+ row = [page.version.name];
+ groups.forEach((group) => {
+ row.push(group.isTotal ? 0 : page.getEntry(group).time);
+ });
+ var rowIndex = dataTable.addRow(row);
+ dataTable.setRowProperty(rowIndex, 'page', page);
+ });
+ renderGraph('Versions for ' + page.name, groups, dataTable,
+ 'pageVersionGraph', true);
+ }
+
+ function renderGraph(title, groups, dataTable, id, isStacked) {
+ var isDiffView = baselineVersion !== undefined;
+ var formatter = new google.visualization.NumberFormat({
+ suffix: (isDiffView ? 'msΔ' : 'ms'),
+ negativeColor: 'red',
+ groupingSymbol: "'"
+ });
+ for (var i = 1; i < dataTable.getNumberOfColumns(); i++) {
+ formatter.format(dataTable, i);
+ }
+ var height = 85 + 28 * dataTable.getNumberOfRows();
+ var options = {
+ isStacked: isStacked,
+ height: height,
+ hAxis: {
+ minValue: 0,
+ },
+ animation:{
+ duration: 500,
+ easing: 'out',
+ },
+ vAxis: {
+ },
+ explorer: {
+ actions: ['dragToZoom', 'rightClickToReset'],
+ maxZoomIn: 0.01
+ },
+ legend: {position:'top', textStyle:{fontSize: '16px'}},
+ chartArea: {left:200, top:50, width:'98%', height:'80%'},
+ colors: groups.map(each => each.color)
+ };
+ var parentNode = $(id);
+ parentNode.querySelector('h2>span, h3>span').innerHTML = title;
+ var graphNode = parentNode.querySelector('.content');
+
+ var chart = graphNode.chart;
+ if (chart === undefined) {
+ chart = graphNode.chart = new google.visualization.BarChart(graphNode);
+ } else {
+ google.visualization.events.removeAllListeners(chart);
+ }
+ google.visualization.events.addListener(chart, 'select', selectHandler);
+ function getChartEntry(selection) {
+ if (!selection) return undefined;
+ var column = selection.column;
+ if (column == undefined) return undefined;
+ var selectedGroup = dataTable.getColumnProperty(column, 'group');
+ var row = selection.row;
+ if (row == null) return selectedGroup;
+ var page = dataTable.getRowProperty(row, 'page');
+ if (!page) return selectedGroup;
+ return page.getEntry(selectedGroup);
+ }
+ function selectHandler() {
+ selectedGroup = getChartEntry(chart.getSelection()[0])
+ if (!selectedGroup) return;
+ selectEntry(selectedGroup, true);
+ }
+
+ // Make our global tooltips work
+ google.visualization.events.addListener(chart, 'onmouseover', mouseOverHandler);
+ function mouseOverHandler(selection) {
+ graphNode.entry = getChartEntry(selection);
+ }
+ chart.draw(dataTable, options);
+ }
+
+ function showGroup(entry) {
+ toggleGroup(entry, true);
+ }
+
+ function toggleGroup(group, show) {
+ $('view').querySelectorAll(".child").forEach((tr) => {
+ var entry = tr.parentEntry;
+ if (!entry) return;
+ if (entry.name !== group.name) return;
+ toggleCssClass(tr, 'visible', show);
+ });
+ }
+
+ function showPopover(entry) {
+ var popover = $('popover');
+ popover.querySelector('td.name').innerHTML = entry.name;
+ popover.querySelector('td.page').innerHTML = entry.page.name;
+ setPopoverDetail(popover, entry, '');
+ popover.querySelector('table').className = "";
+ if (baselineVersion !== undefined) {
+ entry = baselineVersion.getEntry(entry);
+ setPopoverDetail(popover, entry, '.compare');
+ popover.querySelector('table').className = "compare";
+ }
+ }
+
+ function setPopoverDetail(popover, entry, prefix) {
+ var node = (name) => popover.querySelector(prefix + name);
+ if (entry == undefined) {
+ node('.version').innerHTML = baselineVersion.name;
+ node('.time').innerHTML = '-';
+ node('.timeVariance').innerHTML = '-';
+ node('.percent').innerHTML = '-';
+ node('.percentPerEntry').innerHTML = '-';
+ node('.percentVariance').innerHTML = '-';
+ node('.count').innerHTML = '-';
+ node('.countVariance').innerHTML = '-';
+ node('.timeImpact').innerHTML = '-';
+ node('.timePercentImpact').innerHTML = '-';
+ } else {
+ node('.version').innerHTML = entry.page.version.name;
+ node('.time').innerHTML = ms(entry._time, false);
+ node('.timeVariance').innerHTML
+ = percent(entry.timeVariancePercent, false);
+ node('.percent').innerHTML = percent(entry.timePercent, false);
+ node('.percentPerEntry').innerHTML
+ = percent(entry.timePercentPerEntry, false);
+ node('.percentVariance').innerHTML
+ = percent(entry.timePercentVariancePercent, false);
+ node('.count').innerHTML = count(entry._count, false);
+ node('.countVariance').innerHTML
+ = percent(entry.timeVariancePercent, false);
+ node('.timeImpact').innerHTML
+ = ms(entry.getTimeImpact(false), false);
+ node('.timePercentImpact').innerHTML
+ = percent(entry.getTimeImpactVariancePercent(false), false);
+ }
+ }
+ </script>
+ <script type="text/javascript">
+ "use strict"
+ // =========================================================================
+ // Helpers
+ function $(id) {
+ return document.getElementById(id)
+ }
+
+ function removeAllChildren(node) {
+ while (node.firstChild) {
+ node.removeChild(node.firstChild);
+ }
+ }
+
+ function selectOption(select, match) {
+ var options = select.options;
+ for (var i = 0; i < options.length; i++) {
+ if (match(i, options[i])) {
+ select.selectedIndex = i;
+ return;
+ }
+ }
+ }
+
+ function addCodeSearchButton(entry, node) {
+ if (entry.isGroup) return;
+ var button = document.createElement("div");
+ button.innerHTML = '?'
+ button.className = "codeSearch"
+ button.addEventListener('click', handleCodeSearch);
+ node.appendChild(button);
+ return node;
+ }
+
+ function td(tr, content, className) {
+ var td = document.createElement("td");
+ td.innerHTML = content;
+ td.className = className
+ tr.appendChild(td);
+ return td
+ }
+
+ function nodeIndex(node) {
+ var children = node.parentNode.childNodes,
+ i = 0;
+ for (; i < children.length; i++) {
+ if (children[i] == node) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ function toggleCssClass(node, cssClass, toggleState) {
+ var index = -1;
+ var classes;
+ if (node.className != undefined) {
+ classes = node.className.split(' ');
+ index = classes.indexOf(cssClass);
+ }
+ if (index == -1) {
+ if (toggleState === false) return;
+ node.className += ' ' + cssClass;
+ return;
+ }
+ if (toggleState === true) return;
+ classes.splice(index, 1);
+ node.className = classes.join(' ');
+ }
+
+ function NameComparator(a, b) {
+ if (a.name > b.name) return 1;
+ if (a.name < b.name) return -1;
+ return 0
+ }
+
+ function diffSign(value, digits, unit, showDiff) {
+ if (showDiff === false || baselineVersion == undefined) {
+ if (value === undefined) return '';
+ return value.toFixed(digits) + unit;
+ }
+ return (value >= 0 ? '+' : '') + value.toFixed(digits) + unit + 'Δ';
+ }
+
+ function ms(value, showDiff) {
+ return diffSign(value, 1, 'ms', showDiff);
+ }
+
+ function count(value, showDiff) {
+ return diffSign(value, 0, '#', showDiff);
+ }
+
+ function percent(value, showDiff) {
+ return diffSign(value, 1, '%', showDiff);
+ }
+
+ </script>
+ <script type="text/javascript">
+ "use strict"
+ // =========================================================================
+ // EventHandlers
+ function handleBodyLoad() {
+ $('uploadInput').focus();
+ }
+
+ function handleLoadFile() {
+ var files = document.getElementById("uploadInput").files;
+ var file = files[0];
+ var reader = new FileReader();
+
+ reader.onload = function(evt) {
+ pages = new Pages();
+ versions = Versions.fromJSON(JSON.parse(this.result));
+ initialize()
+ showPage(versions.versions[0].pages[0]);
+ }
+ reader.readAsText(file);
+ }
+
+ function handleToggleGroup(event) {
+ var group = event.target.parentNode.parentNode.entry;
+ toggleGroup(selectedPage.get(group.name));
+ }
+
+ function handleSelectPage(select, event) {
+ var option = select.options[select.selectedIndex];
+ if (select.id == "select_0") {
+ showPage(option.page);
+ } else {
+ var columnIndex = select.id.split('_')[1];
+ showPageInColumn(option.page, columnIndex);
+ }
+ }
+
+ function handleSelectVersion(select, event) {
+ var option = select.options[select.selectedIndex];
+ var version = option.version;
+ if (select.id == "selectVersion_0") {
+ var page = version.get(selectedPage.name);
+ showPage(page);
+ } else {
+ var columnIndex = select.id.split('_')[1];
+ var pageSelect = $('select_' + columnIndex);
+ var page = pageSelect.options[pageSelect.selectedIndex].page;
+ page = version.get(page.name);
+ showPageInColumn(page, columnIndex);
+ }
+ }
+
+ function handleSelectDetailRow(table, event) {
+ if (event.target.tagName != 'TD') return;
+ var tr = event.target.parentNode;
+ if (tr.tagName != 'TR') return;
+ if (tr.entry === undefined) return;
+ selectEntry(tr.entry, true);
+ }
+
+ function handleSelectRow(table, event, fromDetail) {
+ if (event.target.tagName != 'TD') return;
+ var tr = event.target.parentNode;
+ if (tr.tagName != 'TR') return;
+ if (tr.entry === undefined) return;
+ selectEntry(tr.entry, false);
+ }
+
+ function handleSelectBaseline(select, event) {
+ var option = select.options[select.selectedIndex];
+ baselineVersion = option.version;
+ var showingDiff = baselineVersion !== undefined;
+ var body = $('body');
+ toggleCssClass(body, 'diff', showingDiff);
+ toggleCssClass(body, 'noDiff', !showingDiff);
+ showPage(selectedPage);
+ if (selectedEntry === undefined) return;
+ selectEntry(selectedEntry, true);
+ }
+
+ function findEntry(event) {
+ var target = event.target;
+ while (target.entry === undefined) {
+ target = target.parentNode;
+ if (!target) return undefined;
+ }
+ return target.entry;
+ }
+
+ function handleUpdatePopover(event) {
+ var popover = $('popover');
+ popover.style.left = event.pageX + 'px';
+ popover.style.top = event.pageY + 'px';
+ popover.style.display = 'none';
+ popover.style.display = event.shiftKey ? 'block' : 'none';
+ var entry = findEntry(event);
+ if (entry === undefined) return;
+ showPopover(entry);
+ }
+
+ function handleToggleVersionEnable(event) {
+ var item = this.item ;
+ if (item === undefined) return;
+ item .enabled = this.checked;
+ initialize();
+ var page = selectedPage;
+ if (page === undefined || !page.version.enabled) {
+ page = versions.getEnabledPage(page.name);
+ }
+ showPage(page);
+ }
+
+ function handleToggleContentVisibility(event) {
+ var content = event.target.contentNode;
+ toggleCssClass(content, 'hidden');
+ }
+
+ function handleCodeSearch(event) {
+ var entry = findEntry(event);
+ if (entry === undefined) return;
+ var url = "https://cs.chromium.org/search/?sq=package:chromium&type=cs&q=";
+ name = entry.name;
+ if (name.startsWith("API_")) {
+ name = name.substring(4);
+ }
+ url += encodeURIComponent(name) + "+file:src/v8/src";
+ window.open(url,'_blank');
+ }
+ </script>
+ <script type="text/javascript">
+ "use strict"
+ // =========================================================================
+ class Versions {
+ constructor() {
+ this.versions = [];
+ }
+ add(version) {
+ this.versions.push(version)
+ }
+ getPageVersions(page) {
+ var result = [];
+ this.versions.forEach((version) => {
+ if (!version.enabled) return;
+ var versionPage = version.get(page.name);
+ if (versionPage !== undefined) result.push(versionPage);
+ });
+ return result;
+ }
+ get length() {
+ return this.versions.length
+ }
+ get(index) {
+ return this.versions[index]
+ };
+ forEach(f) {
+ this.versions.forEach(f);
+ }
+ sort() {
+ this.versions.sort(NameComparator);
+ }
+ getEnabledPage(name) {
+ for (var i = 0; i < this.versions.length; i++) {
+ var version = this.versions[i];
+ if (!version.enabled) continue;
+ var page = version.get(name);
+ if (page !== undefined) return page;
+ }
+ }
+ }
+ Versions.fromJSON = function(json) {
+ var versions = new Versions();
+ for (var version in json) {
+ versions.add(Version.fromJSON(version, json[version]));
+ }
+ versions.sort();
+ return versions;
+ }
+
+ class Version {
+ constructor(name) {
+ this.name = name;
+ this.enabled = true;
+ this.pages = [];
+ }
+ add(page) {
+ this.pages.push(page);
+ }
+ indexOf(name) {
+ for (var i = 0; i < this.pages.length; i++) {
+ if (this.pages[i].name == name) return i;
+ }
+ return -1;
+ }
+ get(name) {
+ var index = this.indexOf(name);
+ if (0 <= index) return this.pages[index];
+ return undefined
+ }
+ get length() {
+ return this.versions.length
+ }
+ getEntry(entry) {
+ if (entry === undefined) return undefined;
+ var page = this.get(entry.page.name);
+ if (page === undefined) return undefined;
+ return page.get(entry.name);
+ }
+ forEachEntry(fun) {
+ this.forEachPage((page) => {
+ page.forEach(fun);
+ });
+ }
+ forEachPage(fun) {
+ this.pages.forEach((page) => {
+ if (!page.enabled) return;
+ fun(page);
+ })
+ }
+ allEntries() {
+ var map = new Map();
+ this.forEachEntry((group, entry) => {
+ if (!map.has(entry.name)) map.set(entry.name, entry);
+ });
+ return Array.from(map.values());
+ }
+ getTotalValue(name, property) {
+ if (name === undefined) name = this.pages[0].total.name;
+ var sum = 0;
+ this.forEachPage((page) => {
+ var entry = page.get(name);
+ if (entry !== undefined) sum += entry[property];
+ });
+ return sum;
+ }
+ getTotalTime(name, showDiff) {
+ return this.getTotalValue(name, showDiff === false ? '_time' : 'time');
+ }
+ getTotalTimePercent(name, showDiff) {
+ if (baselineVersion === undefined || showDiff === false) {
+ // Return the overall average percent of the given entry name.
+ return this.getTotalValue(name, 'time') /
+ this.getTotalTime('Group-Total') * 100;
+ }
+ // Otherwise return the difference to the sum of the baseline version.
+ var baselineValue = baselineVersion.getTotalTime(name, false);
+ var total = this.getTotalValue(name, '_time');
+ return (total / baselineValue - 1) * 100;
+ }
+ getTotalTimeVariance(name, showDiff) {
+ // Calculate the overall error for a given entry name
+ var sum = 0;
+ this.forEachPage((page) => {
+ var entry = page.get(name);
+ if (entry === undefined) return;
+ sum += entry.timeVariance * entry.timeVariance;
+ });
+ return Math.sqrt(sum);
+ }
+ getTotalTimeVariancePercent(name, showDiff) {
+ return this.getTotalTimeVariance(name, showDiff) /
+ this.getTotalTime(name, showDiff) * 100;
+ }
+ getTotalCount(name, showDiff) {
+ return this.getTotalValue(name, showDiff === false ? '_count' : 'count');
+ }
+ getAverageTimeImpact(name, showDiff) {
+ return this.getTotalTime(name, showDiff) / this.pages.length;
+ }
+ getPagesByPercentImpact(name) {
+ var sortedPages =
+ this.pages.filter((each) => {
+ return each.get(name) !== undefined
+ });
+ sortedPages.sort((a, b) => {
+ return b.get(name).timePercent - a.get(name).timePercent;
+ });
+ return sortedPages;
+ }
+ sort() {
+ this.pages.sort(NameComparator)
+ }
+ }
+ Version.fromJSON = function(name, data) {
+ var version = new Version(name);
+ for (var pageName in data) {
+ version.add(PageVersion.fromJSON(version, pageName, data[pageName]));
+ }
+ version.sort();
+ return version;
+ }
+
+ class Pages extends Map {
+ get(name) {
+ if (name.indexOf('www.') == 0) {
+ name = name.substring(4);
+ }
+ if (!this.has(name)) {
+ this.set(name, new Page(name));
+ }
+ return super.get(name);
+ }
+ }
+
+ class Page {
+ constructor(name) {
+ this.name = name;
+ this.enabled = true;
+ this.versions = [];
+ }
+ add(page) {
+ this.versions.push(page);
+ }
+ }
+
+ class PageVersion {
+ constructor(version, page) {
+ this.page = page;
+ this.page.add(this);
+ this.total = new GroupedEntry('Total', /.*Total.*/, '#BBB');
+ this.total.isTotal = true;
+ this.unclassified = new UnclassifiedEntry(this, "#000")
+ this.groups = [
+ this.total,
+ new GroupedEntry('IC', /.*IC.*/, "#3366CC"),
+ new GroupedEntry('Optimize',
+ /StackGuard|.*Optimize.*|.*Deoptimize.*|Recompile.*/, "#DC3912"),
+ new GroupedEntry('Compile', /.*Compile.*/, "#FFAA00"),
+ new GroupedEntry('Parse', /.*Parse.*/, "#FF6600"),
+ new GroupedEntry('Callback', /.*Callback$/, "#109618"),
+ new GroupedEntry('API', /.*API.*/, "#990099"),
+ new GroupedEntry('GC', /GC|AllocateInTargetSpace/, "#0099C6"),
+ new GroupedEntry('JavaScript', /JS_Execution/, "#DD4477"),
+ new GroupedEntry('Runtime', /.*/, "#88BB00"),
+ this.unclassified
+ ];
+ this.entryDict = new Map();
+ this.groups.forEach((entry) => {
+ entry.page = this;
+ this.entryDict.set(entry.name, entry);
+ });
+ this.version = version;
+ }
+ add(entry) {
+ entry.page = this;
+ this.entryDict.set(entry.name, entry);
+ var added = false;
+ this.groups.forEach((group) => {
+ if (!added) added = group.add(entry);
+ });
+ if (added) return;
+ this.unclassified.push(entry);
+ }
+ get(name) {
+ return this.entryDict.get(name)
+ }
+ getEntry(entry) {
+ if (entry === undefined) return undefined;
+ return this.get(entry.name);
+ }
+ get length() {
+ return this.versions.length
+ }
+ get name() { return this.page.name }
+ get enabled() { return this.page.enabled }
+ forEachSorted(referencePage, func) {
+ // Iterate over all the entries in the order they appear on the
+ // reference page.
+ referencePage.forEach((parent, referenceEntry) => {
+ var entry;
+ if (parent) parent = this.entryDict.get(parent.name);
+ if (referenceEntry) entry = this.entryDict.get(referenceEntry.name);
+ func(parent, entry, referenceEntry);
+ });
+ }
+ forEach(fun) {
+ this.forEachGroup((group) => {
+ fun(undefined, group);
+ group.forEach((entry) => {
+ fun(group, entry)
+ });
+ });
+ }
+ forEachGroup(fun) {
+ this.groups.forEach(fun)
+ }
+ sort() {
+ this.groups.sort((a, b) => {
+ return b.time - a.time;
+ });
+ this.groups.forEach((group) => {
+ group.sort()
+ });
+ }
+ distanceFromTotalPercent() {
+ var sum = 0;
+ this.groups.forEach(group => {
+ if (group == this.total) return;
+ var value = group.getTimePercentImpact() -
+ this.getEntry(group).timePercent;
+ sum += value * value;
+ });
+ return sum;
+ }
+ }
+ PageVersion.fromJSON = function(version, name, data) {
+ var page = new PageVersion(version, pages.get(name));
+ for (var i = 0; i < data.length; i++) {
+ page.add(Entry.fromJSON(i, data[data.length - i - 1]));
+ }
+ page.sort();
+ return page
+ }
+
+
+ class Entry {
+ constructor(position, name, time, timeVariance, timeVariancePercent,
+ count,
+ countVariance, countVariancePercent) {
+ this.position = position;
+ this.name = name;
+ this._time = time;
+ this._timeVariance = timeVariance;
+ this._timeVariancePercent = timeVariancePercent;
+ this._count = count;
+ this.countVariance = countVariance;
+ this.countVariancePercent = countVariancePercent;
+ this.page = undefined;
+ this.parent = undefined;
+ this.isTotal = false;
+ }
+ getCompareWithBaseline(value, property) {
+ if (baselineVersion == undefined) return value;
+ var baselineEntry = baselineVersion.getEntry(this);
+ if (!baselineEntry) return value;
+ if (baselineVersion === this.page.version) return value;
+ return value - baselineEntry[property];
+ }
+ cssClass() {
+ return ''
+ }
+ get time() {
+ return this.getCompareWithBaseline(this._time, '_time');
+ }
+ get count() {
+ return this.getCompareWithBaseline(this._count, '_count');
+ }
+ get timePercent() {
+ var value = this._time / this.page.total._time * 100;
+ if (baselineVersion == undefined) return value;
+ var baselineEntry = baselineVersion.getEntry(this);
+ if (!baselineEntry) return value;
+ if (baselineVersion === this.page.version) return value;
+ return (this._time - baselineEntry._time) / this.page.total._time *
+ 100;
+ }
+ get timePercentPerEntry() {
+ var value = this._time / this.page.total._time * 100;
+ if (baselineVersion == undefined) return value;
+ var baselineEntry = baselineVersion.getEntry(this);
+ if (!baselineEntry) return value;
+ if (baselineVersion === this.page.version) return value;
+ return (this._time / baselineEntry._time - 1) * 100;
+ }
+ get timePercentVariancePercent() {
+ // Get the absolute values for the percentages
+ return this.timeVariance / this.page.total._time * 100;
+ }
+ getTimeImpact(showDiff) {
+ return this.page.version.getTotalTime(this.name, showDiff);
+ }
+ getTimeImpactVariancePercent(showDiff) {
+ return this.page.version.getTotalTimeVariancePercent(this.name, showDiff);
+ }
+ getTimePercentImpact(showDiff) {
+ return this.page.version.getTotalTimePercent(this.name, showDiff);
+ }
+ getCountImpact(showDiff) {
+ return this.page.version.getTotalCount(this.name, showDiff);
+ }
+ getAverageTimeImpact(showDiff) {
+ return this.page.version.getAverageTimeImpact(this.name, showDiff);
+ }
+ getPagesByPercentImpact() {
+ return this.page.version.getPagesByPercentImpact(this.name);
+ }
+ get isGroup() {
+ return false
+ }
+ get timeVariance() {
+ return this._timeVariance
+ }
+ get timeVariancePercent() {
+ return this._timeVariancePercent
+ }
+ }
+ Entry.fromJSON = function(position, data) {
+ return new Entry(position, ...data);
+ }
+
+
+ class GroupedEntry extends Entry {
+ constructor(name, regexp, color) {
+ super(0, 'Group-' + name, 0, 0, 0, 0, 0, 0);
+ this.regexp = regexp;
+ this.color = color;
+ this.entries = [];
+ }
+ add(entry) {
+ if (!entry.name.match(this.regexp)) return false;
+ this._time += entry.time;
+ this._count += entry.count;
+ // TODO: sum up variance
+ this.entries.push(entry);
+ entry.parent = this;
+ return true;
+ }
+ forEach(fun) {
+ if (baselineVersion === undefined) {
+ this.entries.forEach(fun);
+ return;
+ }
+ // If we have a baslineVersion to compare against show also all entries
+ // from the other group.
+ var tmpEntries = baselineVersion.getEntry(this)
+ .entries.filter((entry) => {
+ return this.page.get(entry.name) == undefined
+ });
+
+ // The compared entries are sorted by absolute impact.
+ tmpEntries = tmpEntries.map((entry) => {
+ var tmpEntry = new Entry(0, entry.name, 0, 0, 0, 0, 0, 0);
+ tmpEntry.page = this.page;
+ return tmpEntry;
+ });
+ tmpEntries = tmpEntries.concat(this.entries);
+ tmpEntries.sort((a, b) => {
+ return a.time - b.time
+ });
+ tmpEntries.forEach(fun);
+ }
+ sort() {
+ this.entries.sort((a, b) => {
+ return b.time - a.time;
+ });
+ }
+ cssClass() {
+ if (this.page.total == this) return 'total';
+ return '';
+ }
+ get isGroup() {
+ return true
+ }
+ getVarianceForProperty(property) {
+ var sum = 0;
+ this.entries.forEach((entry) => {
+ sum += entry[property + 'Variance'] * entry[property +
+ 'Variance'];
+ });
+ return Math.sqrt(sum);
+ }
+ get timeVariancePercent() {
+ if (this._time == 0) return 0;
+ return this.getVarianceForProperty('time') / this._time * 100
+ }
+ get timeVariance() {
+ return this.getVarianceForProperty('time')
+ }
+ }
+
+ class UnclassifiedEntry extends GroupedEntry {
+ constructor(page, color) {
+ super('Unclassified', undefined, color);
+ this.page = page;
+ this._time = undefined;
+ this._count = undefined;
+ }
+ add(entry) {
+ this.entries.push(entry);
+ entry.parent = this;
+ return true;
+ }
+ forEachPageGroup(fun) {
+ this.page.forEachGroup((group) => {
+ if (group == this) return;
+ if (group == this.page.total) return;
+ fun(group);
+ });
+ }
+ get time() {
+ if (this._time === undefined) {
+ this._time = this.page.total._time;
+ this.forEachPageGroup((group) => {
+ this._time -= group._time;
+ });
+ }
+ return this.getCompareWithBaseline(this._time, '_time');
+ }
+ get count() {
+ if (this._count === undefined) {
+ this._count = this.page.total._count;
+ this.forEachPageGroup((group) => {
+ this._count -= group._count;
+ });
+ }
+ return this.getCompareWithBaseline(this._count, '_count');
+ }
+ }
+ </script>
+</head>
+
+<body id="body" onmousemove="handleUpdatePopover(event)" onload="handleBodyLoad()" class="noDiff">
+ <h1>Runtime Stats Komparator</h1>
+
+ <div id="results">
+ <div class="inline">
+ <h2>Data</h2>
+ <form name="fileForm">
+ <p>
+ <input id="uploadInput" type="file" name="files" onchange="handleLoadFile();" accept=".json">
+ </p>
+ </form>
+ </div>
+
+ <div class="inline hidden">
+ <h2>Result</h2>
+ <div class="compareSelector inline">
+ Compare against:&nbsp;<select id="baseline" onchange="handleSelectBaseline(this, event)"></select><br/>
+ <span style="color: #060">Green</span> the selected version above performs
+ better on this measurement.
+ </div>
+ </div>
+
+ <div id="versionSelector" class="inline toggleContentVisibility">
+ <h2>Version Selector</h2>
+ <div class="content hidden">
+ <ul></ul>
+ </div>
+ </div>
+
+ <div id="pageSelector" class="inline toggleContentVisibility">
+ <h2>Page Selector</h2>
+ <div class="content hidden">
+ <ul></ul>
+ </div>
+ </div>
+
+ <div id="view">
+ </div>
+
+ <div id="detailView" class="hidden">
+ <div class="versionDetail inline toggleContentVisibility">
+ <h3><span></span></h3>
+ <div class="content">
+ <table class="versionDetailTable" onclick="handleSelectDetailRow(this, event);">
+ <thead>
+ <tr>
+ <th class="version">Version&nbsp;</th>
+ <th class="position">Pos.&nbsp;</th>
+ <th class="value time">Time▴&nbsp;</th>
+ <th class="value time">Percent&nbsp;</th>
+ <th class="value count">Count&nbsp;</th>
+ </tr>
+ </thead>
+ <tbody></tbody>
+ </table>
+ </div>
+ </div>
+ <div class="pageDetail inline toggleContentVisibility">
+ <h3>Page Comparison for <span></span></h3>
+ <div class="content">
+ <table class="pageDetailTable" onclick="handleSelectDetailRow(this, event);">
+ <thead>
+ <tr>
+ <th class="page">Page&nbsp;</th>
+ <th class="value time">Time&nbsp;</th>
+ <th class="value time">Percent▾&nbsp;</th>
+ <th class="value time hideNoDiff">%/Entry&nbsp;</th>
+ <th class="value count">Count&nbsp;</th>
+ </tr>
+ </thead>
+ <tfoot>
+ <tr>
+ <td class="page">Total:</td>
+ <td class="value time"></td>
+ <td class="value time"></td>
+ <td class="value time hideNoDiff"></td>
+ <td class="value count"></td>
+ </tr>
+ </tfoot>
+ <tbody></tbody>
+ </table>
+ </div>
+ </div>
+ <div class="impactView inline toggleContentVisibility">
+ <h3>Impact list for <span></span></h3>
+ <div class="content">
+ <table class="pageDetailTable" onclick="handleSelectDetailRow(this, event);">
+ <thead>
+ <tr>
+ <th class="page">Name&nbsp;</th>
+ <th class="value time">Time&nbsp;</th>
+ <th class="value time">Percent▾&nbsp;</th>
+ <th class="">Top Pages</th>
+ </tr>
+ </thead>
+ <tbody></tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ <div id="pageVersionGraph" class="graph hidden toggleContentVisibility">
+ <h3><span></span></h3>
+ <div class="content"></div>
+ </div>
+ <div id="pageGraph" class="graph hidden toggleContentVisibility">
+ <h3><span></span></h3>
+ <div class="content"></div>
+ </div>
+ <div id="versionGraph" class="graph hidden toggleContentVisibility">
+ <h3><span></span></h3>
+ <div class="content"></div>
+ </div>
+
+ <div id="column" class="column">
+ <div class="header">
+ <select class="version" onchange="handleSelectVersion(this, event);"></select>
+ <select class="pageVersion" onchange="handleSelectPage(this, event);"></select>
+ </div>
+ <table class="list" onclick="handleSelectRow(this, event);">
+ <thead>
+ <tr>
+ <th class="position">Pos.&nbsp;</th>
+ <th class="name">Name&nbsp;</th>
+ <th class="value time">Time&nbsp;</th>
+ <th class="value time">Percent&nbsp;</th>
+ <th class="value count">Count&nbsp;</th>
+ </tr>
+ </thead>
+ <tbody></tbody>
+ </table>
+ </div>
+ </div>
+
+ <div class="inline">
+ <h2>Usage</h2>
+ <ol>
+ <li>Install scipy, e.g. <code>sudo aptitude install python-scipy</code>
+ <li>Build chrome.</li>
+ <li>Check out a known working version of webpagereply:
+ <pre>git -C $CHROME_DIR/third_party/webpagereplay checkout 7dbd94752d1cde5536ffc623a9e10a51721eff1d</pre>
+ </li>
+ <li>Run <code>callstats.py</code> with a web-page-replay archive:
+ <pre>$V8_DIR/tools/callstats.py run \
+ --replay-bin=$CHROME_SRC/third_party/webpagereplay/replay.py \
+ --replay-wpr=$INPUT_DIR/top25.wpr \
+ --js-flags="" \
+ --with-chrome=$CHROME_SRC/out/Release/chrome \
+ --sites-file=$INPUT_DIR/top25.json</pre>
+ </li>
+ <li>Move results file to a subdirectory: <code>mkdir $VERSION_DIR; mv *.txt $VERSION_DIR</code></li>
+ <li>Repeat from step 1 with a different configuration (e.g. <code>--js-flags="--nolazy"</code>).</li>
+ <li>Create the final results file: <code>./callstats.py json $VERSION_DIR1 $VERSION_DIR2 > result.json</code></li>
+ <li>Use <code>results.json</code> on this site.</code>
+ </ol>
+ </div>
+
+ <div id="popover">
+ <div class="popoverArrow"></div>
+ <table>
+ <tr>
+ <td class="name" colspan="6"></td>
+ </tr>
+ <tr>
+ <td>Page:</td>
+ <td class="page name" colspan="6"></td>
+ </tr>
+ <tr>
+ <td>Version:</td>
+ <td class="version name" colspan="3"></td>
+ <td class="compare version name" colspan="3"></td>
+ </tr>
+ <tr>
+ <td>Time:</td>
+ <td class="time"></td><td>±</td><td class="timeVariance"></td>
+ <td class="compare time"></td><td class="compare"> ± </td><td class="compare timeVariance"></td>
+ </tr>
+ <tr>
+ <td>Percent:</td>
+ <td class="percent"></td><td>±</td><td class="percentVariance"></td>
+ <td class="compare percent"></td><td class="compare"> ± </td><td class="compare percentVariance"></td>
+ </tr>
+ <tr>
+ <td>Percent per Entry:</td>
+ <td class="percentPerEntry"></td><td colspan=2></td>
+ <td class="compare percentPerEntry"></td><td colspan=2></td>
+ </tr>
+ <tr>
+ <td>Count:</td>
+ <td class="count"></td><td>±</td><td class="countVariance"></td>
+ <td class="compare count"></td><td class="compare"> ± </td><td class="compare countVariance"></td>
+ </tr>
+ <tr>
+ <td>Overall Impact:</td>
+ <td class="timeImpact"></td><td>±</td><td class="timePercentImpact"></td>
+ <td class="compare timeImpact"></td><td class="compare"> ± </td><td class="compare timePercentImpact"></td>
+ </tr>
+ </table>
+ </div>
+</body>
+</html>