Commit f2d0550a authored by Igor Sheludko's avatar Igor Sheludko Committed by Commit Bot

[tools] Fix parsing of Chrome tracing files by v8-heap-stats

Use Oboe.js streaming JSON parser for reading tracing file which
provides the following advantages:
1) streaming parsing allows keeping alive only relevant entries which
   should consume less memory when parsing of huge files (although
   currently the whole file is kept in memory anyway),
2) avoids the need to sanitize tracing file

Bug: v8:10155
Change-Id: Id5268264a610eff804672d09b3e9f3ac353b67de
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2120542
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66888}
parent 872e315b
......@@ -16,6 +16,8 @@ found in the LICENSE file. -->
integrity="sha256-N1z6ddQzX83fjw8v7uSNe7/MgOmMKdwFUv1+AJMDqNM="
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/oboe.js/2.1.5/oboe-browser.min.js"
crossorigin="anonymous"></script>
<script src="helper.js"></script>
<script type="module" src="details-selection.js"></script>
......
......@@ -95,10 +95,13 @@ defineCustomElement('trace-file-reader', (templateText) =>
}
processRawText(file, result) {
let contents = result.split('\n');
const return_data = (result.includes('V8.GC_Objects_Stats')) ?
this.createModelFromChromeTraceFile(contents) :
this.createModelFromV8TraceFile(contents);
let return_data;
if (result.includes('V8.GC_Objects_Stats')) {
return_data = this.createModelFromChromeTraceFile(result);
} else {
let contents = result.split('\n');
return_data = this.createModelFromV8TraceFile(contents);
}
this.extendAndSanitizeModel(return_data);
this.updateLabel('Finished loading \'' + file.name + '\'.');
this.dispatchEvent(new CustomEvent(
......@@ -175,74 +178,62 @@ defineCustomElement('trace-file-reader', (templateText) =>
}
createModelFromChromeTraceFile(contents) {
// Trace files support two formats.
// {traceEvents: [ data ]}
const kObjectTraceFile = {
name: 'object',
endToken: ']}',
getDataArray: o => o.traceEvents
};
// [ data ]
const kArrayTraceFile = {
name: 'array',
endToken: ']',
getDataArray: o => o
};
const handler =
(contents[0][0] === '{') ? kObjectTraceFile : kArrayTraceFile;
console.log(`Processing log as chrome trace file (${handler.name}).`);
const data = Object.create(null); // Final data container.
const parseOneGCEvent = (actual_data) => {
Object.keys(actual_data).forEach(data_set => {
const string_entry = actual_data[data_set];
try {
const entry = JSON.parse(string_entry);
this.createOrUpdateEntryIfNeeded(data, entry);
this.createDatasetIfNeeded(data, entry, data_set);
const isolate = entry.isolate;
const time = entry.time;
const gc_id = entry.id;
data[isolate].gcs[gc_id].time = time;
// Pop last line in log as it might be broken.
contents.pop();
// Remove trailing comma.
contents[contents.length - 1] = contents[contents.length - 1].slice(0, -1);
// Terminate JSON.
const sanitized_contents = [...contents, handler.endToken].join('');
const field_data = entry.field_data;
this.addFieldTypeData(data, isolate, gc_id, data_set,
field_data.tagged_fields,
field_data.inobject_smi_fields,
field_data.embedder_fields,
field_data.unboxed_double_fields,
field_data.boxed_double_fields,
field_data.string_data,
field_data.other_raw_fields);
const data = Object.create(null); // Final data container.
data[isolate].gcs[gc_id][data_set].bucket_sizes =
entry.bucket_sizes;
for (let [instance_type, value] of Object.entries(
entry.type_data)) {
// Trace file format uses markers that do not have actual
// properties.
if (!('overall' in value)) continue;
this.addInstanceTypeData(
data, isolate, gc_id, data_set, instance_type, value);
}
} catch (e) {
console.error('Unable to parse data set entry', e);
}
});
};
console.log(`Processing log as chrome trace file.`);
try {
const raw_data = JSON.parse(sanitized_contents);
const raw_array_data = handler.getDataArray(raw_data);
raw_array_data.filter(e => e.name === 'V8.GC_Objects_Stats')
.forEach(trace_data => {
const actual_data = trace_data.args;
const data_sets = new Set(Object.keys(actual_data));
Object.keys(actual_data).forEach(data_set => {
const string_entry = actual_data[data_set];
try {
const entry = JSON.parse(string_entry);
this.createOrUpdateEntryIfNeeded(data, entry);
this.createDatasetIfNeeded(data, entry, data_set);
const isolate = entry.isolate;
const time = entry.time;
const gc_id = entry.id;
data[isolate].gcs[gc_id].time = time;
const field_data = entry.field_data;
this.addFieldTypeData(data, isolate, gc_id, data_set,
field_data.tagged_fields,
field_data.inobject_smi_fields,
field_data.embedder_fields,
field_data.unboxed_double_fields,
field_data.boxed_double_fields,
field_data.string_data,
field_data.other_raw_fields);
let gc_events_filter = (event) => {
if (event.name == 'V8.GC_Objects_Stats') {
parseOneGCEvent(event.args);
}
return oboe.drop;
};
data[isolate].gcs[gc_id][data_set].bucket_sizes =
entry.bucket_sizes;
for (let [instance_type, value] of Object.entries(
entry.type_data)) {
// Trace file format uses markers that do not have actual
// properties.
if (!('overall' in value)) continue;
this.addInstanceTypeData(
data, isolate, gc_id, data_set, instance_type, value);
}
} catch (e) {
console.log('Unable to parse data set entry', e);
}
});
});
let oboe_stream = oboe();
// Trace files support two formats.
oboe_stream
// 1) {traceEvents: [ data ]}
.node('traceEvents.*', gc_events_filter)
// 2) [ data ]
.node('!.*', gc_events_filter)
.fail(() => { throw new Error("Trace data parse failed!"); });
oboe_stream.emit('data', contents);
} catch (e) {
console.error('Unable to parse chrome trace file.', e);
}
......
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