Commit 093cfad6 authored by Michael Lippautz's avatar Michael Lippautz Committed by Commit Bot

[object-stats] Visualizer: Allow loading trace file format using array

No-try: true
Bug: v8:7266
Change-Id: I65e5cdfcb3d15c4b9ebb1e5da5e69de79032a5d3
Reviewed-on: https://chromium-review.googlesource.com/897609
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51037}
parent 313b490d
...@@ -73,16 +73,14 @@ class TraceFileReader extends HTMLElement { ...@@ -73,16 +73,14 @@ class TraceFileReader extends HTMLElement {
const return_data = (result.includes('V8.GC_Objects_Stats')) ? const return_data = (result.includes('V8.GC_Objects_Stats')) ?
this.createModelFromChromeTraceFile(contents) : this.createModelFromChromeTraceFile(contents) :
this.createModelFromV8TraceFile(contents); this.createModelFromV8TraceFile(contents);
this.extendAndSanitizeModel(return_data);
this.updateLabel('Finished loading \'' + file.name + '\'.'); this.updateLabel('Finished loading \'' + file.name + '\'.');
this.dispatchEvent(new CustomEvent( this.dispatchEvent(new CustomEvent(
'change', {bubbles: true, composed: true, detail: return_data})); 'change', {bubbles: true, composed: true, detail: return_data}));
} }
createOrUpdateEntryIfNeeded(data, keys, entry) { createOrUpdateEntryIfNeeded(data, entry) {
console.assert(entry.isolate, 'entry should have an isolate'); console.assert(entry.isolate, 'entry should have an isolate');
if (!(entry.isolate in keys)) {
keys[entry.isolate] = new Set();
}
if (!(entry.isolate in data)) { if (!(entry.isolate in data)) {
data[entry.isolate] = { data[entry.isolate] = {
non_empty_instance_types: new Set(), non_empty_instance_types: new Set(),
...@@ -106,7 +104,7 @@ class TraceFileReader extends HTMLElement { ...@@ -106,7 +104,7 @@ class TraceFileReader extends HTMLElement {
} }
} }
createDatasetIfNeeded(data, keys, entry, data_set) { createDatasetIfNeeded(data, entry, data_set) {
if (!(data_set in data[entry.isolate].gcs[entry.id])) { if (!(data_set in data[entry.isolate].gcs[entry.id])) {
data[entry.isolate].gcs[entry.id][data_set] = { data[entry.isolate].gcs[entry.id][data_set] = {
instance_type_data: {}, instance_type_data: {},
...@@ -117,9 +115,7 @@ class TraceFileReader extends HTMLElement { ...@@ -117,9 +115,7 @@ class TraceFileReader extends HTMLElement {
} }
} }
addInstanceTypeData( addInstanceTypeData(data, isolate, gc_id, data_set, instance_type, entry) {
data, keys, isolate, gc_id, data_set, instance_type, entry) {
keys[isolate].add(data_set);
data[isolate].gcs[gc_id][data_set].instance_type_data[instance_type] = { data[isolate].gcs[gc_id][data_set].instance_type_data[instance_type] = {
overall: entry.overall, overall: entry.overall,
count: entry.count, count: entry.count,
...@@ -136,14 +132,16 @@ class TraceFileReader extends HTMLElement { ...@@ -136,14 +132,16 @@ class TraceFileReader extends HTMLElement {
} }
} }
extendAndSanitizeModel(data, keys) { extendAndSanitizeModel(data) {
const checkNonNegativeProperty = (obj, property) => { const checkNonNegativeProperty = (obj, property) => {
console.assert(obj[property] >= 0, 'negative property', obj, property); console.assert(obj[property] >= 0, 'negative property', obj, property);
}; };
for (const isolate of Object.keys(data)) { for (const isolate of Object.keys(data)) {
for (const gc of Object.keys(data[isolate].gcs)) { const isolate_data = data[isolate];
for (const data_set_key of keys[isolate]) { for (const gc of Object.keys(isolate_data.gcs)) {
const gc_data = isolate_data.gcs[gc];
for (const data_set_key of isolate_data.data_sets) {
const data_set = data[isolate].gcs[gc][data_set_key]; const data_set = data[isolate].gcs[gc][data_set_key];
// Create a ranked instance type array that sorts instance types by // Create a ranked instance type array that sorts instance types by
// memory size (overall). // memory size (overall).
...@@ -191,52 +189,66 @@ class TraceFileReader extends HTMLElement { ...@@ -191,52 +189,66 @@ class TraceFileReader extends HTMLElement {
} }
createModelFromChromeTraceFile(contents) { createModelFromChromeTraceFile(contents) {
console.log('Processing log as chrome trace file.'); // Trace files support two formats.
const data = Object.create(null); // Final data container. // {traceEvents: [ data ]}
const keys = Object.create(null); // Collecting 'keys' per isolate. 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}).`);
// Pop last line in log as it might be broken. // Pop last line in log as it might be broken.
contents.pop(); contents.pop();
// Remove trailing comma. // Remove trailing comma.
contents[contents.length - 1] = contents[contents.length - 1].slice(0, -1); contents[contents.length - 1] = contents[contents.length - 1].slice(0, -1);
// Terminate JSON. // Terminate JSON.
const sanitized_contents = [...contents, ']}'].join(''); const sanitized_contents = [...contents, handler.endToken].join('');
const data = Object.create(null); // Final data container.
try { try {
const raw_data = JSON.parse(sanitized_contents); const raw_data = JSON.parse(sanitized_contents);
const objects_stats_data = const raw_array_data = handler.getDataArray(raw_data);
raw_data.traceEvents.filter(e => e.name == 'V8.GC_Objects_Stats'); raw_array_data.filter(e => e.name === 'V8.GC_Objects_Stats')
objects_stats_data.forEach(trace_data => { .forEach(trace_data => {
const actual_data = trace_data.args; const actual_data = trace_data.args;
const data_sets = new Set(Object.keys(actual_data)); const data_sets = new Set(Object.keys(actual_data));
Object.keys(actual_data).forEach(data_set => { Object.keys(actual_data).forEach(data_set => {
const string_entry = actual_data[data_set]; const string_entry = actual_data[data_set];
try { try {
const entry = JSON.parse(string_entry); const entry = JSON.parse(string_entry);
this.createOrUpdateEntryIfNeeded(data, keys, entry); this.createOrUpdateEntryIfNeeded(data, entry);
this.createDatasetIfNeeded(data, keys, entry, data_set); this.createDatasetIfNeeded(data, entry, data_set);
const isolate = entry.isolate; const isolate = entry.isolate;
const time = entry.time; const time = entry.time;
const gc_id = entry.id; const gc_id = entry.id;
data[isolate].gcs[gc_id].time = time; data[isolate].gcs[gc_id].time = time;
data[isolate].gcs[gc_id][data_set].bucket_sizes = data[isolate].gcs[gc_id][data_set].bucket_sizes =
entry.bucket_sizes; entry.bucket_sizes;
for (let [instance_type, value] of Object.entries( for (let [instance_type, value] of Object.entries(
entry.type_data)) { entry.type_data)) {
// Trace file format uses markers that do not have actual // Trace file format uses markers that do not have actual
// properties. // properties.
if (!('overall' in value)) continue; if (!('overall' in value)) continue;
this.addInstanceTypeData( this.addInstanceTypeData(
data, keys, isolate, gc_id, data_set, instance_type, value); data, isolate, gc_id, data_set, instance_type, value);
} }
} catch (e) { } catch (e) {
console.log('Unable to parse data set entry', e); console.log('Unable to parse data set entry', e);
} }
}); });
}); });
} catch (e) { } catch (e) {
console.error('Unable to parse chrome trace file.', e); console.error('Unable to parse chrome trace file.', e);
} }
this.extendAndSanitizeModel(data, keys);
return data; return data;
} }
...@@ -254,14 +266,12 @@ class TraceFileReader extends HTMLElement { ...@@ -254,14 +266,12 @@ class TraceFileReader extends HTMLElement {
}); });
const data = Object.create(null); // Final data container. const data = Object.create(null); // Final data container.
const keys = Object.create(null); // Collecting 'keys' per isolate.
for (var entry of contents) { for (var entry of contents) {
if (entry === null || entry.type === undefined) { if (entry === null || entry.type === undefined) {
continue; continue;
} }
if (entry.type === 'zone') { if (entry.type === 'zone') {
this.createOrUpdateEntryIfNeeded(data, keys, entry); this.createOrUpdateEntryIfNeeded(data, entry);
const stacktrace = ('stacktrace' in entry) ? entry.stacktrace : []; const stacktrace = ('stacktrace' in entry) ? entry.stacktrace : [];
data[entry.isolate].samples.zone[entry.time] = { data[entry.isolate].samples.zone[entry.time] = {
allocated: entry.allocated, allocated: entry.allocated,
...@@ -270,26 +280,26 @@ class TraceFileReader extends HTMLElement { ...@@ -270,26 +280,26 @@ class TraceFileReader extends HTMLElement {
}; };
} else if ( } else if (
entry.type === 'zonecreation' || entry.type === 'zonedestruction') { entry.type === 'zonecreation' || entry.type === 'zonedestruction') {
this.createOrUpdateEntryIfNeeded(data, keys, entry); this.createOrUpdateEntryIfNeeded(data, entry);
data[entry.isolate].zonetags.push( data[entry.isolate].zonetags.push(
Object.assign({opening: entry.type === 'zonecreation'}, entry)); Object.assign({opening: entry.type === 'zonecreation'}, entry));
} else if (entry.type === 'gc_descriptor') { } else if (entry.type === 'gc_descriptor') {
this.createOrUpdateEntryIfNeeded(data, keys, entry); this.createOrUpdateEntryIfNeeded(data, entry);
data[entry.isolate].gcs[entry.id].time = entry.time; data[entry.isolate].gcs[entry.id].time = entry.time;
if ('zone' in entry) if ('zone' in entry)
data[entry.isolate].gcs[entry.id].malloced = entry.zone; data[entry.isolate].gcs[entry.id].malloced = entry.zone;
} else if (entry.type === 'instance_type_data') { } else if (entry.type === 'instance_type_data') {
if (entry.id in data[entry.isolate].gcs) { if (entry.id in data[entry.isolate].gcs) {
this.createOrUpdateEntryIfNeeded(data, keys, entry); this.createOrUpdateEntryIfNeeded(data, entry);
this.createDatasetIfNeeded(data, keys, entry, entry.key); this.createDatasetIfNeeded(data, entry, entry.key);
this.addInstanceTypeData( this.addInstanceTypeData(
data, keys, entry.isolate, entry.id, entry.key, data, entry.isolate, entry.id, entry.key,
entry.instance_type_name, entry); entry.instance_type_name, entry);
} }
} else if (entry.type === 'bucket_sizes') { } else if (entry.type === 'bucket_sizes') {
if (entry.id in data[entry.isolate].gcs) { if (entry.id in data[entry.isolate].gcs) {
this.createOrUpdateEntryIfNeeded(data, keys, entry); this.createOrUpdateEntryIfNeeded(data, entry);
this.createDatasetIfNeeded(data, keys, entry, entry.key); this.createDatasetIfNeeded(data, entry, entry.key);
data[entry.isolate].gcs[entry.id][entry.key].bucket_sizes = data[entry.isolate].gcs[entry.id][entry.key].bucket_sizes =
entry.sizes; entry.sizes;
} }
...@@ -297,7 +307,6 @@ class TraceFileReader extends HTMLElement { ...@@ -297,7 +307,6 @@ class TraceFileReader extends HTMLElement {
console.log('Unknown entry type: ' + entry.type); console.log('Unknown entry type: ' + entry.type);
} }
} }
this.extendAndSanitizeModel(data, keys);
return data; return data;
} }
} }
......
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