[tools] Add api events timeline-track to system-analyzer
- Clean up entry selection code - Add source positions for code and deopt events - Fix log entry selection from script - Improve log parsing speed Bug: v8:10644 Change-Id: Ie466679132b8ce24506ecf75223118b32275f931 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2569756 Commit-Queue: Camillo Bruni <cbruni@chromium.org> Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org> Cr-Commit-Position: refs/heads/master@{#71624}
This commit is contained in:
parent
ddbc2ba4a1
commit
1d7aa2f8d0
@ -26,6 +26,7 @@ group("v8_mjsunit") {
|
||||
"../../tools/profile_view.mjs",
|
||||
"../../tools/splaytree.mjs",
|
||||
"../../tools/system-analyzer/helper.mjs",
|
||||
"../../tools/system-analyzer/log/api.mjs",
|
||||
"../../tools/system-analyzer/log/code.mjs",
|
||||
"../../tools/system-analyzer/log/ic.mjs",
|
||||
"../../tools/system-analyzer/log/log.mjs",
|
||||
|
@ -62,7 +62,11 @@ export class CsvParser {
|
||||
}
|
||||
// Convert the selected escape sequence to a single character.
|
||||
let escapeChars = string.substring(pos, nextPos);
|
||||
result += String.fromCharCode(parseInt(escapeChars, 16));
|
||||
if (escapeChars === '2C') {
|
||||
result += ',';
|
||||
} else {
|
||||
result += String.fromCharCode(parseInt(escapeChars, 16));
|
||||
}
|
||||
}
|
||||
|
||||
// Continue looking for the next escape sequence.
|
||||
|
@ -47,6 +47,7 @@ export class Script {
|
||||
source;
|
||||
// Map<line, Map<column, SourcePosition>>
|
||||
lineToColumn = new Map();
|
||||
_entries = [];
|
||||
|
||||
constructor(id) {
|
||||
this.id = id;
|
||||
@ -62,6 +63,10 @@ export class Script {
|
||||
return this.source.length;
|
||||
}
|
||||
|
||||
get entries() {
|
||||
return this._entries;
|
||||
}
|
||||
|
||||
addSourcePosition(line, column, entry) {
|
||||
let sourcePosition = this.lineToColumn.get(line)?.get(column);
|
||||
if (sourcePosition === undefined) {
|
||||
@ -69,6 +74,7 @@ export class Script {
|
||||
this._addSourcePosition(line, column, sourcePosition);
|
||||
}
|
||||
sourcePosition.addEntry(entry);
|
||||
this._entries.push(entry);
|
||||
return sourcePosition;
|
||||
}
|
||||
|
||||
@ -83,10 +89,12 @@ export class Script {
|
||||
this.sourcePositions.push(sourcePosition);
|
||||
columnToSourcePosition.set(column, sourcePosition);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
class SourceInfo{
|
||||
class SourceInfo {
|
||||
script;
|
||||
start;
|
||||
end;
|
||||
@ -107,6 +115,10 @@ class SourceInfo{
|
||||
setDisassemble(code) {
|
||||
this.disassemble = code;
|
||||
}
|
||||
|
||||
getSourceCode() {
|
||||
return this.script.source?.substring(this.start, this.end);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -170,7 +182,7 @@ export class Profile {
|
||||
return this.CodeState.IGNITION;
|
||||
case '-':
|
||||
return this.CodeState.NATIVE_CONTEXT_INDEPENDENT;
|
||||
case '=':
|
||||
case '+':
|
||||
return this.CodeState.TURBOPROP;
|
||||
case '*':
|
||||
return this.CodeState.TURBOFAN;
|
||||
@ -639,9 +651,14 @@ class DynamicFuncCodeEntry extends CodeEntry {
|
||||
constructor(size, type, func, state) {
|
||||
super(size, '', type);
|
||||
this.func = func;
|
||||
func.addDynamicCode(this);
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
getSourceCode() {
|
||||
return this.source?.getSourceCode();
|
||||
}
|
||||
|
||||
static STATE_PREFIX = ["", "~", "-", "+", "*"];
|
||||
getState() {
|
||||
return DynamicFuncCodeEntry.STATE_PREFIX[this.state];
|
||||
@ -675,10 +692,26 @@ class DynamicFuncCodeEntry extends CodeEntry {
|
||||
* @constructor
|
||||
*/
|
||||
class FunctionEntry extends CodeEntry {
|
||||
|
||||
// Contains the list of generated code for this function.
|
||||
_codeEntries = new Set();
|
||||
|
||||
constructor(name) {
|
||||
super(0, name);
|
||||
}
|
||||
|
||||
addDynamicCode(code) {
|
||||
if (code.func != this) {
|
||||
throw new Error("Adding dynamic code to wrong function");
|
||||
}
|
||||
this._codeEntries.add(code);
|
||||
}
|
||||
|
||||
getSourceCode() {
|
||||
// All code entries should map to the same source positions.
|
||||
return this._codeEntries.values().next().value.getSourceCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns node name.
|
||||
*/
|
||||
|
@ -17,6 +17,7 @@ class State {
|
||||
_mapTimeline;
|
||||
_deoptTimeline;
|
||||
_codeTimeline;
|
||||
_apiTimeline;
|
||||
_minStartTime = Number.POSITIVE_INFINITY;
|
||||
_maxEndTime = Number.NEGATIVE_INFINITY;
|
||||
|
||||
@ -38,11 +39,13 @@ class State {
|
||||
}
|
||||
}
|
||||
|
||||
setTimelines(mapTimeline, icTimeline, deoptTimeline, codeTimeline) {
|
||||
setTimelines(
|
||||
mapTimeline, icTimeline, deoptTimeline, codeTimeline, apiTimeline) {
|
||||
this._mapTimeline = mapTimeline;
|
||||
this._icTimeline = icTimeline;
|
||||
this._deoptTimeline = deoptTimeline;
|
||||
this._codeTimeline = codeTimeline;
|
||||
this._apiTimeline = apiTimeline;
|
||||
for (let timeline of arguments) {
|
||||
if (timeline === undefined) return;
|
||||
this._minStartTime = Math.min(this._minStartTime, timeline.startTime);
|
||||
@ -70,9 +73,14 @@ class State {
|
||||
return this._codeTimeline;
|
||||
}
|
||||
|
||||
get apiTimeline() {
|
||||
return this._apiTimeline;
|
||||
}
|
||||
|
||||
get timelines() {
|
||||
return [
|
||||
this.mapTimeline, this.icTimeline, this.deoptTimeline, this.codeTimeline
|
||||
this._mapTimeline, this._icTimeline, this._deoptTimeline,
|
||||
this._codeTimeline, this._apiTimeline
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -18,9 +18,7 @@ found in the LICENSE file. -->
|
||||
// Delay loading of the main App
|
||||
(async function() {
|
||||
let module = await import('./index.mjs');
|
||||
globalThis.app = new module.App("#log-file-reader", "#map-panel", "#map-stats-panel",
|
||||
"#timeline-panel", "#ic-panel", "#map-track", "#ic-track", "#deopt-track",
|
||||
"#code-track", "#source-panel", "#code-panel", "#tool-tip");
|
||||
globalThis.app = new module.App();
|
||||
})();
|
||||
</script>
|
||||
|
||||
@ -104,6 +102,7 @@ found in the LICENSE file. -->
|
||||
<timeline-track id="ic-track"></timeline-track>
|
||||
<timeline-track id="deopt-track"></timeline-track>
|
||||
<timeline-track id="code-track"></timeline-track>
|
||||
<timeline-track id="api-track"></timeline-track>
|
||||
</timeline-panel>
|
||||
<div class="panels">
|
||||
<map-panel id="map-panel"></map-panel>
|
||||
@ -159,16 +158,17 @@ found in the LICENSE file. -->
|
||||
<code>--trace-maps</code>
|
||||
</a>
|
||||
</dt>
|
||||
<dd>Log<a href="https://v8.dev/blog/fast-properties" target="_blank">
|
||||
Maps</a></dd>
|
||||
<dd>
|
||||
Log<a href="https://v8.dev/blog/fast-properties">Maps</a>
|
||||
</dd>
|
||||
<dt>
|
||||
<a href="https://source.chromium.org/search?q=FLAG_trace_ic">
|
||||
<code>--trace-ic</code>
|
||||
</a>
|
||||
</dt>
|
||||
<dd>Log
|
||||
<a href="https://mathiasbynens.be/notes/shapes-ics" target="_blank">
|
||||
ICs</a></dd>
|
||||
<dd>
|
||||
Log <a href="https://mathiasbynens.be/notes/shapes-ics">ICs</a>
|
||||
</dd>
|
||||
<dt>
|
||||
<a href="https://source.chromium.org/search?q=FLAG_log_source_code">
|
||||
<code>--log-source-code</code>
|
||||
@ -181,6 +181,12 @@ found in the LICENSE file. -->
|
||||
</a>
|
||||
</dt>
|
||||
<dd>Log detailed generated generated code</dd>
|
||||
<dt>
|
||||
<a href="https://source.chromium.org/search?q=FLAG_log_api">
|
||||
<code>--log-api</code>
|
||||
</a>
|
||||
</dt>
|
||||
<dd>Log various API uses.</dd>
|
||||
</dl>
|
||||
|
||||
<h3>Keyboard Shortcuts for Navigation</h3>
|
||||
|
@ -5,6 +5,8 @@
|
||||
import {SourcePosition} from '../profile.mjs';
|
||||
|
||||
import {State} from './app-model.mjs';
|
||||
import {ApiLogEntry} from './log/api.mjs';
|
||||
import {DeoptLogEntry} from './log/code.mjs';
|
||||
import {CodeLogEntry} from './log/code.mjs';
|
||||
import {IcLogEntry} from './log/ic.mjs';
|
||||
import {MapLogEntry} from './log/map.mjs';
|
||||
@ -17,24 +19,22 @@ class App {
|
||||
_view;
|
||||
_navigation;
|
||||
_startupPromise;
|
||||
constructor(
|
||||
fileReaderId, mapPanelId, mapStatsPanelId, timelinePanelId, icPanelId,
|
||||
mapTrackId, icTrackId, deoptTrackId, codeTrackId, sourcePanelId,
|
||||
codePanelId, toolTipId) {
|
||||
constructor() {
|
||||
this._view = {
|
||||
__proto__: null,
|
||||
logFileReader: $(fileReaderId),
|
||||
icPanel: $(icPanelId),
|
||||
mapPanel: $(mapPanelId),
|
||||
mapStatsPanel: $(mapStatsPanelId),
|
||||
timelinePanel: $(timelinePanelId),
|
||||
mapTrack: $(mapTrackId),
|
||||
icTrack: $(icTrackId),
|
||||
deoptTrack: $(deoptTrackId),
|
||||
codeTrack: $(codeTrackId),
|
||||
sourcePanel: $(sourcePanelId),
|
||||
codePanel: $(codePanelId),
|
||||
toolTip: $(toolTipId),
|
||||
logFileReader: $('#log-file-reader'),
|
||||
mapPanel: $('#map-panel'),
|
||||
mapStatsPanel: $('#map-stats-panel'),
|
||||
timelinePanel: $('#timeline-panel'),
|
||||
mapTrack: $('#map-track'),
|
||||
icTrack: $('#ic-track'),
|
||||
icPanel: $('#ic-panel'),
|
||||
deoptTrack: $('#deopt-track'),
|
||||
codePanel: $('#code-panel'),
|
||||
codeTrack: $('#code-track'),
|
||||
apiTrack: $('#api-track'),
|
||||
sourcePanel: $('#source-panel'),
|
||||
toolTip: $('#tool-tip'),
|
||||
};
|
||||
this.toggleSwitch = $('.theme-switch input[type="checkbox"]');
|
||||
this.toggleSwitch.addEventListener('change', (e) => this.switchTheme(e));
|
||||
@ -67,19 +67,61 @@ class App {
|
||||
}
|
||||
|
||||
handleShowEntries(e) {
|
||||
const entry = e.entries[0];
|
||||
if (entry instanceof MapLogEntry) {
|
||||
this.showMapEntries(e.entries);
|
||||
} else if (entry instanceof IcLogEntry) {
|
||||
this.showIcEntries(e.entries);
|
||||
} else if (entry instanceof SourcePosition) {
|
||||
this.showSourcePositionEntries(e.entries);
|
||||
} else if (e.entries[0] instanceof CodeLogEntry) {
|
||||
this.showCodeEntries(e.entries);
|
||||
} else {
|
||||
throw new Error('Unknown selection type!');
|
||||
}
|
||||
e.stopPropagation();
|
||||
this.showEntries(e.entries);
|
||||
}
|
||||
|
||||
showEntries(entries) {
|
||||
const groups = new Map();
|
||||
for (let entry of entries) {
|
||||
const group = groups.get(entry.constructor);
|
||||
if (group !== undefined) {
|
||||
group.push(entry);
|
||||
} else {
|
||||
groups.set(entry.constructor, [entry]);
|
||||
}
|
||||
}
|
||||
groups.forEach(entries => this.showEntriesOfSingleType(entries));
|
||||
}
|
||||
|
||||
showEntriesOfSingleType(entries) {
|
||||
switch (entries[0].constructor) {
|
||||
case SourcePosition:
|
||||
return this.showSourcePositions(entries);
|
||||
case MapLogEntry:
|
||||
return this.showMapEntries(entries);
|
||||
case IcLogEntry:
|
||||
return this.showIcEntries(entries);
|
||||
case ApiLogEntry:
|
||||
return this.showApiEntries(entries);
|
||||
case CodeLogEntry:
|
||||
return this.showCodeEntries(entries);
|
||||
case DeoptLogEntry:
|
||||
return this.showDeoptEntries(entries);
|
||||
default:
|
||||
throw new Error('Unknown selection type!');
|
||||
}
|
||||
}
|
||||
|
||||
handleShowEntryDetail(e) {
|
||||
e.stopPropagation();
|
||||
const entry = e.entry;
|
||||
switch (entry.constructor) {
|
||||
case SourcePosition:
|
||||
return this.selectSourcePosition(entry);
|
||||
case MapLogEntry:
|
||||
return this.selectMapLogEntry(entry);
|
||||
case IcLogEntry:
|
||||
return this.selectIcLogEntry(entry);
|
||||
case ApiLogEntry:
|
||||
return this.selectApiLogEntry(entry);
|
||||
case CodeLogEntry:
|
||||
return this.selectCodeLogEntry(entry);
|
||||
case DeoptLogEntry:
|
||||
return this.selectDeoptLogEntry(entry);
|
||||
default:
|
||||
throw new Error('Unknown selection type!');
|
||||
}
|
||||
}
|
||||
|
||||
showMapEntries(entries) {
|
||||
@ -94,17 +136,22 @@ class App {
|
||||
}
|
||||
|
||||
showDeoptEntries(entries) {
|
||||
// TODO: creat list panel.
|
||||
// TODO: create list panel.
|
||||
this._state.selectedDeoptLogEntries = entries;
|
||||
}
|
||||
|
||||
showCodeEntries(entries) {
|
||||
// TODO: creat list panel
|
||||
// TODO: create list panel
|
||||
this._state.selectedCodeLogEntries = entries;
|
||||
this._view.codePanel.selectedEntries = entries;
|
||||
}
|
||||
|
||||
showSourcePositionEntries(entries) {
|
||||
showApiEntries(entries) {
|
||||
// TODO: create list panel
|
||||
this._state.selectedApiLogEntries = entries;
|
||||
}
|
||||
|
||||
showSourcePositions(entries) {
|
||||
// TODO: Handle multiple source position selection events
|
||||
this._view.sourcePanel.selectedSourcePositions = entries
|
||||
}
|
||||
@ -120,32 +167,17 @@ class App {
|
||||
this.showIcEntries(this._state.icTimeline.selection ?? []);
|
||||
this.showDeoptEntries(this._state.deoptTimeline.selection ?? []);
|
||||
this.showCodeEntries(this._state.codeTimeline.selection ?? []);
|
||||
this.showApiEntries(this._state.apiTimeline.selection ?? []);
|
||||
this._view.timelinePanel.timeSelection = {start, end};
|
||||
}
|
||||
|
||||
handleShowEntryDetail(e) {
|
||||
const entry = e.entry;
|
||||
if (entry instanceof MapLogEntry) {
|
||||
this.selectMapLogEntry(e.entry);
|
||||
} else if (entry instanceof IcLogEntry) {
|
||||
this.selectICLogEntry(e.entry);
|
||||
} else if (entry instanceof SourcePosition) {
|
||||
this.selectSourcePosition(e.entry);
|
||||
} else if (e.entry instanceof CodeLogEntry) {
|
||||
this.selectCodeLogEntry(e.entry);
|
||||
} else {
|
||||
throw new Error('Unknown selection type!');
|
||||
}
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
selectMapLogEntry(entry) {
|
||||
this._state.map = entry;
|
||||
this._view.mapTrack.selectedEntry = entry;
|
||||
this._view.mapPanel.map = entry;
|
||||
}
|
||||
|
||||
selectICLogEntry(entry) {
|
||||
selectIcLogEntry(entry) {
|
||||
this._state.ic = entry;
|
||||
this._view.icPanel.selectedEntry = [entry];
|
||||
}
|
||||
@ -155,6 +187,15 @@ class App {
|
||||
this._view.codePanel.entry = entry;
|
||||
}
|
||||
|
||||
selectDeoptLogEntry(entry) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
selectApiLogEntry(entry) {
|
||||
this._state.apiLogEntry = entry;
|
||||
this._view.apiTrack.selectedEntry = entry;
|
||||
}
|
||||
|
||||
selectSourcePosition(sourcePositions) {
|
||||
if (!sourcePositions.script) return;
|
||||
this._view.sourcePanel.selectedSourcePositions = [sourcePositions];
|
||||
@ -183,8 +224,9 @@ class App {
|
||||
const icTimeline = processor.icTimeline;
|
||||
const deoptTimeline = processor.deoptTimeline;
|
||||
const codeTimeline = processor.codeTimeline;
|
||||
const apiTimeline = processor.apiTimeline;
|
||||
this._state.setTimelines(
|
||||
mapTimeline, icTimeline, deoptTimeline, codeTimeline);
|
||||
mapTimeline, icTimeline, deoptTimeline, codeTimeline, apiTimeline);
|
||||
// Transitions must be set before timeline for stats panel.
|
||||
this._view.mapPanel.timeline = mapTimeline;
|
||||
this._view.mapStatsPanel.transitions =
|
||||
@ -208,6 +250,7 @@ class App {
|
||||
this._view.icTrack.data = this._state.icTimeline;
|
||||
this._view.deoptTrack.data = this._state.deoptTimeline;
|
||||
this._view.codeTrack.data = this._state.codeTimeline;
|
||||
this._view.apiTrack.data = this._state.apiTimeline;
|
||||
}
|
||||
|
||||
switchTheme(event) {
|
||||
|
14
tools/system-analyzer/log/api.mjs
Normal file
14
tools/system-analyzer/log/api.mjs
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright 2020 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.
|
||||
import {LogEntry} from './log.mjs';
|
||||
|
||||
export class ApiLogEntry extends LogEntry {
|
||||
constructor(type, time, name) {
|
||||
super(type, time);
|
||||
this._name = name;
|
||||
}
|
||||
toString() {
|
||||
return `Api(${this.type}): ${this._name}`;
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ export class IcLogEntry extends LogEntry {
|
||||
} else if (this.type.indexOf('Load') !== -1) {
|
||||
this.category = 'Load';
|
||||
}
|
||||
let parts = fn_file.split(' ');
|
||||
const parts = fn_file.split(' ');
|
||||
this.functionName = parts[0];
|
||||
this.file = parts[1];
|
||||
let position = line + ':' + column;
|
||||
|
@ -3,19 +3,20 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
export class LogEntry {
|
||||
_time;
|
||||
_type;
|
||||
constructor(type, time) {
|
||||
// TODO(zcankara) remove type and add empty getters to override
|
||||
this._time = time;
|
||||
this._type = type;
|
||||
this.sourcePosition = undefined;
|
||||
}
|
||||
|
||||
get time() {
|
||||
return this._time;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
// Returns an Array of all possible #type values.
|
||||
static get allTypes() {
|
||||
throw new Error('Not implemented.');
|
||||
|
@ -34,17 +34,6 @@ define(Array.prototype, 'last', function() {
|
||||
// Map Log Events
|
||||
|
||||
class MapLogEntry extends LogEntry {
|
||||
id = -1;
|
||||
edge = undefined;
|
||||
children = [];
|
||||
depth = 0;
|
||||
_isDeprecated = false;
|
||||
deprecatedTargets = null;
|
||||
leftId = 0;
|
||||
rightId = 0;
|
||||
filePosition = '';
|
||||
script = '';
|
||||
description = '';
|
||||
constructor(id, time) {
|
||||
if (!time) throw new Error('Invalid time');
|
||||
// Use MapLogEntry.type getter instead of property, since we only know the
|
||||
@ -52,6 +41,17 @@ class MapLogEntry extends LogEntry {
|
||||
super(undefined, time);
|
||||
this.id = id;
|
||||
MapLogEntry.set(id, this);
|
||||
this.id = -1;
|
||||
this.edge = undefined;
|
||||
this.children = [];
|
||||
this.depth = 0;
|
||||
this._isDeprecated = false;
|
||||
this.deprecatedTargets = null;
|
||||
this.leftId = 0;
|
||||
this.rightId = 0;
|
||||
this.filePosition = '';
|
||||
this.script = '';
|
||||
this.description = '';
|
||||
}
|
||||
|
||||
toString() {
|
||||
|
@ -5,6 +5,7 @@
|
||||
import {LogReader, parseString, parseVarArgs} from '../logreader.mjs';
|
||||
import {Profile} from '../profile.mjs';
|
||||
|
||||
import {ApiLogEntry} from './log/api.mjs';
|
||||
import {CodeLogEntry, DeoptLogEntry} from './log/code.mjs';
|
||||
import {IcLogEntry} from './log/ic.mjs';
|
||||
import {Edge, MapLogEntry} from './log/map.mjs';
|
||||
@ -18,7 +19,10 @@ export class Processor extends LogReader {
|
||||
_icTimeline = new Timeline();
|
||||
_deoptTimeline = new Timeline();
|
||||
_codeTimeline = new Timeline();
|
||||
_apiTimeline = new Timeline();
|
||||
_formatPCRegexp = /(.*):[0-9]+:[0-9]+$/;
|
||||
_lastTimestamp = 0;
|
||||
_lastCodeLogEntry;
|
||||
MAJOR_VERSION = 7;
|
||||
MINOR_VERSION = 6;
|
||||
constructor(logString) {
|
||||
@ -115,6 +119,10 @@ export class Processor extends LogReader {
|
||||
parsers: propertyICParser,
|
||||
processor: this.processPropertyIC.bind(this, 'StoreInArrayLiteralIC')
|
||||
},
|
||||
'api': {
|
||||
parsers: [parseString, parseVarArgs],
|
||||
processor: this.processApiEvent
|
||||
},
|
||||
};
|
||||
if (logString) this.processString(logString);
|
||||
}
|
||||
@ -182,7 +190,21 @@ export class Processor extends LogReader {
|
||||
});
|
||||
}
|
||||
|
||||
processV8Version(majorVersion, minorVersion) {
|
||||
if ((majorVersion == this.MAJOR_VERSION &&
|
||||
minorVersion <= this.MINOR_VERSION) ||
|
||||
(majorVersion < this.MAJOR_VERSION)) {
|
||||
window.alert(
|
||||
`Unsupported version ${majorVersion}.${minorVersion}. \n` +
|
||||
`Please use the matching tool for given the V8 version.`);
|
||||
}
|
||||
}
|
||||
|
||||
processCodeCreation(type, kind, timestamp, start, size, name, maybe_func) {
|
||||
this._lastTimestamp = timestamp;
|
||||
if (timestamp == 5724567) {
|
||||
console.log(start);
|
||||
}
|
||||
let entry;
|
||||
let stateName = '';
|
||||
if (maybe_func.length) {
|
||||
@ -194,26 +216,20 @@ export class Processor extends LogReader {
|
||||
} else {
|
||||
entry = this._profile.addCode(type, name, timestamp, start, size);
|
||||
}
|
||||
this._codeTimeline.push(
|
||||
new CodeLogEntry(type + stateName, timestamp, kind, entry));
|
||||
this._lastCodeLogEntry =
|
||||
new CodeLogEntry(type + stateName, timestamp, kind, entry);
|
||||
this._codeTimeline.push(this._lastCodeLogEntry);
|
||||
}
|
||||
|
||||
processCodeDeopt(
|
||||
timestamp, codeSize, instructionStart, inliningId, scriptOffset,
|
||||
deoptKind, deoptLocation, deoptReason) {
|
||||
this._deoptTimeline.push(new DeoptLogEntry(
|
||||
this._lastTimestamp = timestamp;
|
||||
const logEntry = new DeoptLogEntry(
|
||||
deoptKind, timestamp, deoptReason, deoptLocation, scriptOffset,
|
||||
instructionStart, codeSize, inliningId));
|
||||
}
|
||||
|
||||
processV8Version(majorVersion, minorVersion) {
|
||||
if ((majorVersion == this.MAJOR_VERSION &&
|
||||
minorVersion <= this.MINOR_VERSION) ||
|
||||
(majorVersion < this.MAJOR_VERSION)) {
|
||||
window.alert(
|
||||
`Unsupported version ${majorVersion}.${minorVersion}. \n` +
|
||||
`Please use the matching tool for given the V8 version.`);
|
||||
}
|
||||
instructionStart, codeSize, inliningId);
|
||||
this._deoptTimeline.push(logEntry);
|
||||
this.addSourcePosition(this._profile.findEntry(instructionStart), logEntry);
|
||||
}
|
||||
|
||||
processScriptSource(scriptId, url, source) {
|
||||
@ -233,11 +249,24 @@ export class Processor extends LogReader {
|
||||
}
|
||||
|
||||
processCodeSourceInfo(
|
||||
start, script, startPos, endPos, sourcePositions, inliningPositions,
|
||||
start, scriptId, startPos, endPos, sourcePositions, inliningPositions,
|
||||
inlinedFunctions) {
|
||||
this._profile.addSourcePositions(
|
||||
start, script, startPos, endPos, sourcePositions, inliningPositions,
|
||||
start, scriptId, startPos, endPos, sourcePositions, inliningPositions,
|
||||
inlinedFunctions);
|
||||
let profileEntry = this._profile.findEntry(start);
|
||||
if (profileEntry !== this._lastCodeLogEntry._entry) return;
|
||||
this.addSourcePosition(profileEntry, this._lastCodeLogEntry);
|
||||
this._lastCodeLogEntry = undefined;
|
||||
}
|
||||
|
||||
addSourcePosition(profileEntry, logEntry) {
|
||||
let script = this.getProfileEntryScript(profileEntry);
|
||||
const parts = profileEntry.getRawName().split(':');
|
||||
if (parts.length < 3) return;
|
||||
const line = parseInt(parts[parts.length - 2]);
|
||||
const column = parseInt(parts[parts.length - 1]);
|
||||
logEntry.sourcePosition = script.addSourcePosition(line, column, logEntry);
|
||||
}
|
||||
|
||||
processCodeDisassemble(start, kind, disassemble) {
|
||||
@ -247,6 +276,7 @@ export class Processor extends LogReader {
|
||||
processPropertyIC(
|
||||
type, pc, time, line, column, old_state, new_state, map, key, modifier,
|
||||
slow_reason) {
|
||||
this._lastTimestamp = time;
|
||||
let profileEntry = this._profile.findEntry(pc);
|
||||
let fnName = this.formatProfileEntry(profileEntry);
|
||||
let script = this.getProfileEntryScript(profileEntry);
|
||||
@ -284,6 +314,7 @@ export class Processor extends LogReader {
|
||||
}
|
||||
|
||||
processMap(type, time, from, to, pc, line, column, reason, name) {
|
||||
this._lastTimestamp = time;
|
||||
const time_ = parseInt(time);
|
||||
if (type === 'Deprecate') return this.deprecateMap(type, time_, from);
|
||||
// Skip normalized maps that were cached so we don't introduce multiple
|
||||
@ -318,12 +349,14 @@ export class Processor extends LogReader {
|
||||
}
|
||||
|
||||
deprecateMap(type, time, id) {
|
||||
this._lastTimestamp = time;
|
||||
this.getMapEntry(id, time).deprecate();
|
||||
}
|
||||
|
||||
processMapCreate(time, id) {
|
||||
// map-create events might override existing maps if the addresses get
|
||||
// recycled. Hence we do not check for existing maps.
|
||||
this._lastTimestamp = time;
|
||||
this.createMapEntry(id, time);
|
||||
}
|
||||
|
||||
@ -334,6 +367,7 @@ export class Processor extends LogReader {
|
||||
}
|
||||
|
||||
createMapEntry(id, time) {
|
||||
this._lastTimestamp = time;
|
||||
const map = new MapLogEntry(id, time);
|
||||
this._mapTimeline.push(map);
|
||||
return map;
|
||||
@ -357,6 +391,16 @@ export class Processor extends LogReader {
|
||||
return script;
|
||||
}
|
||||
|
||||
processApiEvent(name, varArgs) {
|
||||
if (varArgs.length == 0) {
|
||||
varArgs = [name];
|
||||
const index = name.indexOf(':');
|
||||
if (index > 0) name = name.substr(0, index);
|
||||
}
|
||||
this._apiTimeline.push(
|
||||
new ApiLogEntry(name, this._lastTimestamp, varArgs[0]));
|
||||
}
|
||||
|
||||
get icTimeline() {
|
||||
return this._icTimeline;
|
||||
}
|
||||
@ -373,6 +417,10 @@ export class Processor extends LogReader {
|
||||
return this._codeTimeline;
|
||||
}
|
||||
|
||||
get apiTimeline() {
|
||||
return this._apiTimeline;
|
||||
}
|
||||
|
||||
get scripts() {
|
||||
return this._profile.scripts_.filter(script => script !== undefined);
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ DOM.defineCustomElement(
|
||||
|
||||
set selectedEntries(entries) {
|
||||
this._selectedEntries = entries;
|
||||
// TODO: add code selection dropdown
|
||||
this._entry = entries.first();
|
||||
this.update();
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ DOM.defineCustomElement('view/source-panel',
|
||||
const option =
|
||||
this.scriptDropdown.options[this.scriptDropdown.selectedIndex];
|
||||
this.script = option.script;
|
||||
this.selectLogEntries(this._script.entries());
|
||||
this.selectLogEntries(this._script.entries);
|
||||
}
|
||||
|
||||
handleSourcePositionClick(e) {
|
||||
@ -124,21 +124,7 @@ DOM.defineCustomElement('view/source-panel',
|
||||
}
|
||||
|
||||
selectLogEntries(logEntries) {
|
||||
let icLogEntries = [];
|
||||
let mapLogEntries = [];
|
||||
for (const entry of logEntries) {
|
||||
if (entry instanceof MapLogEntry) {
|
||||
mapLogEntries.push(entry);
|
||||
} else if (entry instanceof IcLogEntry) {
|
||||
icLogEntries.push(entry);
|
||||
}
|
||||
}
|
||||
if (icLogEntries.length > 0) {
|
||||
this.dispatchEvent(new SelectionEvent(icLogEntries));
|
||||
}
|
||||
if (mapLogEntries.length > 0) {
|
||||
this.dispatchEvent(new SelectionEvent(mapLogEntries));
|
||||
}
|
||||
this.dispatchEvent(new SelectionEvent(logEntries));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -4,6 +4,11 @@ found in the LICENSE file. -->
|
||||
|
||||
<head>
|
||||
<link href="./index.css" rel="stylesheet">
|
||||
<style>
|
||||
.panel {
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<div class="panel">
|
||||
<h2>Timeline Panel</h2>
|
||||
|
@ -63,36 +63,40 @@ found in the LICENSE file. -->
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
#legend {
|
||||
.legend {
|
||||
position: relative;
|
||||
float: right;
|
||||
width: 100%;
|
||||
max-width: 280px;
|
||||
padding-left: 20px;
|
||||
padding-top: 10px;
|
||||
height: calc(200px + 12px);
|
||||
overflow-y: scroll;
|
||||
margin-right: -10px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
#legendTable {
|
||||
width: 280px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
width: 200px;
|
||||
text-align: left;
|
||||
padding-bottom: 3px;
|
||||
padding: 1px 3px 2px 3px;
|
||||
}
|
||||
|
||||
/* right align numbers */
|
||||
#legend td:nth-of-type(4n+3),
|
||||
#legend td:nth-of-type(4n+4) {
|
||||
text-align: right;
|
||||
}
|
||||
/* Center colors */
|
||||
#legend td:nth-of-type(4n+1) {
|
||||
text-align: center;;
|
||||
#legendTable td:nth-of-type(4n+1) {
|
||||
text-align: center;
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
.legendTypeColumn {
|
||||
/* Left align text*/
|
||||
#legendTable td:nth-of-type(4n+2) {
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
/* right align numbers */
|
||||
#legendTable td:nth-of-type(4n+3),
|
||||
#legendTable td:nth-of-type(4n+4) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.timeline {
|
||||
background-color: var(--timeline-background-color);
|
||||
@ -120,17 +124,18 @@ found in the LICENSE file. -->
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
<table id="legend" class="typeStatsTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Type</td>
|
||||
<td>Count</td>
|
||||
<td>Percent</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
<div class="legend">
|
||||
<table id="legendTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Type</td>
|
||||
<td>Count</td>
|
||||
<td>Percent</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="timeline">
|
||||
<div id="selection">
|
||||
<div id="leftHandle"></div>
|
||||
|
@ -25,7 +25,7 @@ DOM.defineCustomElement('view/timeline/timeline-track',
|
||||
constructor() {
|
||||
super(templateText);
|
||||
this._selectionHandler = new SelectionHandler(this);
|
||||
this._legend = new Legend(this.$('#legend'));
|
||||
this._legend = new Legend(this.$('#legendTable'));
|
||||
this._legend.onFilter = (type) => this._handleFilterTimeline();
|
||||
this.timelineNode.addEventListener(
|
||||
'scroll', e => this._handleTimelineScroll(e));
|
||||
|
Loading…
Reference in New Issue
Block a user