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

[object-stats] CSV export and simple fixes

Allow exporting the current selection as CSV.

No-try: true
Bug: v8:7266
Change-Id: Idd275e749506d2a195a132efa5ec08ebb21ca72f
Reviewed-on: https://chromium-review.googlesource.com/870781Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50668}
parent db129b65
...@@ -63,9 +63,19 @@ span { ...@@ -63,9 +63,19 @@ span {
Merge categories Merge categories
</label> </label>
</li> </li>
<li>
<label for="gc-select">
Garbage collection (at a specific time in ms)
</label>
<select id="gc-select">
<option>No data</option>
</select>
</li>
<li>
<button id="csv-export" disabled="disabled">Export selection as CSV</button>
</li>
</ul> </ul>
<div id="categories"></div> <div id="categories"></div>
</template> </template>
<script type="text/javascript" src="categories.js"></script> <script type="text/javascript" src="categories.js"></script>
......
...@@ -17,6 +17,10 @@ class DetailsSelection extends HTMLElement { ...@@ -17,6 +17,10 @@ class DetailsSelection extends HTMLElement {
'change', e => this.handleIsolateChange(e)); 'change', e => this.handleIsolateChange(e));
this.datasetSelect.addEventListener( this.datasetSelect.addEventListener(
'change', e => this.notifySelectionChanged(e)); 'change', e => this.notifySelectionChanged(e));
this.gcSelect.addEventListener(
'change', e => this.notifySelectionChanged(e));
this.$('#csv-export')
.addEventListener('click', e => this.exportCurrentSelection(e));
this.$('#merge-categories') this.$('#merge-categories')
.addEventListener('change', e => this.notifySelectionChanged(e)); .addEventListener('change', e => this.notifySelectionChanged(e));
} }
...@@ -69,9 +73,14 @@ class DetailsSelection extends HTMLElement { ...@@ -69,9 +73,14 @@ class DetailsSelection extends HTMLElement {
return this.$('#isolate-select'); return this.$('#isolate-select');
} }
get gcSelect() {
return this.$('#gc-select');
}
dataChanged() { dataChanged() {
this.clearUI(); this.clearUI();
this.populateSelect('#isolate-select', Object.keys(this.data)); this.populateSelect(
'#isolate-select', Object.keys(this.data).map(v => [v, v]));
this.handleIsolateChange(); this.handleIsolateChange();
} }
...@@ -79,7 +88,9 @@ class DetailsSelection extends HTMLElement { ...@@ -79,7 +88,9 @@ class DetailsSelection extends HTMLElement {
this.selection = {categories: {}}; this.selection = {categories: {}};
removeAllChildren(this.isolateSelect); removeAllChildren(this.isolateSelect);
removeAllChildren(this.datasetSelect); removeAllChildren(this.datasetSelect);
removeAllChildren(this.gcSelect);
this.clearCategories(); this.clearCategories();
this.$('#csv-export').disabled = 'disabled';
} }
handleIsolateChange(e) { handleIsolateChange(e) {
...@@ -90,7 +101,12 @@ class DetailsSelection extends HTMLElement { ...@@ -90,7 +101,12 @@ class DetailsSelection extends HTMLElement {
} }
this.populateSelect( this.populateSelect(
'#dataset-select', this.data[this.selection.isolate].data_sets, 'live'); '#dataset-select',
this.data[this.selection.isolate].data_sets.entries(), 'live');
this.populateSelect(
'#gc-select',
Object.keys(this.data[this.selection.isolate].gcs)
.map(v => [v, this.data[this.selection.isolate].gcs[v].time]));
this.populateCategories(); this.populateCategories();
this.notifySelectionChanged(); this.notifySelectionChanged();
} }
...@@ -106,6 +122,8 @@ class DetailsSelection extends HTMLElement { ...@@ -106,6 +122,8 @@ class DetailsSelection extends HTMLElement {
this.selection.category_names = CATEGORY_NAMES; this.selection.category_names = CATEGORY_NAMES;
this.selection.data_set = this.datasetSelect.value; this.selection.data_set = this.datasetSelect.value;
this.selection.merge_categories = this.$('#merge-categories').checked; this.selection.merge_categories = this.$('#merge-categories').checked;
this.selection.gc = this.gcSelect.value;
this.$('#csv-export').disabled = false;
this.dispatchEvent(new CustomEvent( this.dispatchEvent(new CustomEvent(
'change', {bubbles: true, composed: true, detail: this.selection})); 'change', {bubbles: true, composed: true, detail: this.selection}));
} }
...@@ -125,17 +143,17 @@ class DetailsSelection extends HTMLElement { ...@@ -125,17 +143,17 @@ class DetailsSelection extends HTMLElement {
return 'unclassified'; return 'unclassified';
} }
createOption(text) { createOption(value, text) {
const option = document.createElement('option'); const option = document.createElement('option');
option.value = text; option.value = value;
option.text = text; option.text = text;
return option; return option;
} }
populateSelect(id, iterable, autoselect = null) { populateSelect(id, iterable, autoselect = null) {
for (let option_value of iterable) { for (let [value, text] of iterable) {
const option = this.createOption(option_value); const option = this.createOption(value, text);
if (autoselect === option_value) { if (autoselect === value) {
option.selected = 'selected'; option.selected = 'selected';
} }
this.$(id).appendChild(option); this.$(id).appendChild(option);
...@@ -206,6 +224,33 @@ class DetailsSelection extends HTMLElement { ...@@ -206,6 +224,33 @@ class DetailsSelection extends HTMLElement {
label.htmlFor = instance_type + 'Checkbox'; label.htmlFor = instance_type + 'Checkbox';
return div; return div;
} }
exportCurrentSelection(e) {
const data = [];
const selected_data = this.data[this.selection.isolate]
.gcs[this.selection.gc][this.selection.data_set]
.instance_type_data;
Object.values(this.selection.categories).forEach(instance_types => {
instance_types.forEach(instance_type => {
data.push([instance_type, selected_data[instance_type].overall / KB]);
});
});
const createInlineContent = arrayOfRows => {
const content = arrayOfRows.reduce(
(accu, rowAsArray) => {return accu + `${rowAsArray.join(',')}\n`},
'');
return `data:text/csv;charset=utf-8,${content}`;
};
const encodedUri = encodeURI(createInlineContent(data));
const link = document.createElement('a');
link.setAttribute('href', encodedUri);
link.setAttribute(
'download',
`heap_objects_data_${this.selection.isolate}_${this.selection.gc}.csv`);
this.shadowRoot.appendChild(link);
link.click();
this.shadowRoot.removeChild(link);
}
} }
customElements.define('details-selection', DetailsSelection); customElements.define('details-selection', DetailsSelection);
...@@ -66,7 +66,7 @@ function globalSelectionChangedA(e) { ...@@ -66,7 +66,7 @@ function globalSelectionChangedA(e) {
<h1>V8 Heap Statistics</h1> <h1>V8 Heap Statistics</h1>
<p>Visualize object statistics that have been gathered using</p> <p>Visualize object statistics that have been gathered using</p>
<ul> <ul>
<li><code>--trace-gc-object-stats on V8</code></li> <li><code>--trace-gc-object-stats</code> on V8</li>
<li> <li>
<a <a
href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">Chrome's href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">Chrome's
......
...@@ -151,14 +151,24 @@ class TraceFileReader extends HTMLElement { ...@@ -151,14 +151,24 @@ class TraceFileReader extends HTMLElement {
Array( Array(
data_set.instance_type_data.FIXED_ARRAY_TYPE.histogram.length) data_set.instance_type_data.FIXED_ARRAY_TYPE.histogram.length)
.fill(0); .fill(0);
let known_over_allocated = 0;
let known_over_allocated_histogram =
Array(data_set.instance_type_data.FIXED_ARRAY_TYPE
.over_allocated_histogram.length)
.fill(0);
for (const instance_type in data_set.instance_type_data) { for (const instance_type in data_set.instance_type_data) {
if (!instance_type.startsWith('*FIXED_ARRAY')) continue; if (!instance_type.startsWith('*FIXED_ARRAY')) continue;
const subtype = data_set.instance_type_data[instance_type]; const subtype = data_set.instance_type_data[instance_type];
known_count += subtype.count; known_count += subtype.count;
known_overall += subtype.count; known_overall += subtype.count;
known_over_allocated += subtype.over_allocated;
for (let i = 0; i < subtype.histogram.length; i++) { for (let i = 0; i < subtype.histogram.length; i++) {
known_histogram[i] += subtype.histogram[i]; known_histogram[i] += subtype.histogram[i];
} }
for (let i = 0; i < subtype.over_allocated_histogram.length; i++) {
known_over_allocated_histogram[i] +=
subtype.over_allocated_histogram[i];
}
} }
const fixed_array_data = data_set.instance_type_data.FIXED_ARRAY_TYPE; const fixed_array_data = data_set.instance_type_data.FIXED_ARRAY_TYPE;
...@@ -166,20 +176,30 @@ class TraceFileReader extends HTMLElement { ...@@ -166,20 +176,30 @@ class TraceFileReader extends HTMLElement {
count: fixed_array_data.count - known_count, count: fixed_array_data.count - known_count,
overall: fixed_array_data.overall - known_overall, overall: fixed_array_data.overall - known_overall,
histogram: fixed_array_data.histogram.map( histogram: fixed_array_data.histogram.map(
(value, index) => value - known_histogram[index]) (value, index) => value - known_histogram[index]),
over_allocated:
fixed_array_data.over_allocated - known_over_allocated,
over_allocated_histogram:
fixed_array_data.over_allocated_histogram.map(
(value, index) =>
value - known_over_allocated_histogram[index])
}; };
// Check for non-negative values. // Check for non-negative values.
checkNonNegativeProperty(unknown_entry, 'count'); checkNonNegativeProperty(unknown_entry, 'count');
checkNonNegativeProperty(unknown_entry, 'overall'); checkNonNegativeProperty(unknown_entry, 'overall');
checkNonNegativeProperty(unknown_entry, 'over_allocated');
for (let i = 0; i < unknown_entry.histogram.length; i++) { for (let i = 0; i < unknown_entry.histogram.length; i++) {
checkNonNegativeProperty(unknown_entry.histogram, i); checkNonNegativeProperty(unknown_entry.histogram, i);
} }
for (let i = 0; i < unknown_entry.over_allocated_histogram.length;
i++) {
checkNonNegativeProperty(unknown_entry.over_allocated_histogram, i);
}
data_set.instance_type_data['*FIXED_ARRAY_UNKNOWN_SUB_TYPE'] = this.addInstanceTypeData(
unknown_entry; data, keys, isolate, gc, data_set_key,
data_set.non_empty_instance_types.add( '*FIXED_ARRAY_UNKNOWN_SUB_TYPE', unknown_entry);
'*FIXED_ARRAY_UNKNOWN_SUB_TYPE');
} }
} }
} }
......
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