[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/870781 Reviewed-by: Camillo Bruni <cbruni@chromium.org> Commit-Queue: Michael Lippautz <mlippautz@chromium.org> Cr-Commit-Position: refs/heads/master@{#50668}
This commit is contained in:
parent
db129b6525
commit
b68cdf2594
@ -63,9 +63,19 @@ span {
|
||||
Merge categories
|
||||
</label>
|
||||
</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>
|
||||
|
||||
|
||||
<div id="categories"></div>
|
||||
</template>
|
||||
<script type="text/javascript" src="categories.js"></script>
|
||||
|
@ -17,6 +17,10 @@ class DetailsSelection extends HTMLElement {
|
||||
'change', e => this.handleIsolateChange(e));
|
||||
this.datasetSelect.addEventListener(
|
||||
'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')
|
||||
.addEventListener('change', e => this.notifySelectionChanged(e));
|
||||
}
|
||||
@ -69,9 +73,14 @@ class DetailsSelection extends HTMLElement {
|
||||
return this.$('#isolate-select');
|
||||
}
|
||||
|
||||
get gcSelect() {
|
||||
return this.$('#gc-select');
|
||||
}
|
||||
|
||||
dataChanged() {
|
||||
this.clearUI();
|
||||
this.populateSelect('#isolate-select', Object.keys(this.data));
|
||||
this.populateSelect(
|
||||
'#isolate-select', Object.keys(this.data).map(v => [v, v]));
|
||||
this.handleIsolateChange();
|
||||
}
|
||||
|
||||
@ -79,7 +88,9 @@ class DetailsSelection extends HTMLElement {
|
||||
this.selection = {categories: {}};
|
||||
removeAllChildren(this.isolateSelect);
|
||||
removeAllChildren(this.datasetSelect);
|
||||
removeAllChildren(this.gcSelect);
|
||||
this.clearCategories();
|
||||
this.$('#csv-export').disabled = 'disabled';
|
||||
}
|
||||
|
||||
handleIsolateChange(e) {
|
||||
@ -90,7 +101,12 @@ class DetailsSelection extends HTMLElement {
|
||||
}
|
||||
|
||||
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.notifySelectionChanged();
|
||||
}
|
||||
@ -106,6 +122,8 @@ class DetailsSelection extends HTMLElement {
|
||||
this.selection.category_names = CATEGORY_NAMES;
|
||||
this.selection.data_set = this.datasetSelect.value;
|
||||
this.selection.merge_categories = this.$('#merge-categories').checked;
|
||||
this.selection.gc = this.gcSelect.value;
|
||||
this.$('#csv-export').disabled = false;
|
||||
this.dispatchEvent(new CustomEvent(
|
||||
'change', {bubbles: true, composed: true, detail: this.selection}));
|
||||
}
|
||||
@ -125,17 +143,17 @@ class DetailsSelection extends HTMLElement {
|
||||
return 'unclassified';
|
||||
}
|
||||
|
||||
createOption(text) {
|
||||
createOption(value, text) {
|
||||
const option = document.createElement('option');
|
||||
option.value = text;
|
||||
option.value = value;
|
||||
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) {
|
||||
for (let [value, text] of iterable) {
|
||||
const option = this.createOption(value, text);
|
||||
if (autoselect === value) {
|
||||
option.selected = 'selected';
|
||||
}
|
||||
this.$(id).appendChild(option);
|
||||
@ -206,6 +224,33 @@ class DetailsSelection extends HTMLElement {
|
||||
label.htmlFor = instance_type + 'Checkbox';
|
||||
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);
|
||||
|
@ -66,7 +66,7 @@ function globalSelectionChangedA(e) {
|
||||
<h1>V8 Heap Statistics</h1>
|
||||
<p>Visualize object statistics that have been gathered using</p>
|
||||
<ul>
|
||||
<li><code>--trace-gc-object-stats on V8</code></li>
|
||||
<li><code>--trace-gc-object-stats</code> on V8</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">Chrome's
|
||||
|
@ -151,14 +151,24 @@ class TraceFileReader extends HTMLElement {
|
||||
Array(
|
||||
data_set.instance_type_data.FIXED_ARRAY_TYPE.histogram.length)
|
||||
.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) {
|
||||
if (!instance_type.startsWith('*FIXED_ARRAY')) continue;
|
||||
const subtype = data_set.instance_type_data[instance_type];
|
||||
known_count += subtype.count;
|
||||
known_overall += subtype.count;
|
||||
known_over_allocated += subtype.over_allocated;
|
||||
for (let i = 0; i < subtype.histogram.length; 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;
|
||||
@ -166,20 +176,30 @@ class TraceFileReader extends HTMLElement {
|
||||
count: fixed_array_data.count - known_count,
|
||||
overall: fixed_array_data.overall - known_overall,
|
||||
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.
|
||||
checkNonNegativeProperty(unknown_entry, 'count');
|
||||
checkNonNegativeProperty(unknown_entry, 'overall');
|
||||
checkNonNegativeProperty(unknown_entry, 'over_allocated');
|
||||
for (let i = 0; i < unknown_entry.histogram.length; 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'] =
|
||||
unknown_entry;
|
||||
data_set.non_empty_instance_types.add(
|
||||
'*FIXED_ARRAY_UNKNOWN_SUB_TYPE');
|
||||
this.addInstanceTypeData(
|
||||
data, keys, isolate, gc, data_set_key,
|
||||
'*FIXED_ARRAY_UNKNOWN_SUB_TYPE', unknown_entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user