Commit b80a698d authored by Michael Lippautz's avatar Michael Lippautz Committed by Commit Bot

[tools] V8 heap stats visualizer

Visualizes --trace-gc-object-stats output.

No-try: true
Bug: v8:7266
Change-Id: I5beb15d63e645484f07fe5f2341105c4cfe0b550
Reviewed-on: https://chromium-review.googlesource.com/860580
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50587}
parent cd43f56e
# Heap Stats
Heap stats is a HTML-based tool for visualizing V8-internal object statistics.
For example, the tool can be used to visualize how much heap memory is used for
maintaining internal state versus actually allocated by the user.
The tool consumes log files produced by d8 (or Chromium) by passing
`--trace-gc-object-stats`.
Hosting requires a web server, e.g.:
cd tools/heap/stats
python -m SimpleHTTPServer 8000
// 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.
// Categories for instance types.
const CATEGORIES = new Map([
[
'user', new Set([
'*FIXED_ARRAY_CONTEXT_SUB_TYPE',
'*FIXED_ARRAY_COPY_ON_WRITE_SUB_TYPE',
'*FIXED_ARRAY_PACKED_ELEMENTS_SUB_TYPE',
'CONS_ONE_BYTE_STRING_TYPE',
'CONS_STRING_TYPE',
'DESCRIPTOR_ARRAY_TYPE',
'EXTERNAL_INTERNALIZED_STRING_TYPE',
'EXTERNAL_ONE_BYTE_INTERNALIZED_STRING_TYPE',
'EXTERNAL_ONE_BYTE_STRING_TYPE',
'EXTERNAL_STRING_TYPE',
'EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE',
'FIXED_DOUBLE_ARRAY_TYPE',
'FIXED_FLOAT32_ARRAY_TYPE',
'FIXED_FLOAT64_ARRAY_TYPE',
'FIXED_INT16_ARRAY_TYPE',
'FIXED_INT32_ARRAY_TYPE',
'FIXED_INT8_ARRAY_TYPE',
'FIXED_UINT16_ARRAY_TYPE',
'FIXED_UINT32_ARRAY_TYPE',
'FIXED_UINT8_ARRAY_TYPE',
'FIXED_UINT8_CLAMPED_ARRAY_TYPE',
'HEAP_NUMBER_TYPE',
'INTERNALIZED_STRING_TYPE',
'JS_ARGUMENTS_TYPE',
'JS_ARRAY_BUFFER_TYPE',
'JS_ARRAY_TYPE',
'JS_BOUND_FUNCTION_TYPE',
'JS_DATE_TYPE',
'JS_ERROR_TYPE',
'JS_FAST_ARRAY_KEY_ITERATOR_TYPE',
'JS_FAST_ARRAY_VALUE_ITERATOR_TYPE',
'JS_FAST_HOLEY_ARRAY_VALUE_ITERATOR_TYPE',
'JS_FAST_HOLEY_SMI_ARRAY_VALUE_ITERATOR_TYPE',
'JS_FAST_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE',
'JS_FAST_SMI_ARRAY_VALUE_ITERATOR_TYPE',
'JS_FUNCTION_TYPE',
'JS_GENERIC_ARRAY_VALUE_ITERATOR_TYPE',
'JS_GLOBAL_OBJECT_TYPE',
'JS_GLOBAL_PROXY_TYPE',
'JS_MAP_TYPE',
'JS_MESSAGE_OBJECT_TYPE',
'JS_OBJECT_TYPE',
'JS_PROMISE_TYPE',
'JS_REGEXP_TYPE',
'JS_SET_TYPE',
'JS_STRING_ITERATOR_TYPE',
'JS_TYPED_ARRAY_TYPE',
'JS_VALUE_TYPE',
'JS_WEAK_MAP_TYPE',
'MUTABLE_HEAP_NUMBER_TYPE',
'ONE_BYTE_INTERNALIZED_STRING_TYPE',
'ONE_BYTE_STRING_TYPE',
'PROPERTY_ARRAY_TYPE',
'SHORT_EXTERNAL_INTERNALIZED_STRING_TYPE',
'SHORT_EXTERNAL_ONE_BYTE_INTERNALIZED_STRING_TYPE',
'SHORT_EXTERNAL_ONE_BYTE_STRING_TYPE',
'SLICED_ONE_BYTE_STRING_TYPE',
'SLICED_STRING_TYPE',
'STRING_TYPE',
'SYMBOL_TYPE',
'THIN_ONE_BYTE_STRING_TYPE',
'THIN_STRING_TYPE',
])
],
[
'system', new Set([
'*FIXED_ARRAY_DEPENDENT_CODE_SUB_TYPE',
'*FIXED_ARRAY_ENUM_CACHE_SUB_TYPE',
'*FIXED_ARRAY_ENUM_INDICES_CACHE_SUB_TYPE',
'*FIXED_ARRAY_FAST_TEMPLATE_INSTANTIATIONS_CACHE_SUB_TYPE',
'*FIXED_ARRAY_NUMBER_STRING_CACHE_SUB_TYPE',
'*FIXED_ARRAY_PROTOTYPE_USERS_SUB_TYPE',
'*FIXED_ARRAY_REGEXP_MULTIPLE_CACHE_SUB_TYPE',
'*FIXED_ARRAY_RETAINED_MAPS_SUB_TYPE',
'*FIXED_ARRAY_SCOPE_INFO_SUB_TYPE',
'*FIXED_ARRAY_SCRIPT_LIST_SUB_TYPE',
'*FIXED_ARRAY_SINGLE_CHARACTER_STRING_CACHE_SUB_TYPE',
'*FIXED_ARRAY_STRING_SPLIT_CACHE_SUB_TYPE',
'*FIXED_ARRAY_TEMPLATE_INFO_SUB_TYPE',
'*FIXED_ARRAY_WEAK_NEW_SPACE_OBJECT_TO_CODE_SUB_TYPE',
'ACCESS_CHECK_INFO_TYPE',
'ACCESSOR_INFO_TYPE',
'ACCESSOR_PAIR_TYPE',
'ALLOCATION_MEMENTO_TYPE',
'ALLOCATION_SITE_TYPE',
'BYTE_ARRAY_TYPE',
'CELL_TYPE',
'CONTEXT_EXTENSION_TYPE',
'FOREIGN_TYPE',
'FUNCTION_TEMPLATE_INFO_TYPE',
'INTERCEPTOR_INFO_TYPE',
'JS_API_OBJECT_TYPE',
'JS_SPECIAL_API_OBJECT_TYPE',
'MAP_TYPE',
'OBJECT_TEMPLATE_INFO_TYPE',
'ODDBALL_TYPE',
'PROMISE_REACTION_JOB_INFO_TYPE',
'PROMISE_RESOLVE_THENABLE_JOB_INFO_TYPE',
'PROPERTY_CELL_TYPE',
'PROTOTYPE_INFO_TYPE',
'STACK_FRAME_INFO_TYPE',
'TRANSITION_ARRAY_TYPE',
'WEAK_CELL_TYPE',
])
],
[
'code', new Set([
'*CODE_BUILTIN',
'*CODE_BYTECODE_HANDLER',
'*CODE_OPTIMIZED_FUNCTION',
'*CODE_REGEXP',
'*CODE_STUB',
'*FIXED_ARRAY_BYTECODE_ARRAY_CONSTANT_POOL_SUB_TYPE',
'*FIXED_ARRAY_BYTECODE_ARRAY_HANDLER_TABLE_SUB_TYPE',
'*FIXED_ARRAY_DEOPTIMIZATION_DATA_SUB_TYPE',
'*FIXED_ARRAY_EMBEDDED_OBJECT_SUB_TYPE',
'*FIXED_ARRAY_HANDLER_TABLE_SUB_TYPE',
'*FIXED_ARRAY_NOSCRIPT_SHARED_FUNCTION_INFOS_SUB_TYPE',
'*FIXED_ARRAY_OPTIMIZED_CODE_LITERALS_SUB_TYPE',
'*FIXED_ARRAY_SHARED_FUNCTION_INFOS_SUB_TYPE',
'BYTECODE_ARRAY_TYPE',
'CODE_DATA_CONTAINER_TYPE',
'FEEDBACK_VECTOR_TYPE',
'LOAD_HANDLER_TYPE',
'SCRIPT_TYPE',
'SHARED_FUNCTION_INFO_TYPE',
'STORE_HANDLER_TYPE',
])
],
['unclassified', new Set()],
]);
// Maps category to description text that is shown in html.
const CATEGORY_NAMES = new Map([
['user', 'User'],
['system', 'System'],
['code', 'Code'],
['unclassified', 'Unclassified'],
]);
// Instance types that are constructed from their sub types and
// should thus be hidden.
const IGNORED_INSTANCE_TYPES = new Set([
'FIXED_ARRAY_TYPE',
'CODE_TYPE',
]);
<!-- 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. -->
<template id="details-selection-template">
<style>
.box {
border-left: dashed 1px #666666;
border-right: dashed 1px #666666;
border-bottom: dashed 1px #666666;
padding: 10px;
overflow: hidden;
}
.box:nth-of-type(1) {
border-top: dashed 1px #666666;
border-radius: 5px 5px 0px 0px;
}
.box:last-of-type {
border-radius: 0px 0px 5px 5px;
}
span {
display: block;
padding: 5px;
font-weight: bold;
}
.boxDiv {
padding: 3px;
float: left;
}
.boxDiv > label {
font-size: xx-small;
}
#categories {
margin-top: 10px;
}
</style>
<label for="isolate-select">
Isolate
</label>
<select id="isolate-select">
<option>No data</option>
</select>
<label for="dataset-select">
Data set
</label>
<select id="dataset-select">
<option>No data</option>
</select>
<div id="categories"></div>
</template>
<script type="text/javascript" src="categories.js"></script>
<script type="text/javascript" src="details-selection.js"></script>
\ No newline at end of file
// 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.
'use strict';
const details_selection_template =
document.currentScript.ownerDocument.querySelector(
'#details-selection-template');
class DetailsSelection extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(details_selection_template.content.cloneNode(true));
this.isolateSelect.addEventListener(
'change', e => this.handleIsolateChange(e));
this.datasetSelect.addEventListener(
'change', e => this.notifySelectionChanged(e));
}
connectedCallback() {
for (let category of CATEGORIES.keys()) {
this.$('#categories').appendChild(this.buildCategory(category));
}
}
set data(value) {
this._data = value;
this.dataChanged();
}
get data() {
return this._data;
}
buildCategory(name) {
const div = document.createElement('div');
div.id = name;
div.classList.add('box');
const span = document.createElement('span');
div.appendChild(span);
span.innerHTML = CATEGORY_NAMES.get(name) + ' ';
const all_button = document.createElement('button');
span.appendChild(all_button);
all_button.innerHTML = 'All';
all_button.addEventListener('click', e => this.selectCategory(name));
const none_button = document.createElement('button');
span.appendChild(none_button);
none_button.innerHTML = 'None';
none_button.addEventListener('click', e => this.unselectCategory(name));
const innerDiv = document.createElement('div');
div.appendChild(innerDiv);
innerDiv.id = name + 'Content';
return div;
}
$(id) {
return this.shadowRoot.querySelector(id);
}
get datasetSelect() {
return this.$('#dataset-select');
}
get isolateSelect() {
return this.$('#isolate-select');
}
dataChanged() {
this.clearUI();
this.populateSelect('#isolate-select', Object.keys(this.data));
this.handleIsolateChange();
}
clearUI() {
this.selection = {categories: {}};
removeAllChildren(this.isolateSelect);
removeAllChildren(this.datasetSelect);
this.clearCategories();
}
handleIsolateChange(e) {
this.selection.isolate = this.isolateSelect.value;
this.populateSelect(
'#dataset-select', this.data[this.selection.isolate].data_sets, 'live');
this.populateCategories();
this.notifySelectionChanged();
}
notifySelectionChanged(e) {
if (!this.selection.isolate) return;
for (let category of CATEGORIES.keys()) {
this.selection.categories[category] = this.selectedInCategory(category);
}
this.selection.category_names = CATEGORY_NAMES;
this.selection.data_set = this.datasetSelect.value;
this.dispatchEvent(new CustomEvent(
'change', {bubbles: true, composed: true, detail: this.selection}));
}
selectedInCategory(category) {
const selected = this.shadowRoot.querySelectorAll(
'input[name=' + category + 'Checkbox]:checked');
var tmp = [];
for (var val of selected.values()) tmp.push(val.value);
return tmp;
}
categoryForType(instance_type) {
for (let [key, value] of CATEGORIES.entries()) {
if (value.has(instance_type)) return key;
}
return 'unclassified';
}
createOption(text) {
const option = document.createElement('option');
option.value = text;
option.text = text;
return option;
}
populateSelect(id, iterable, autoselect = null) {
for (let option_value of iterable) {
const option = this.createOption(option_value);
if (autoselect === option_value) {
option.selected = 'selected';
}
this.$(id).appendChild(option);
}
}
clearCategories() {
for (const category of CATEGORIES.keys()) {
let f = this.$('#' + category + 'Content');
while (f.firstChild) {
f.removeChild(f.firstChild);
}
}
}
populateCategories() {
this.clearCategories();
const categories = {};
for (let cat of CATEGORIES.keys()) {
categories[cat] = [];
}
for (let instance_type of this.data[this.selection.isolate]
.non_empty_instance_types) {
if (IGNORED_INSTANCE_TYPES.has(instance_type)) continue;
const category = this.categoryForType(instance_type);
categories[category].push(instance_type);
}
for (let category of Object.keys(categories)) {
categories[category].sort();
for (let instance_type of categories[category]) {
this.$('#' + category + 'Content')
.appendChild(this.createCheckBox(instance_type, category));
}
}
}
unselectCategory(category) {
for (let checkbox of this.shadowRoot.querySelectorAll(
'input[name=' + category + 'Checkbox]')) {
checkbox.checked = false;
}
this.notifySelectionChanged();
}
selectCategory(category) {
for (let checkbox of this.shadowRoot.querySelectorAll(
'input[name=' + category + 'Checkbox]')) {
checkbox.checked = true;
}
this.notifySelectionChanged();
}
createCheckBox(instance_type, category) {
const div = document.createElement('div');
div.classList.add('boxDiv');
const input = document.createElement('input');
div.appendChild(input);
input.type = 'checkbox';
input.name = category + 'Checkbox';
input.checked = 'checked';
input.id = instance_type + 'Checkbox';
input.value = instance_type;
input.addEventListener('change', e => this.notifySelectionChanged(e));
const label = document.createElement('label');
div.appendChild(label);
label.innerText = instance_type;
label.htmlFor = instance_type + 'Checkbox';
return div;
}
}
customElements.define('details-selection', DetailsSelection);
<!-- 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. -->
<template id="global-timeline-template">
<style>
#chart {
width: 100%;
height: 500px;
}
</style>
<div id="container" style="display: none;">
<h2>Timeline</h2>
<div id="chart"></div>
</div>
</template>
<script type="text/javascript" src="global-timeline.js"></script>
\ No newline at end of file
// 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.
'use strict';
const KB = 1024;
const MB = KB * KB;
const global_timeline_template =
document.currentScript.ownerDocument.querySelector(
'#global-timeline-template');
class GlobalTimeline extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(global_timeline_template.content.cloneNode(true));
}
set data(value) {
this._data = value;
this.stateChanged();
}
get data() {
return this._data;
}
set selection(value) {
this._selection = value;
this.stateChanged();
}
get selection() {
return this._selection;
}
isValid() {
return this.data && this.selection;
}
stateChanged() {
if (this.isValid()) this.drawChart();
}
drawChart() {
console.assert(this.data, 'invalid data');
console.assert(this.selection, 'invalid selection');
const categories = Object.keys(this.selection.categories)
.map(k => this.selection.category_names.get(k));
const labels = ['Time', ...categories];
const chart_data = [labels];
const isolate_data = this.data[this.selection.isolate];
for (let k of Object.keys(isolate_data.gcs)) {
const gc_data = isolate_data.gcs[k];
const data_set = gc_data[this.selection.data_set].instance_type_data;
const data = [];
data.push(gc_data.time);
for (let [category, instance_types] of Object.entries(
this.selection.categories)) {
let overall = 0;
for (let instance_type of instance_types) {
overall += data_set[instance_type].overall;
}
data.push(overall / KB);
}
chart_data.push(data);
}
const data = google.visualization.arrayToDataTable(chart_data);
const options = {
isStacked: true,
hAxis: {
title: 'Time [ms]',
},
vAxis: {title: 'Memory consumption [KBytes]'},
chartArea: {width: '85%', height: '80%'},
legend: {position: 'top', maxLines: '1'},
pointsVisible: true,
pointSize: 5,
};
const chart = new google.visualization.AreaChart(
this.shadowRoot.querySelector('#chart'));
this.shadowRoot.querySelector('#container').style.display = 'block';
chart.draw(data, google.charts.Line.convertOptions(options));
}
}
customElements.define('global-timeline', GlobalTimeline);
<!DOCTYPE html>
<!-- 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. -->
<html lang="en">
<head>
<meta charset="UTF-8">
<title>V8 Heap Statistics</title>
<link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<link rel="import" href="details-selection.html">
<link rel="import" href="global-timeline.html">
<link rel="import" href="trace-file-reader.html">
<style type="text/css">
body {
font-family: 'Roboto', sans-serif;
margin-left: 5%;
margin-right: 5%;
}
</style>
<script type="text/javascript">
'use strict';
google.charts.load('current', {'packages':['line', 'corechart']});
function $(id) { return document.querySelector(id); }
function removeAllChildren(node) {
while (node.firstChild) {
node.removeChild(node.firstChild);
}
}
let state = Object.create(null);
function globalDataChanged(e) {
state.data = e.detail;
state.selection = null;
$('#global-timeline').selection = state.selection;
$('#global-timeline').data = state.data;
$('#type-details').selection = state.selection;
$('#type-details').data = state.data;
$('#details-selection').data = state.data;
}
function globalSelectionChangedA(e) {
state.selection = e.detail;
$('#global-timeline').selection = state.selection;
$('#type-details').selection = state.selection;
}
</script>
</head>
<body>
<trace-file-reader onchange="globalDataChanged(event)"></trace-file-reader>
<h1>V8 Heap Statistics</h1>
<p>
Visualize object stats gathered using <code>--trace-gc-object-stats</code>.
Needs to be run on a web server (e.g. <code>python -m
SimpleHTTPServer</code>) due to HTML imports requiring CORS.
</p>
<details-selection id="details-selection" onchange="globalSelectionChangedA(event)"></details-selection>
<global-timeline id="global-timeline"></global-timeline>
<type-details id="type-details"></type-details>
</body>
</html>
<!-- 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. -->
<template id="trace-file-reader-template">
<style>
#fileReader {
width: 100%;
height: 100px;
line-height: 100px;
text-align: center;
border: solid 1px #000000;
border-radius: 5px;
}
#fileReader > input {
display: none;
}
</style>
<div id="fileReader">
<span id="label">
Drag and drop a trace file into this area, or click to choose from disk.
</span>
<input id="file" type="file" name="file" />
</div>
</template>
<script type="text/javascript" src="trace-file-reader.js"></script>
// 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.
'use strict';
const trace_file_reader_template =
document.currentScript.ownerDocument.querySelector(
'#trace-file-reader-template');
class TraceFileReader extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(trace_file_reader_template.content.cloneNode(true));
this.addEventListener('click', e => this.handleClick(e));
this.addEventListener('dragover', e => this.handleDragOver(e));
this.addEventListener('drop', e => this.handleChange(e));
this.$('#file').addEventListener('change', e => this.handleChange(e));
}
$(id) {
return this.shadowRoot.querySelector(id);
}
updateLabel(text) {
this.$('#label').innerText = text;
}
handleClick(event) {
this.$('#file').click();
}
handleChange(event) {
// Used for drop and file change.
event.preventDefault();
var host = event.dataTransfer ? event.dataTransfer : event.target;
this.readFile(host.files[0]);
}
handleDragOver(event) {
event.preventDefault();
}
connectedCallback() {}
readFile(file) {
if (!file) {
this.updateLabel('Failed to load file.');
return;
}
const result = new FileReader();
result.onload = (e) => {
let contents = e.target.result.split('\n');
contents = contents.map(function(line) {
try {
// Strip away a potentially present adb logcat prefix.
line = line.replace(/^I\/v8\s*\(\d+\):\s+/g, '');
return JSON.parse(line);
} catch (e) {
console.log('unable to parse line: \'' + line + '\'\' (' + e + ')');
}
return null;
});
const return_data = this.createModel(contents);
this.updateLabel('Finished loading \'' + file.name + '\'.');
this.dispatchEvent(new CustomEvent(
'change', {bubbles: true, composed: true, detail: return_data}));
};
result.readAsText(file);
}
createModel(contents) {
// contents is an array of JSON objects that is consolidated into the
// application model.
const data = Object.create(null); // Final data container.
const keys = Object.create(null); // Collecting 'keys' per isolate.
let createOrUpdateEntryIfNeeded = entry => {
if (!(entry.isolate in keys)) {
keys[entry.isolate] = new Set();
}
if (!(entry.isolate in data)) {
data[entry.isolate] = {
non_empty_instance_types: new Set(),
gcs: {},
zonetags: [],
samples: {zone: {}},
start: null,
end: null,
data_sets: new Set()
};
}
const data_object = data[entry.isolate];
if (('id' in entry) && !(entry.id in data_object.gcs)) {
data_object.gcs[entry.id] = {non_empty_instance_types: new Set()};
}
if (data_object.end === null || data_object.end < entry.time) {
data_object.end = entry.time;
}
if (data_object.start === null || data_object.start > entry.time)
data_object.start = entry.time;
};
for (var entry of contents) {
if (entry === null || entry.type === undefined) {
continue;
}
if (entry.type === 'zone') {
createOrUpdateEntryIfNeeded(entry);
const stacktrace = ('stacktrace' in entry) ? entry.stacktrace : [];
data[entry.isolate].samples.zone[entry.time] = {
allocated: entry.allocated,
pooled: entry.pooled,
stacktrace: stacktrace
};
} else if (
entry.type === 'zonecreation' || entry.type === 'zonedestruction') {
createOrUpdateEntryIfNeeded(entry);
data[entry.isolate].zonetags.push(
Object.assign({opening: entry.type === 'zonecreation'}, entry));
} else if (entry.type === 'gc_descriptor') {
createOrUpdateEntryIfNeeded(entry);
data[entry.isolate].gcs[entry.id].time = entry.time;
if ('zone' in entry)
data[entry.isolate].gcs[entry.id].malloced = entry.zone;
} else if (entry.type === 'instance_type_data') {
if (entry.id in data[entry.isolate].gcs) {
createOrUpdateEntryIfNeeded(entry);
if (!(entry.key in data[entry.isolate].gcs[entry.id])) {
data[entry.isolate].gcs[entry.id][entry.key] = {
instance_type_data: {},
non_empty_instance_types: new Set(),
overall: 0
};
data[entry.isolate].data_sets.add(entry.key);
}
const instanceTypeName = entry.instance_type_name;
const id = entry.id;
const key = entry.key;
keys[entry.isolate].add(key);
data[entry.isolate]
.gcs[id][key]
.instance_type_data[instanceTypeName] = {
overall: entry.overall,
count: entry.count,
histogram: entry.histogram,
over_allocated: entry.over_allocated,
over_allocated_histogram: entry.over_allocated_histogram
};
data[entry.isolate].gcs[id][key].overall += entry.overall;
if (entry.overall !== 0) {
data[entry.isolate].gcs[id][key].non_empty_instance_types.add(
instanceTypeName);
data[entry.isolate].gcs[id].non_empty_instance_types.add(
instanceTypeName);
data[entry.isolate].non_empty_instance_types.add(instanceTypeName);
}
}
} else if (entry.type === 'bucket_sizes') {
if (entry.id in data[entry.isolate].gcs) {
createOrUpdateEntryIfNeeded(entry);
if (!(entry.key in data[entry.isolate].gcs[entry.id])) {
data[entry.isolate].gcs[entry.id][entry.key] = {
instance_type_data: {},
non_empty_instance_types: new Set(),
overall: 0
};
data[entry.isolate].data_sets.add(entry.key);
}
data[entry.isolate].gcs[entry.id][entry.key].bucket_sizes =
entry.sizes;
}
} else {
console.warning('Unknown entry type: ' + entry.type);
}
}
let checkNonNegativeProperty = (obj, property) => {
if (obj[property] < 0) {
console.warning(
'Property \'' + property + '\' negative: ' + obj[property]);
}
};
for (const isolate of Object.keys(data)) {
for (const gc of Object.keys(data[isolate].gcs)) {
for (const key of keys[isolate]) {
const data_set = data[isolate].gcs[gc][key];
// 1. 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;
});
// 2. Create *FIXED_ARRAY_UNKNOWN_SUB_TYPE that accounts for all
// missing fixed array sub types.
const fixed_array_data =
Object.assign({}, data_set.instance_type_data.FIXED_ARRAY_TYPE);
for (const instanceType in data_set.instance_type_data) {
if (!instanceType.startsWith('*FIXED_ARRAY')) continue;
const subtype = data_set.instance_type_data[instanceType];
fixed_array_data.count -= subtype.count;
fixed_array_data.overall -= subtype.overall;
for (let i = 0; i < fixed_array_data.histogram.length; i++) {
fixed_array_data.histogram[i] -= subtype.histogram[i];
}
}
// Emit log messages for negative values.
checkNonNegativeProperty(fixed_array_data, 'count');
checkNonNegativeProperty(fixed_array_data, 'overall');
for (let i = 0; i < fixed_array_data.histogram.length; i++) {
checkNonNegativeProperty(fixed_array_data.histogram, i);
}
data_set.instance_type_data['*FIXED_ARRAY_UNKNOWN_SUB_TYPE'] =
fixed_array_data;
data_set.non_empty_instance_types.add(
'*FIXED_ARRAY_UNKNOWN_SUB_TYPE');
}
}
}
console.log(data);
return data;
}
}
customElements.define('trace-file-reader', TraceFileReader);
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