Commit bca72425 authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[tools] Improve HeapStats category percentages

- Provide sorted instance type contribution per GC
- Visualize percentages per InstanceType based on the selected GC
- Visualize percentags per category
- Use some more arrow functions
- Introduce helper.js file

Bug: v8:7266
Change-Id: I26099cc64d9545b2de9e4574da2faf52d54ad198
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/949222
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51743}
parent 7c914dd0
...@@ -92,6 +92,7 @@ const CATEGORIES = new Map([ ...@@ -92,6 +92,7 @@ const CATEGORIES = new Map([
'ACCESSOR_PAIR_TYPE', 'ACCESSOR_PAIR_TYPE',
'ALLOCATION_MEMENTO_TYPE', 'ALLOCATION_MEMENTO_TYPE',
'ALLOCATION_SITE_TYPE', 'ALLOCATION_SITE_TYPE',
'BOILERPLATE_DESCRIPTION_TYPE',
'BOILERPLATE_ELEMENTS_TYPE', 'BOILERPLATE_ELEMENTS_TYPE',
'BOILERPLATE_PROPERTY_ARRAY_TYPE', 'BOILERPLATE_PROPERTY_ARRAY_TYPE',
'BOILERPLATE_PROPERTY_DICTIONARY_TYPE', 'BOILERPLATE_PROPERTY_DICTIONARY_TYPE',
......
...@@ -13,6 +13,7 @@ found in the LICENSE file. --> ...@@ -13,6 +13,7 @@ found in the LICENSE file. -->
border-bottom: dashed 1px #666666; border-bottom: dashed 1px #666666;
padding: 10px; padding: 10px;
overflow: hidden; overflow: hidden;
position: relative;
} }
.box:nth-of-type(1) { .box:nth-of-type(1) {
...@@ -41,48 +42,40 @@ found in the LICENSE file. --> ...@@ -41,48 +42,40 @@ found in the LICENSE file. -->
font-weight: bold; font-weight: bold;
} }
.boxDiv { .instanceTypeSelectBox {
padding: 0px 5px 2px 0px; position: relative;
overflow: hidden;
float: left; float: left;
padding: 0px 5px 2px 0px;
margin: 3px; margin: 3px;
border-radius: 3px;
} }
.percent100 { .instanceTypeSelectBox > label {
background: linear-gradient(70deg, #9cf 100%, #e0edfe 100%) font-size: xx-small;
}
.percent90 {
background: linear-gradient(70deg, #9cf 90%, #e0edfe 90%)
}
.percent80 {
background: linear-gradient(70deg, #9cf 80%, #e0edfe 80%)
}
.percent70 {
background: linear-gradient(70deg, #9cf 70%, #e0edfe 70%)
}
.percent60 {
background: linear-gradient(70deg, #9cf 60%, #e0edfe 60%)
}
.percent50 {
background: linear-gradient(70deg, #9cf 50%, #e0edfe 50%)
}
.percent40 {
background: linear-gradient(70deg, #9cf 40%, #e0edfe 40%)
}
.percent30 {
background: linear-gradient(70deg, #9cf 30%, #e0edfe 30%)
}
.percent20 {
background: linear-gradient(70deg, #9cf 20%, #e0edfe 20%)
} }
.percent10 {
background: linear-gradient(70deg, #9cf 10%, #e0edfe 10%) .instanceTypeSelectBox > input {
vertical-align: middle;
} }
.percent0 {
background: linear-gradient(70deg, #9cf 0%, #e0edfe 0%) .percentBackground {
position: absolute;
width: 200%;
height: 100%;
left: 0%;
top: 0px;
margin-left: -100%;
transition: all 1s ease-in-out;
} }
.boxDiv > label { .instanceTypeSelectBox > .percentBackground {
font-size: xx-small; background: linear-gradient(90deg, #68b0f7 50%, #b3d9ff 50%);
z-index: -1;
}
.box > .percentBackground {
background: linear-gradient(90deg, #e0edfe 50%, #fff 50%);
z-index: -2;
} }
#categories { #categories {
......
...@@ -24,7 +24,7 @@ class DetailsSelection extends HTMLElement { ...@@ -24,7 +24,7 @@ class DetailsSelection extends HTMLElement {
this.$('#merge-categories') this.$('#merge-categories')
.addEventListener('change', e => this.notifySelectionChanged(e)); .addEventListener('change', e => this.notifySelectionChanged(e));
this.$('#category-filter-btn') this.$('#category-filter-btn')
.addEventListener('click', e => this.filterCurrentSeclection(e)); .addEventListener('click', e => this.filterCurrentSelection(e));
this.$('#category-auto-filter-btn') this.$('#category-auto-filter-btn')
.addEventListener('click', e => this.filterTop20Categories(e)); .addEventListener('click', e => this.filterTop20Categories(e));
} }
...@@ -54,6 +54,26 @@ class DetailsSelection extends HTMLElement { ...@@ -54,6 +54,26 @@ class DetailsSelection extends HTMLElement {
return this.selectedIsolate.gcs[this.selection.gc][this.selection.data_set]; return this.selectedIsolate.gcs[this.selection.gc][this.selection.data_set];
} }
$(id) {
return this.shadowRoot.querySelector(id);
}
querySelectorAll(query) {
return this.shadowRoot.querySelectorAll(query);
}
get datasetSelect() {
return this.$('#dataset-select');
}
get isolateSelect() {
return this.$('#isolate-select');
}
get gcSelect() {
return this.$('#gc-select');
}
buildCategory(name) { buildCategory(name) {
const div = document.createElement('div'); const div = document.createElement('div');
div.id = name; div.id = name;
...@@ -82,25 +102,13 @@ class DetailsSelection extends HTMLElement { ...@@ -82,25 +102,13 @@ class DetailsSelection extends HTMLElement {
const innerDiv = document.createElement('div'); const innerDiv = document.createElement('div');
div.appendChild(innerDiv); div.appendChild(innerDiv);
innerDiv.id = name + 'Content'; innerDiv.id = name + 'Content';
const percentDiv = document.createElement('div');
div.appendChild(percentDiv);
percentDiv.className = 'percentBackground';
percentDiv.id = name + 'PercentBackground';
return div; return div;
} }
$(id) {
return this.shadowRoot.querySelector(id);
}
get datasetSelect() {
return this.$('#dataset-select');
}
get isolateSelect() {
return this.$('#isolate-select');
}
get gcSelect() {
return this.$('#gc-select');
}
dataChanged() { dataChanged() {
this.selection = {categories: {}}; this.selection = {categories: {}};
this.resetUI(true); this.resetUI(true);
...@@ -147,7 +155,12 @@ class DetailsSelection extends HTMLElement { ...@@ -147,7 +155,12 @@ class DetailsSelection extends HTMLElement {
'#gc-select', '#gc-select',
Object.keys(this.selectedIsolate.gcs) Object.keys(this.selectedIsolate.gcs)
.map(id => [id, this.selectedIsolate.gcs[id].time]), .map(id => [id, this.selectedIsolate.gcs[id].time]),
(key, time, index) => index + ': ' + (time * kMillis2Seconds) + 's'); (key, time, index) => {
return (index + ': ').padStart(4, '0') +
formatSeconds(time).padStart(6, '0') + ' ' +
formatBytes(this.selectedIsolate.gcs[key].live.overall)
.padStart(9, '0');
});
this.populateCategories(); this.populateCategories();
this.notifySelectionChanged(); this.notifySelectionChanged();
} }
...@@ -166,11 +179,12 @@ class DetailsSelection extends HTMLElement { ...@@ -166,11 +179,12 @@ class DetailsSelection extends HTMLElement {
this.selection.gc = this.gcSelect.value; this.selection.gc = this.gcSelect.value;
this.setButtonState(false); this.setButtonState(false);
this.updatePercentagesInCategory(); this.updatePercentagesInCategory();
this.updatePercentagesInInstanceTypes();
this.dispatchEvent(new CustomEvent( this.dispatchEvent(new CustomEvent(
'change', {bubbles: true, composed: true, detail: this.selection})); 'change', {bubbles: true, composed: true, detail: this.selection}));
} }
filterCurrentSeclection(e) { filterCurrentSelection(e) {
const minSize = this.$('#category-filter').value * KB; const minSize = this.$('#category-filter').value * KB;
this.filterCurrentSelectionWithThresold(minSize); this.filterCurrentSelectionWithThresold(minSize);
} }
...@@ -192,11 +206,15 @@ class DetailsSelection extends HTMLElement { ...@@ -192,11 +206,15 @@ class DetailsSelection extends HTMLElement {
if (minSize === 0) return; if (minSize === 0) return;
this.selection.category_names.forEach((_, category) => { this.selection.category_names.forEach((_, category) => {
for (let checkbox of this.shadowRoot.querySelectorAll( for (let checkbox of this.querySelectorAll(
'input[name=' + category + 'Checkbox]')) { 'input[name=' + category + 'Checkbox]')) {
checkbox.checked = checkbox.checked =
this.selectedData.instance_type_data[checkbox.instance_type] this.selectedData.instance_type_data[checkbox.instance_type]
.overall > minSize; .overall > minSize;
console.log(
checkbox.instance_type, checkbox.checked,
this.selectedData.instance_type_data[checkbox.instance_type]
.overall);
} }
}); });
this.notifySelectionChanged(); this.notifySelectionChanged();
...@@ -207,7 +225,7 @@ class DetailsSelection extends HTMLElement { ...@@ -207,7 +225,7 @@ class DetailsSelection extends HTMLElement {
let overall = 0; let overall = 0;
// Reset all categories. // Reset all categories.
this.selection.category_names.forEach((_, category) => { this.selection.category_names.forEach((_, category) => {
this.$(`#${category}PercentContent`).innerHTML = '0%'; overalls[category] = 0;
}); });
// Only update categories that have selections. // Only update categories that have selections.
Object.entries(this.selection.categories).forEach(([category, value]) => { Object.entries(this.selection.categories).forEach(([category, value]) => {
...@@ -220,16 +238,30 @@ class DetailsSelection extends HTMLElement { ...@@ -220,16 +238,30 @@ class DetailsSelection extends HTMLElement {
overall += overalls[category]; overall += overalls[category];
}); });
Object.entries(overalls).forEach(([category, category_overall]) => { Object.entries(overalls).forEach(([category, category_overall]) => {
let percents = category_overall / overall * 100;
this.$(`#${category}PercentContent`).innerHTML = this.$(`#${category}PercentContent`).innerHTML =
`${(category_overall / overall * 100).toFixed(1)}%`; `${percents.toFixed(1)}%`;
this.$('#' + category + 'PercentBackground').style.left = percents + '%';
});
}
updatePercentagesInInstanceTypes() {
const instanceTypeData = this.selectedData.instance_type_data;
const maxInstanceType = this.selectedData.singleInstancePeakMemory;
this.querySelectorAll('.instanceTypeSelectBox input').forEach(checkbox => {
let instanceType = checkbox.value;
let instanceTypeSize = instanceTypeData[instanceType].overall;
let percents = instanceTypeSize / maxInstanceType;
let percentDiv = checkbox.parentNode.querySelector('.percentBackground');
percentDiv.style.left = (percents * 100) + '%';
}); });
} }
selectedInCategory(category) { selectedInCategory(category) {
const selected = this.shadowRoot.querySelectorAll( let tmp = [];
'input[name=' + category + 'Checkbox]:checked'); this.querySelectorAll('input[name=' + category + 'Checkbox]:checked')
var tmp = []; .forEach(checkbox => tmp.push(checkbox.value));
for (var val of selected.values()) tmp.push(val.value);
return tmp; return tmp;
} }
...@@ -291,29 +323,20 @@ class DetailsSelection extends HTMLElement { ...@@ -291,29 +323,20 @@ class DetailsSelection extends HTMLElement {
} }
unselectCategory(category) { unselectCategory(category) {
for (let checkbox of this.shadowRoot.querySelectorAll( this.querySelectorAll('input[name=' + category + 'Checkbox]')
'input[name=' + category + 'Checkbox]')) { .forEach(checkbox => checkbox.checked = false);
checkbox.checked = false;
}
this.notifySelectionChanged(); this.notifySelectionChanged();
} }
selectCategory(category) { selectCategory(category) {
for (let checkbox of this.shadowRoot.querySelectorAll( this.querySelectorAll('input[name=' + category + 'Checkbox]')
'input[name=' + category + 'Checkbox]')) { .forEach(checkbox => checkbox.checked = true);
checkbox.checked = true;
}
this.notifySelectionChanged(); this.notifySelectionChanged();
} }
createCheckBox(instance_type, category) { createCheckBox(instance_type, category) {
const div = document.createElement('div'); const div = document.createElement('div');
div.classList.add('boxDiv'); div.classList.add('instanceTypeSelectBox');
let peakMemory =
this.selectedIsolate.getInstanceTypePeakMemory(instance_type);
let maxInstanceType = this.selectedIsolate.singleInstanceTypePeakMemory;
let percentRounded = Math.round(peakMemory / maxInstanceType * 10) * 10;
div.classList.add('percent' + percentRounded);
const input = document.createElement('input'); const input = document.createElement('input');
div.appendChild(input); div.appendChild(input);
input.type = 'checkbox'; input.type = 'checkbox';
...@@ -327,6 +350,9 @@ class DetailsSelection extends HTMLElement { ...@@ -327,6 +350,9 @@ class DetailsSelection extends HTMLElement {
div.appendChild(label); div.appendChild(label);
label.innerText = instance_type; label.innerText = instance_type;
label.htmlFor = instance_type + 'Checkbox'; label.htmlFor = instance_type + 'Checkbox';
const percentDiv = document.createElement('div');
percentDiv.className = 'percentBackground';
div.appendChild(percentDiv);
return div; return div;
} }
......
// Copyright 2018 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.
const KB = 1024;
const MB = KB * KB;
const GB = MB * KB;
const kMillis2Seconds = 1 / 1000;
function formatBytes(bytes) {
const units = ['B', 'KiB', 'MiB', 'GiB'];
const divisor = 1024;
let index = 0;
while (index < units.length && bytes >= divisor) {
index++;
bytes /= divisor;
}
return bytes.toFixed(2) + units[index];
}
function formatSeconds(millis) {
return (millis * kMillis2Seconds).toFixed(2) + 's';
}
...@@ -16,6 +16,8 @@ found in the LICENSE file. --> ...@@ -16,6 +16,8 @@ found in the LICENSE file. -->
integrity="sha256-N1z6ddQzX83fjw8v7uSNe7/MgOmMKdwFUv1+AJMDqNM=" integrity="sha256-N1z6ddQzX83fjw8v7uSNe7/MgOmMKdwFUv1+AJMDqNM="
crossorigin="anonymous"></script> crossorigin="anonymous"></script>
<script src="helper.js"></script>
<link rel="import" href="details-selection.html"> <link rel="import" href="details-selection.html">
<link rel="import" href="global-timeline.html"> <link rel="import" href="global-timeline.html">
<link rel="import" href="histogram-viewer.html"> <link rel="import" href="histogram-viewer.html">
......
...@@ -4,10 +4,6 @@ ...@@ -4,10 +4,6 @@
'use strict'; 'use strict';
const KB = 1024;
const MB = KB * KB;
const kMillis2Seconds = 1 / 1000;
class Isolate { class Isolate {
constructor(address) { constructor(address) {
this.address = address; this.address = address;
...@@ -33,8 +29,7 @@ class Isolate { ...@@ -33,8 +29,7 @@ class Isolate {
getLabel() { getLabel() {
let label = `${this.address}: gc=#${Object.keys(this.gcs).length}`; let label = `${this.address}: gc=#${Object.keys(this.gcs).length}`;
const peakSizeMB = Math.round(this.peakMemory / 1024 / 1024 * 100) / 100; label += ` peak=${formatBytes(this.peakMemory)}`
label += ` peak=${peakSizeMB}MB`
return label; return label;
} }
...@@ -53,18 +48,20 @@ class Isolate { ...@@ -53,18 +48,20 @@ class Isolate {
finalizeDataSet(data_set) { finalizeDataSet(data_set) {
// Create a ranked instance type array that sorts instance types by // Create a ranked instance type array that sorts instance types by
// memory size (overall). // memory size (overall).
data_set.ranked_instance_types = let data = data_set.instance_type_data;
[...data_set.non_empty_instance_types].sort(function(a, b) { let ranked_instance_types =
if (data_set.instance_type_data[a].overall > [...data_set.non_empty_instance_types].sort((a, b) => {
data_set.instance_type_data[b].overall) { return data[a].overall - data[b].overall;
return 1;
} else if (
data_set.instance_type_data[a].overall <
data_set.instance_type_data[b].overall) {
return -1;
}
return 0;
}); });
// Reassemble the instance_type list sorted by size.
let sorted_data = Object.create(null);
let max = 0;
ranked_instance_types.forEach((name) => {
let entry = sorted_data[name] = data[name];
max = Math.max(max, entry.overall);
});
data_set.instance_type_data = data;
data_set.singleInstancePeakMemory = max;
Object.entries(data_set.instance_type_data).forEach(([name, entry]) => { Object.entries(data_set.instance_type_data).forEach(([name, entry]) => {
this.checkHistogram( this.checkHistogram(
......
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