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([
'ACCESSOR_PAIR_TYPE',
'ALLOCATION_MEMENTO_TYPE',
'ALLOCATION_SITE_TYPE',
'BOILERPLATE_DESCRIPTION_TYPE',
'BOILERPLATE_ELEMENTS_TYPE',
'BOILERPLATE_PROPERTY_ARRAY_TYPE',
'BOILERPLATE_PROPERTY_DICTIONARY_TYPE',
......
......@@ -13,6 +13,7 @@ found in the LICENSE file. -->
border-bottom: dashed 1px #666666;
padding: 10px;
overflow: hidden;
position: relative;
}
.box:nth-of-type(1) {
......@@ -41,48 +42,40 @@ found in the LICENSE file. -->
font-weight: bold;
}
.boxDiv {
padding: 0px 5px 2px 0px;
.instanceTypeSelectBox {
position: relative;
overflow: hidden;
float: left;
padding: 0px 5px 2px 0px;
margin: 3px;
border-radius: 3px;
}
.percent100 {
background: linear-gradient(70deg, #9cf 100%, #e0edfe 100%)
}
.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%)
.instanceTypeSelectBox > label {
font-size: xx-small;
}
.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 {
font-size: xx-small;
.instanceTypeSelectBox > .percentBackground {
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 {
......
......@@ -24,7 +24,7 @@ class DetailsSelection extends HTMLElement {
this.$('#merge-categories')
.addEventListener('change', e => this.notifySelectionChanged(e));
this.$('#category-filter-btn')
.addEventListener('click', e => this.filterCurrentSeclection(e));
.addEventListener('click', e => this.filterCurrentSelection(e));
this.$('#category-auto-filter-btn')
.addEventListener('click', e => this.filterTop20Categories(e));
}
......@@ -54,6 +54,26 @@ class DetailsSelection extends HTMLElement {
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) {
const div = document.createElement('div');
div.id = name;
......@@ -82,25 +102,13 @@ class DetailsSelection extends HTMLElement {
const innerDiv = document.createElement('div');
div.appendChild(innerDiv);
innerDiv.id = name + 'Content';
const percentDiv = document.createElement('div');
div.appendChild(percentDiv);
percentDiv.className = 'percentBackground';
percentDiv.id = name + 'PercentBackground';
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() {
this.selection = {categories: {}};
this.resetUI(true);
......@@ -147,7 +155,12 @@ class DetailsSelection extends HTMLElement {
'#gc-select',
Object.keys(this.selectedIsolate.gcs)
.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.notifySelectionChanged();
}
......@@ -166,11 +179,12 @@ class DetailsSelection extends HTMLElement {
this.selection.gc = this.gcSelect.value;
this.setButtonState(false);
this.updatePercentagesInCategory();
this.updatePercentagesInInstanceTypes();
this.dispatchEvent(new CustomEvent(
'change', {bubbles: true, composed: true, detail: this.selection}));
}
filterCurrentSeclection(e) {
filterCurrentSelection(e) {
const minSize = this.$('#category-filter').value * KB;
this.filterCurrentSelectionWithThresold(minSize);
}
......@@ -192,11 +206,15 @@ class DetailsSelection extends HTMLElement {
if (minSize === 0) return;
this.selection.category_names.forEach((_, category) => {
for (let checkbox of this.shadowRoot.querySelectorAll(
for (let checkbox of this.querySelectorAll(
'input[name=' + category + 'Checkbox]')) {
checkbox.checked =
this.selectedData.instance_type_data[checkbox.instance_type]
.overall > minSize;
console.log(
checkbox.instance_type, checkbox.checked,
this.selectedData.instance_type_data[checkbox.instance_type]
.overall);
}
});
this.notifySelectionChanged();
......@@ -207,7 +225,7 @@ class DetailsSelection extends HTMLElement {
let overall = 0;
// Reset all categories.
this.selection.category_names.forEach((_, category) => {
this.$(`#${category}PercentContent`).innerHTML = '0%';
overalls[category] = 0;
});
// Only update categories that have selections.
Object.entries(this.selection.categories).forEach(([category, value]) => {
......@@ -220,16 +238,30 @@ class DetailsSelection extends HTMLElement {
overall += overalls[category];
});
Object.entries(overalls).forEach(([category, category_overall]) => {
let percents = category_overall / overall * 100;
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) {
const selected = this.shadowRoot.querySelectorAll(
'input[name=' + category + 'Checkbox]:checked');
var tmp = [];
for (var val of selected.values()) tmp.push(val.value);
let tmp = [];
this.querySelectorAll('input[name=' + category + 'Checkbox]:checked')
.forEach(checkbox => tmp.push(checkbox.value));
return tmp;
}
......@@ -291,29 +323,20 @@ class DetailsSelection extends HTMLElement {
}
unselectCategory(category) {
for (let checkbox of this.shadowRoot.querySelectorAll(
'input[name=' + category + 'Checkbox]')) {
checkbox.checked = false;
}
this.querySelectorAll('input[name=' + category + 'Checkbox]')
.forEach(checkbox => checkbox.checked = false);
this.notifySelectionChanged();
}
selectCategory(category) {
for (let checkbox of this.shadowRoot.querySelectorAll(
'input[name=' + category + 'Checkbox]')) {
checkbox.checked = true;
}
this.querySelectorAll('input[name=' + category + 'Checkbox]')
.forEach(checkbox => checkbox.checked = true);
this.notifySelectionChanged();
}
createCheckBox(instance_type, category) {
const div = document.createElement('div');
div.classList.add('boxDiv');
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);
div.classList.add('instanceTypeSelectBox');
const input = document.createElement('input');
div.appendChild(input);
input.type = 'checkbox';
......@@ -327,6 +350,9 @@ class DetailsSelection extends HTMLElement {
div.appendChild(label);
label.innerText = instance_type;
label.htmlFor = instance_type + 'Checkbox';
const percentDiv = document.createElement('div');
percentDiv.className = 'percentBackground';
div.appendChild(percentDiv);
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. -->
integrity="sha256-N1z6ddQzX83fjw8v7uSNe7/MgOmMKdwFUv1+AJMDqNM="
crossorigin="anonymous"></script>
<script src="helper.js"></script>
<link rel="import" href="details-selection.html">
<link rel="import" href="global-timeline.html">
<link rel="import" href="histogram-viewer.html">
......
......@@ -4,10 +4,6 @@
'use strict';
const KB = 1024;
const MB = KB * KB;
const kMillis2Seconds = 1 / 1000;
class Isolate {
constructor(address) {
this.address = address;
......@@ -33,8 +29,7 @@ class Isolate {
getLabel() {
let label = `${this.address}: gc=#${Object.keys(this.gcs).length}`;
const peakSizeMB = Math.round(this.peakMemory / 1024 / 1024 * 100) / 100;
label += ` peak=${peakSizeMB}MB`
label += ` peak=${formatBytes(this.peakMemory)}`
return label;
}
......@@ -53,18 +48,20 @@ class Isolate {
finalizeDataSet(data_set) {
// Create a ranked instance type array that sorts instance types by
// memory size (overall).
data_set.ranked_instance_types =
[...data_set.non_empty_instance_types].sort(function(a, b) {
if (data_set.instance_type_data[a].overall >
data_set.instance_type_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;
let data = data_set.instance_type_data;
let ranked_instance_types =
[...data_set.non_empty_instance_types].sort((a, b) => {
return data[a].overall - data[b].overall;
});
// 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]) => {
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