Commit e6f11a46 authored by Zeynep Cankara's avatar Zeynep Cankara Committed by Commit Bot

[tools][system-analyzer] Add Stats panel

This CL attempts to encapsulate Statistics gathered
to a separate custom web component.

Screenshots: https://imgur.com/a/xlffxDE
Bug: v8:10667

Change-Id: I37816a4fe3d37f2b042d05a7110eda04af303fbb
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2287506Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Commit-Queue: Zeynep Cankara <zcankara@google.com>
Cr-Commit-Position: refs/heads/master@{#68794}
parent 2c20f4f3
...@@ -12,6 +12,7 @@ found in the LICENSE file. --> ...@@ -12,6 +12,7 @@ found in the LICENSE file. -->
<!-- <link rel="icon" type="image/png" href="/images/favicon.png"/> --> <!-- <link rel="icon" type="image/png" href="/images/favicon.png"/> -->
<!-- <script type="module" src="index.js"></script> --> <!-- <script type="module" src="index.js"></script> -->
<script src="helper.js"></script> <script src="helper.js"></script>
<script type="module" src="log-file-reader.mjs"></script> <script type="module" src="log-file-reader.mjs"></script>
<script type="module" src="map-panel.mjs"></script> <script type="module" src="map-panel.mjs"></script>
<script type="module" src="timeline-panel.mjs"></script> <script type="module" src="timeline-panel.mjs"></script>
...@@ -46,46 +47,7 @@ found in the LICENSE file. --> ...@@ -46,46 +47,7 @@ found in the LICENSE file. -->
padding: 10px 10px 60px 10px ; padding: 10px 10px 60px 10px ;
margin: auto; margin: auto;
} }
#stats {
display: flex;
height: 250px;
background-color: #121212;
padding: 10px 10px 10px 10px ;
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
margin: auto;
}
.stats-panel {
background-color: #232323;
margin: auto;
transition: 0.3s;
}
#stats table {
flex: 1;
padding-right: 50px;
max-height: 250px;
display: inline-block;
overflow-y: scroll;
}
#stats table td {
cursor: pointer;
}
#stats .transitionTable {
overflow-y: scroll;
}
#stats .transitionTable tr {
max-width: 200px;
}
#stats .transitionType {
text-align: right;
max-width: 380px;
}
#stats .transitionType tr td:nth-child(2) {
text-align: left;
}
#stats table thead td {
border-bottom: 1px black dotted;
}
/* /*
.indicium-logo { .indicium-logo {
width: 380px; width: 380px;
...@@ -181,7 +143,7 @@ function globalDataUpload(e) { ...@@ -181,7 +143,7 @@ function globalDataUpload(e) {
$('#container').style.display = 'block'; $('#container').style.display = 'block';
// instantiate the app logic // instantiate the app logic
stateGlobal.fileData = e.detail; stateGlobal.fileData = e.detail;
stateGlobal.state = new State('#map-panel', '#stats', '#timeline-panel'); stateGlobal.state = new State('#map-panel','#timeline-panel');
stateGlobal.timeline = handleLoadTextMapProcessor(stateGlobal.fileData.chunk); stateGlobal.timeline = handleLoadTextMapProcessor(stateGlobal.fileData.chunk);
updateDocumentState(); updateDocumentState();
// process the IC explorer // process the IC explorer
...@@ -192,6 +154,12 @@ function globalSearchBarEvent(e) { ...@@ -192,6 +154,12 @@ function globalSearchBarEvent(e) {
if(!e.detail.isValidMap) return; if(!e.detail.isValidMap) return;
document.state.map = e.detail.map; document.state.map = e.detail.map;
} }
function showMaps(e) {
// show maps on the view
document.state.view.transitionView.showMaps(e.detail);
}
</script> </script>
</head> </head>
<body> <body>
...@@ -203,11 +171,8 @@ function globalSearchBarEvent(e) { ...@@ -203,11 +171,8 @@ function globalSearchBarEvent(e) {
<br></br> <br></br>
</section> </section>
<div id="container" style="display: none;"> <div id="container" style="display: none;">
<div class="stats-panel">
<section id="stats"><h2>Stats</h2></section>
</div>
<timeline-panel id="timeline-panel"></timeline-panel> <timeline-panel id="timeline-panel"></timeline-panel>
<map-panel id="map-panel" onclick="globalSearchBarEvent(event)"></map-panel> <map-panel id="map-panel" onclick="globalSearchBarEvent(event)" onchange="showMaps(event)"></map-panel>
<ic-panel id="ic-panel"></ic-panel> <ic-panel id="ic-panel"></ic-panel>
</div> </div>
</div> </div>
......
...@@ -7,12 +7,12 @@ ...@@ -7,12 +7,12 @@
// const kChunkWidth = 10; // const kChunkWidth = 10;
class State { class State {
constructor(mapPanelId, statPanelId, timelinePanelId) { constructor(mapPanelId, timelinePanelId) {
this._nofChunks = 400; this._nofChunks = 400;
this._map = undefined; this._map = undefined;
this._timeline = undefined; this._timeline = undefined;
this._chunks = undefined; this._chunks = undefined;
this._view = new View(this, mapPanelId, statPanelId, timelinePanelId); this._view = new View(this, mapPanelId, timelinePanelId);
this._navigation = new Navigation(this, this.view); this._navigation = new Navigation(this, this.view);
} }
get timeline() { get timeline() {
...@@ -192,16 +192,14 @@ class Navigation { ...@@ -192,16 +192,14 @@ class Navigation {
} }
class View { class View {
constructor(state, mapPanelId, statPanelId, timelinePanelId) { constructor(state, mapPanelId, timelinePanelId) {
this.mapPanel_ = $(mapPanelId); this.mapPanel_ = $(mapPanelId);
this.statPanel_ = $(statPanelId);
this.timelinePanel_ = $(timelinePanelId); this.timelinePanel_ = $(timelinePanelId);
this.state = state; this.state = state;
setInterval(this.updateOverviewWindow(timelinePanelId), 50); setInterval(this.updateOverviewWindow(timelinePanelId), 50);
this.backgroundCanvas = document.createElement('canvas'); this.backgroundCanvas = document.createElement('canvas');
this.transitionView = this.transitionView =
new TransitionView(state, this.mapPanel_.transitionViewSelect); new TransitionView(state, this.mapPanel_.transitionViewSelect);
this.statsView = new StatsView(state, this.statPanel_);
this.isLocked = false; this.isLocked = false;
} }
get chunks() { get chunks() {
...@@ -215,7 +213,7 @@ class View { ...@@ -215,7 +213,7 @@ class View {
} }
updateStats() { updateStats() {
this.statsView.update(); this.mapPanel_.timeline = this.state.timeline;
} }
updateMapDetails() { updateMapDetails() {
...@@ -658,95 +656,6 @@ class TransitionView { ...@@ -658,95 +656,6 @@ class TransitionView {
} }
} }
class StatsView {
constructor(state, node) {
this.state = state;
this.node = node;
}
get timeline() {
return this.state.timeline
}
get transitionView() {
return this.state.view.transitionView;
}
update() {
removeAllChildren(this.node);
this.updateGeneralStats();
this.updateNamedTransitionsStats();
}
updateGeneralStats() {
let pairs = [
['Total', null, e => true],
['Transitions', 'black', e => e.edge && e.edge.isTransition()],
['Fast to Slow', 'violet', e => e.edge && e.edge.isFastToSlow()],
['Slow to Fast', 'orange', e => e.edge && e.edge.isSlowToFast()],
['Initial Map', 'yellow', e => e.edge && e.edge.isInitial()],
[
'Replace Descriptors', 'red',
e => e.edge && e.edge.isReplaceDescriptors()
],
['Copy as Prototype', 'red', e => e.edge && e.edge.isCopyAsPrototype()],
[
'Optimize as Prototype', null,
e => e.edge && e.edge.isOptimizeAsPrototype()
],
['Deprecated', null, e => e.isDeprecated()],
['Bootstrapped', 'green', e => e.isBootstrapped()],
];
let text = '';
let tableNode = table('transitionType');
tableNode.innerHTML =
'<thead><tr><td>Color</td><td>Type</td><td>Count</td><td>Percent</td></tr></thead>';
let name, filter;
let total = this.timeline.size();
pairs.forEach(([name, color, filter]) => {
let row = tr();
if (color !== null) {
row.appendChild(td(div(['colorbox', color])));
} else {
row.appendChild(td(''));
}
row.onclick = (e) => {
// lazily compute the stats
let node = e.target.parentNode;
if (node.maps == undefined) {
node.maps = this.timeline.filterUniqueTransitions(filter);
}
this.transitionView.showMaps(node.maps);
};
row.appendChild(td(name));
let count = this.timeline.count(filter);
row.appendChild(td(count));
let percent = Math.round(count / total * 1000) / 10;
row.appendChild(td(percent.toFixed(1) + '%'));
tableNode.appendChild(row);
});
this.node.appendChild(tableNode);
};
updateNamedTransitionsStats() {
let tableNode = table('transitionTable');
let nameMapPairs = Array.from(this.timeline.transitions.entries());
tableNode.innerHTML =
'<thead><tr><td>Propery Name</td><td>#</td></tr></thead>';
nameMapPairs.sort((a, b) => b[1].length - a[1].length).forEach(([
name, maps
]) => {
let row = tr();
row.maps = maps;
row.addEventListener(
'click',
e => this.transitionView.showMaps(
e.target.parentNode.maps.map(map => map.to)));
row.appendChild(td(name));
row.appendChild(td(maps.length));
tableNode.appendChild(row);
});
this.node.appendChild(tableNode);
}
}
// ========================================================================= // =========================================================================
function transitionTypeToColor(type) { function transitionTypeToColor(type) {
......
<!-- Copyright 2020 the V8 project authors. All rights reserved. <!-- Copyright 2020 the V8 project authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. --> found in the LICENSE file. -->
<style> <style>
.map-panel { .map-panel {
background-color: #232323; background-color: #232323;
...@@ -195,6 +194,8 @@ h2 { ...@@ -195,6 +194,8 @@ h2 {
width: 200px; width: 200px;
} }
</style> </style>
<stats-panel id="stats-panel" onchange="showMaps(event)"></stats-panel>
<div class="map-panel"> <div class="map-panel">
<section id="map-panel"> <section id="map-panel">
<h2>Map Panel</h2> <h2>Map Panel</h2>
......
// Copyright 2020 the V8 project authors. All rights reserved. // Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import "./stats-panel.mjs";
defineCustomElement('map-panel', (templateText) => defineCustomElement('map-panel', (templateText) =>
class MapPanel extends HTMLElement { class MapPanel extends HTMLElement {
...@@ -40,6 +41,24 @@ defineCustomElement('map-panel', (templateText) => ...@@ -40,6 +41,24 @@ defineCustomElement('map-panel', (templateText) =>
get tooltipContentsSelect() { get tooltipContentsSelect() {
return this.$('#tooltipContents'); return this.$('#tooltipContents');
} }
get tooltipContentsSelect() {
return this.$('#tooltipContents');
}
get statsPanelSelect() {
return this.$('#stats-panel');
}
// send a timeline to the stats-panel
get timeline() {
return this.statsPanelSelect.timeline;
}
set timeline(value) {
console.assert(value !== undefined, "timeline undefined!");
this.statsPanelSelect.timeline = value;
this.statsPanelSelect.update();
}
handleTransitionViewChange(e){ handleTransitionViewChange(e){
this.tooltipSelect.style.left = e.pageX + "px"; this.tooltipSelect.style.left = e.pageX + "px";
...@@ -67,4 +86,5 @@ defineCustomElement('map-panel', (templateText) => ...@@ -67,4 +86,5 @@ defineCustomElement('map-panel', (templateText) =>
this.dispatchEvent(new CustomEvent( this.dispatchEvent(new CustomEvent(
'click', {bubbles: true, composed: true, detail: dataModel})); 'click', {bubbles: true, composed: true, detail: dataModel}));
} }
}); });
<!-- Copyright 2020 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. -->
<style>
h2 {
background-color: #BB86FC;
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
color: black;
padding: 15px 25px;
text-align: center;
text-decoration: none;
display: inline-block;
}
#stats {
display: flex;
height: 250px;
background-color: #121212;
padding: 10px 10px 10px 10px ;
margin: auto;
}
#stats-panel {
background-color: #121212;
padding: 10px 10px 10px 10px ;
margin: auto;
}
.stats-panel {
background-color:#232323;
padding: 20px 20px 20px 20px ;
margin: auto;
}
#stats table {
flex: 1;
padding-right: 50px;
max-height: 250px;
display: inline-block;
overflow-y: scroll;
}
#stats table td {
cursor: pointer;
}
#stats .transitionTable {
overflow-y: scroll;
}
#stats .transitionTable tr {
max-width: 200px;
}
#stats .transitionType {
text-align: right;
max-width: 380px;
}
#stats .transitionType tr td:nth-child(2) {
text-align: left;
}
#stats table thead td {
border-bottom: 1px black dotted;
}
</style>
<div id="container">
<div class="stats-panel">
<section id="stats-panel">
<h2>Stats Panel</h2>
<h3>Stats</h3>
<section id="stats">
</section>
</section>
</div>
</div>
// Copyright 2020 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.
defineCustomElement('stats-panel', (templateText) =>
class StatsPanel extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = templateText;
this.timeline_ = undefined;
}
$(id) {
return this.shadowRoot.querySelector(id);
}
querySelectorAll(query) {
return this.shadowRoot.querySelectorAll(query);
}
get statsSelect() {
return this.$('#stats');
}
// decouple stats panel
removeAllChildren(node) {
while (node.lastChild) {
node.removeChild(node.lastChild);
}
}
set timeline(value){
this.timeline_ = value;
}
get timeline(){
return this.timeline_;
}
set timeline(value){
this.timeline_ = value;
}
get timeline(){
return this.timeline_;
}
update() {
this.removeAllChildren(this.statsSelect);
this.updateGeneralStats();
this.updateNamedTransitionsStats();
}
updateGeneralStats() {
console.assert(this.timeline_ !== undefined, "Timeline not set yet!");
let pairs = [
['Total', null, e => true],
['Transitions', 'black', e => e.edge && e.edge.isTransition()],
['Fast to Slow', 'violet', e => e.edge && e.edge.isFastToSlow()],
['Slow to Fast', 'orange', e => e.edge && e.edge.isSlowToFast()],
['Initial Map', 'yellow', e => e.edge && e.edge.isInitial()],
[
'Replace Descriptors', 'red',
e => e.edge && e.edge.isReplaceDescriptors()
],
['Copy as Prototype', 'red', e => e.edge && e.edge.isCopyAsPrototype()],
[
'Optimize as Prototype', null,
e => e.edge && e.edge.isOptimizeAsPrototype()
],
['Deprecated', null, e => e.isDeprecated()],
['Bootstrapped', 'green', e => e.isBootstrapped()],
];
let text = '';
let tableNode = table('transitionType');
tableNode.innerHTML =
'<thead><tr><td>Color</td><td>Type</td><td>Count</td><td>Percent</td></tr></thead>';
let name, filter;
//TODO(zc) timeline
let total = this.timeline.size();
pairs.forEach(([name, color, filter]) => {
let row = tr();
if (color !== null) {
row.appendChild(td(div(['colorbox', color])));
} else {
row.appendChild(td(''));
}
row.onclick = (e) => {
// lazily compute the stats
let node = e.target.parentNode;
if (node.maps == undefined) {
node.maps = this.timeline.filterUniqueTransitions(filter);
}
this.dispatchEvent(new CustomEvent(
'change', {bubbles: true, composed: true, detail: node.maps}));
};
row.appendChild(td(name));
let count = this.timeline.count(filter);
row.appendChild(td(count));
let percent = Math.round(count / total * 1000) / 10;
row.appendChild(td(percent.toFixed(1) + '%'));
tableNode.appendChild(row);
});
this.statsSelect.appendChild(tableNode);
}
updateNamedTransitionsStats() {
let tableNode = table('transitionTable');
let nameMapPairs = Array.from(this.timeline.transitions.entries());
tableNode.innerHTML =
'<thead><tr><td>Propery Name</td><td>#</td></tr></thead>';
nameMapPairs.sort((a, b) => b[1].length - a[1].length).forEach(([
name, maps
]) => {
let row = tr();
row.maps = maps;
row.addEventListener(
'click',
e => this.dispatchEvent(new CustomEvent(
'change', {bubbles: true, composed: true, detail: e.target.parentNode.maps.map(map => map.to)})));
row.appendChild(td(name));
row.appendChild(td(maps.length));
tableNode.appendChild(row);
});
this.statsSelect.appendChild(tableNode);
}
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment