Commit 78b04f24 authored by Zeynep Cankara's avatar Zeynep Cankara Committed by Commit Bot

[tools][system-analyzer] Unify map-processor and ic-processor

This CL unifies the map-processor and ic-processor into a
single log processing pipeline. Unified processing pipeline
prevents doing 2 pass over the data.

Bug: v8:10644

Change-Id: Ic0221a9bb32901f43202390b98fded4830a50f70
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2385496
Commit-Queue: Zeynep Cankara <zcankara@google.com>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69676}
parent d4cf7d1f
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import CustomIcProcessor from "./ic-processor.mjs";
import Processor from "./processor.mjs";
// For compatibility with console scripts:
print = console.log;
......@@ -24,8 +24,8 @@ export class Group {
createSubGroups() {
this.groups = {};
for (let i = 0; i < CustomIcProcessor.kProperties.length; i++) {
let subProperty = CustomIcProcessor.kProperties[i];
for (let i = 0; i < Processor.kProperties.length; i++) {
let subProperty = Processor.kProperties[i];
if (this.property == subProperty) continue;
this.groups[subProperty] = Group.groupBy(this.entries, subProperty);
}
......
......@@ -3,9 +3,9 @@
// found in the LICENSE file.
import { Group } from './ic-model.mjs';
import CustomIcProcessor from "./ic-processor.mjs";
import { MapLogEvent } from "./map-processor.mjs";
import { SourcePositionLogEvent } from './event.mjs';
import Processor from "./processor.mjs";
import { SourcePositionLogEvent } from "./log/sourcePosition.mjs";
import { MapLogEvent } from "./log/map.mjs";
import { FocusEvent, SelectTimeEvent, SelectionEvent } from './events.mjs';
import { defineCustomElement, V8CustomElement } from './helper.mjs';
......@@ -15,18 +15,16 @@ defineCustomElement('ic-panel', (templateText) =>
#timeline;
constructor() {
super(templateText);
this.initGroupKeySelect();
this.groupKey.addEventListener(
'change', e => this.updateTable(e));
this.$('#filterICTimeBtn').addEventListener(
'click', e => this.handleICTimeFilter(e));
}
get timeline() {
return this.#timeline;
}
set timeline(value) {
console.assert(value !== undefined, "timeline undefined!");
this.#timeline = value;
this.selectedLogEvents = this.timeline.all;
this.selectedLogEvents = this.#timeline.all;
this.updateCount();
}
get groupKey() {
......@@ -121,6 +119,7 @@ defineCustomElement('ic-panel', (templateText) =>
return Array.from(selectedMapLogEventsSet);
}
//TODO(zcankara) Handle in the processor for events with source positions.
handleFilePositionClick(e) {
const entry = e.target.parentNode.entry;
const filePosition =
......@@ -221,9 +220,9 @@ defineCustomElement('ic-panel', (templateText) =>
initGroupKeySelect() {
let select = this.groupKey;
select.options.length = 0;
for (let i in CustomIcProcessor.kProperties) {
for (let i in Processor.kProperties) {
let option = document.createElement("option");
option.text = CustomIcProcessor.kProperties[i];
option.text = Processor.kProperties[i];
select.add(option);
}
}
......
......@@ -2,13 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import CustomIcProcessor from "./ic-processor.mjs";
import { SelectionEvent, FocusEvent } from "./events.mjs";
import { IcLogEvent } from "./ic-processor.mjs";
import { SelectionEvent, FocusEvent, SelectTimeEvent } from "./events.mjs";
import { State } from "./app-model.mjs";
import { MapProcessor, MapLogEvent } from "./map-processor.mjs";
import { SelectTimeEvent } from "./events.mjs";
import { SourcePositionLogEvent } from "./event.mjs";
import { SourcePositionLogEvent } from "./log/sourcePosition.mjs";
import { MapLogEvent } from "./log/map.mjs";
import { IcLogEvent } from "./log/ic.mjs";
import Processor from "./processor.mjs";
import { $ } from "./helper.mjs";
import "./ic-panel.mjs";
import "./timeline-panel.mjs";
......@@ -121,22 +120,11 @@ class App {
this.#state = new State();
this.#navigation = new Navigation(this.#state, this.#view);
}
// Map event log processing
handleLoadTextMapProcessor(text) {
let mapProcessor = new MapProcessor();
return mapProcessor.processString(text);
}
// IC event file reading and log processing
loadICLogFile(fileData) {
let reader = new FileReader();
reader.onload = (evt) => {
let icProcessor = new CustomIcProcessor();
this.#state.icTimeline = icProcessor.processString(fileData.chunk);
this.#view.icPanel.timeline = this.#state.icTimeline;
this.#view.icTrack.data = this.#state.icTimeline;
};
reader.readAsText(fileData.file);
this.#view.icPanel.initGroupKeySelect();
// Event log processing
handleLoadTextProcessor(text) {
let logProcessor = new Processor();
logProcessor.processString(text);
return logProcessor;
}
// call when a new file uploaded
......@@ -146,17 +134,25 @@ class App {
// instantiate the app logic
let fileData = e.detail;
try {
const timeline = this.handleLoadTextMapProcessor(fileData.chunk);
const processor = this.handleLoadTextProcessor(fileData.chunk);
const mapTimeline = processor.mapTimeline;
const icTimeline = processor.icTimeline;
//TODO(zcankara) Make sure only one instance of src event map ic id match
// Load map log events timeline.
this.#state.mapTimeline = mapTimeline;
// Transitions must be set before timeline for stats panel.
this.#state.mapTimeline = timeline;
this.#view.mapPanel.transitions = this.#state.mapTimeline.transitions;
this.#view.mapTrack.data = this.#state.mapTimeline;
this.#view.mapTrack.data = mapTimeline;
this.#state.chunks = this.#view.mapTrack.chunks;
this.#view.mapPanel.timeline = this.#state.mapTimeline;
this.#view.mapPanel.timeline = mapTimeline;
// Load ic log events timeline.
this.#state.icTimeline = icTimeline;
this.#view.icPanel.timeline = icTimeline;
this.#view.icTrack.data = icTimeline;
// TODO(zcankara) Load source position log events timeline.
} catch (error) {
console.log(error);
}
this.loadICLogFile(fileData);
this.fileLoaded = true;
}
......
// 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 { Event } from './log.mjs';
class IcLogEvent extends Event {
constructor(
type, fn_file, time, line, column, key, oldState, newState, map, reason,
script, additional) {
super(type, time);
this.category = 'other';
if (this.type.indexOf('Store') !== -1) {
this.category = 'Store';
} else if (this.type.indexOf('Load') !== -1) {
this.category = 'Load';
}
let parts = fn_file.split(' ');
this.functionName = parts[0];
this.file = parts[1];
let position = line + ':' + column;
this.filePosition = this.file + ':' + position;
this.oldState = oldState;
this.newState = newState;
this.state = this.oldState + ' → ' + this.newState;
this.key = key;
this.map = map;
this.reason = reason;
this.additional = additional;
this.script = script;
}
parseMapProperties(parts, offset) {
let next = parts[++offset];
if (!next.startsWith('dict')) return offset;
this.propertiesMode = next.substr(5) == '0' ? 'fast' : 'slow';
this.numberOfOwnProperties = parts[++offset].substr(4);
next = parts[++offset];
this.instanceType = next.substr(5, next.length - 6);
return offset;
}
parsePositionAndFile(parts, start) {
// find the position of 'at' in the parts array.
let offset = start;
for (let i = start + 1; i < parts.length; i++) {
offset++;
if (parts[i] == 'at') break;
}
if (parts[offset] !== 'at') return -1;
this.position = parts.slice(start, offset).join(' ');
offset += 1;
this.isNative = parts[offset] == 'native'
offset += this.isNative ? 1 : 0;
this.file = parts[offset];
return offset;
}
}
export { IcLogEvent };
......@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
class Event {
#time;
#type;
......@@ -18,14 +19,4 @@ class Event {
}
}
class SourcePositionLogEvent extends Event {
constructor(type, time, file, line, col, script) {
super(type, time);
this.file = file;
this.line = line;
this.col = col;
this.script = script;
}
}
export { Event, SourcePositionLogEvent };
export { Event };
......@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { typeToColor } from './helper.mjs';
import { Timeline } from './timeline.mjs';
import { typeToColor } from '../helper.mjs';
import { Event } from './log.mjs';
// ===========================================================================
import { Event } from './event.mjs';
// Map Log Events
const kChunkHeight = 250;
const kChunkWidth = 10;
......@@ -29,254 +30,9 @@ define(Array.prototype, 'first', function () {
define(Array.prototype, 'last', function () {
return this[this.length - 1]
});
// ===========================================================================
class MapProcessor extends LogReader {
#profile = new Profile();
#timeline = new Timeline();
#formatPCRegexp = /(.*):[0-9]+:[0-9]+$/;
MAJOR_VERSION = 7;
MINOR_VERSION = 6;
constructor() {
super();
this.dispatchTable_ = {
__proto__: null,
'code-creation': {
parsers: [
parseString, parseInt, parseInt, parseInt, parseInt, parseString,
parseVarArgs
],
processor: this.processCodeCreation
},
'v8-version': {
parsers: [
parseInt, parseInt,
],
processor: this.processV8Version
},
'script-source': {
parsers: [parseInt, parseString, parseString],
processor: this.processScriptSource
},
'code-move': {
parsers: [parseInt, parseInt],
'sfi-move':
{ parsers: [parseInt, parseInt], processor: this.processCodeMove },
'code-delete': {
parsers: [parseInt],
processor: this.processCodeDelete
},
processor: this.processFunctionMove
},
'map-create':
{ parsers: [parseInt, parseString], processor: this.processMapCreate },
'map': {
parsers: [
parseString, parseInt, parseString, parseString, parseInt, parseInt,
parseString, parseString, parseString
],
processor: this.processMap
},
'map-details': {
parsers: [parseInt, parseString, parseString],
processor: this.processMapDetails
}
};
}
printError(str) {
console.error(str);
throw str
}
processString(string) {
let end = string.length;
let current = 0;
let next = 0;
let line;
let i = 0;
let entry;
try {
while (current < end) {
next = string.indexOf('\n', current);
if (next === -1) break;
i++;
line = string.substring(current, next);
current = next + 1;
this.processLogLine(line);
}
} catch (e) {
console.error('Error occurred during parsing, trying to continue: ' + e);
}
return this.finalize();
}
processLogFile(fileName) {
this.collectEntries = true;
this.lastLogFileName_ = fileName;
let i = 1;
let line;
try {
while (line = readline()) {
this.processLogLine(line);
i++;
}
} catch (e) {
console.error(
'Error occurred during parsing line ' + i +
', trying to continue: ' + e);
}
return this.finalize();
}
finalize() {
// TODO(cbruni): print stats;
this.#timeline.transitions = new Map();
let id = 0;
this.#timeline.forEach(map => {
if (map.isRoot()) id = map.finalizeRootMap(id + 1);
if (map.edge && map.edge.name) {
let edge = map.edge;
let list = this.#timeline.transitions.get(edge.name);
if (list === undefined) {
this.#timeline.transitions.set(edge.name, [edge]);
} else {
list.push(edge);
}
}
});
return this.#timeline;
}
addEntry(entry) {
this.entries.push(entry);
}
/**
* Parser for dynamic code optimization state.
*/
parseState(s) {
switch (s) {
case '':
return Profile.CodeState.COMPILED;
case '~':
return Profile.CodeState.OPTIMIZABLE;
case '*':
return Profile.CodeState.OPTIMIZED;
}
throw new Error('unknown code state: ' + s);
}
processCodeCreation(type, kind, timestamp, start, size, name, maybe_func) {
if (maybe_func.length) {
let funcAddr = parseInt(maybe_func[0]);
let state = this.parseState(maybe_func[1]);
this.#profile.addFuncCode(
type, name, timestamp, start, size, funcAddr, state);
} else {
this.#profile.addCode(type, name, timestamp, start, size);
}
}
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.`);
}
}
processScriptSource(scriptId, url, source) {
this.#profile.addScriptSource(scriptId, url, source);
}
processCodeMove(from, to) {
this.#profile.moveCode(from, to);
}
processCodeDelete(start) {
this.#profile.deleteCode(start);
}
processFunctionMove(from, to) {
this.#profile.moveFunc(from, to);
}
formatPC(pc, line, column) {
let entry = this.#profile.findEntry(pc);
if (!entry) return '<unknown>'
if (entry.type === 'Builtin') {
return entry.name;
}
let name = entry.func.getName();
let array = this.#formatPCRegexp.exec(name);
if (array === null) {
entry = name;
} else {
entry = entry.getState() + array[1];
}
return entry + ':' + line + ':' + column;
}
processFileName(filePositionLine) {
if (!(/\s/.test(filePositionLine))) return;
filePositionLine = filePositionLine.split(' ');
let file = filePositionLine[1].split(':')[0];
return file;
}
processMap(type, time, from, to, pc, line, column, reason, name) {
let time_ = parseInt(time);
if (type === 'Deprecate') return this.deprecateMap(type, time_, from);
let from_ = this.getExistingMap(from, time_);
let to_ = this.getExistingMap(to, time_);
let edge = new Edge(type, name, reason, time, from_, to_);
to_.filePosition = this.formatPC(pc, line, column);
let fileName = this.processFileName(to_.filePosition);
to_.script = this.getScript(fileName);
edge.finishSetup();
}
deprecateMap(type, time, id) {
this.getExistingMap(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.
let map = this.createMap(id, time);
}
processMapDetails(time, id, string) {
// TODO(cbruni): fix initial map logging.
let map = this.getExistingMap(id, time);
map.description = string;
}
createMap(id, time) {
let map = new MapLogEvent(id, time);
this.#timeline.push(map);
return map;
}
getExistingMap(id, time) {
if (id === '0x000000000000') return undefined;
let map = MapLogEvent.get(id, time);
if (map === undefined) {
console.error('No map details provided: id=' + id);
// Manually patch in a map to continue running.
return this.createMap(id, time);
};
return map;
}
getScript(url) {
return this.#profile.getScript(url);
}
}
// ===========================================================================
// Map Log Events
class MapLogEvent extends Event {
edge = void 0;
......@@ -356,7 +112,6 @@ class MapLogEvent extends Event {
return -1;
}
//TODO(zcankara) Add tests for the chunk positions
position(chunks) {
let index = this.chunkIndex(chunks);
let xFrom = (index + 1.5) * kChunkWidth;
......@@ -538,25 +293,4 @@ class Edge {
}
// ===========================================================================
class ArgumentsProcessor extends BaseArgumentsProcessor {
getArgsDispatch() {
return {
'--range':
['range', 'auto,auto', 'Specify the range limit as [start],[end]'],
'--source-map': [
'sourceMap', null,
'Specify the source map that should be used for output'
]
};
}
getDefaultResults() {
return {
logFileName: 'v8.log',
range: 'auto,auto',
};
}
}
export { MapProcessor, MapLogEvent, kChunkWidth, kChunkHeight };
export { MapLogEvent, Edge, kChunkWidth, kChunkHeight };
// 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 { Event } from './log.mjs';
class SourcePositionLogEvent extends Event {
constructor(type, time, file, line, col, script) {
super(type, time);
this.file = file;
this.line = line;
this.col = col;
this.script = script;
}
}
export { SourcePositionLogEvent };
......@@ -5,7 +5,7 @@ import "./stats-panel.mjs";
import "./map-panel/map-details.mjs";
import "./map-panel/map-transitions.mjs";
import { FocusEvent } from './events.mjs';
import { MapLogEvent } from "./map-processor.mjs";
import { MapLogEvent } from "./log/map.mjs";
import { defineCustomElement, V8CustomElement } from './helper.mjs';
defineCustomElement('map-panel', (templateText) =>
......@@ -50,9 +50,6 @@ defineCustomElement('map-panel', (templateText) =>
}
// send a timeline to the stats-panel
get timeline() {
return this.statsPanel.timeline;
}
set timeline(value) {
console.assert(value !== undefined, "timeline undefined!");
this.statsPanel.timeline = value;
......
......@@ -3,7 +3,7 @@
// found in the LICENSE file.
import { V8CustomElement, defineCustomElement } from "../helper.mjs";
import { FocusEvent } from "../events.mjs";
import { SourcePositionLogEvent } from '../event.mjs';
import { SourcePositionLogEvent } from "../log/sourcePosition.mjs";
defineCustomElement(
"./map-panel/map-details",
......
......@@ -2,35 +2,29 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { Event } from './event.mjs';
import { MapLogEvent, Edge } from "./log/map.mjs";
import { IcLogEvent } from "./log/ic.mjs";
import { Timeline } from './timeline.mjs';
/**
* Parser for dynamic code optimization state.
*/
function parseState(s) {
switch (s) {
case '':
return Profile.CodeState.COMPILED;
case '~':
return Profile.CodeState.OPTIMIZABLE;
case '*':
return Profile.CodeState.OPTIMIZED;
}
throw new Error('unknown code state: ' + s);
}
class IcProcessor extends LogReader {
#profile;
MAJOR_VERSION = 8;
MINOR_VERSION = 5;
// ===========================================================================
class Processor extends LogReader {
#profile = new Profile();
#mapTimeline = new Timeline();
#icTimeline = new Timeline();
#formatPCRegexp = /(.*):[0-9]+:[0-9]+$/;
MAJOR_VERSION = 7;
MINOR_VERSION = 6;
constructor() {
super();
let propertyICParser = [
this.propertyICParser = [
parseInt, parseInt, parseInt, parseInt, parseString, parseString,
parseString, parseString, parseString, parseString
];
LogReader.call(this, {
this.dispatchTable_ = {
__proto__: null,
'code-creation': {
parsers: [
parseString, parseInt, parseInt, parseInt, parseInt, parseString,
......@@ -53,54 +47,55 @@ class IcProcessor extends LogReader {
'code-delete': { parsers: [parseInt], processor: this.processCodeDelete },
'sfi-move':
{ parsers: [parseInt, parseInt], processor: this.processFunctionMove },
'map-create':
{ parsers: [parseInt, parseString], processor: this.processMapCreate },
'map': {
parsers: [
parseString, parseInt, parseString, parseString, parseInt, parseInt,
parseString, parseString, parseString
],
processor: this.processMap
},
'map-details': {
parsers: [parseInt, parseString, parseString],
processor: this.processMapDetails
},
'LoadGlobalIC': {
parsers: propertyICParser,
parsers: this.propertyICParser,
processor: this.processPropertyIC.bind(this, 'LoadGlobalIC')
},
'StoreGlobalIC': {
parsers: propertyICParser,
parsers: this.propertyICParser,
processor: this.processPropertyIC.bind(this, 'StoreGlobalIC')
},
'LoadIC': {
parsers: propertyICParser,
parsers: this.propertyICParser,
processor: this.processPropertyIC.bind(this, 'LoadIC')
},
'StoreIC': {
parsers: propertyICParser,
parsers: this.propertyICParser,
processor: this.processPropertyIC.bind(this, 'StoreIC')
},
'KeyedLoadIC': {
parsers: propertyICParser,
parsers: this.propertyICParser,
processor: this.processPropertyIC.bind(this, 'KeyedLoadIC')
},
'KeyedStoreIC': {
parsers: propertyICParser,
parsers: this.propertyICParser,
processor: this.processPropertyIC.bind(this, 'KeyedStoreIC')
},
'StoreInArrayLiteralIC': {
parsers: propertyICParser,
parsers: this.propertyICParser,
processor: this.processPropertyIC.bind(this, 'StoreInArrayLiteralIC')
},
});
this.#profile = new Profile();
this.LoadGlobalIC = 0;
this.StoreGlobalIC = 0;
this.LoadIC = 0;
this.StoreIC = 0;
this.KeyedLoadIC = 0;
this.KeyedStoreIC = 0;
this.StoreInArrayLiteralIC = 0;
};
}
get profile() {
return this.#profile;
}
/**
* @override
*/
printError(str) {
print(str);
console.error(str);
throw str
}
processString(string) {
let end = string.length;
let current = 0;
......@@ -108,66 +103,114 @@ class IcProcessor extends LogReader {
let line;
let i = 0;
let entry;
while (current < end) {
next = string.indexOf('\n', current);
if (next === -1) break;
i++;
line = string.substring(current, next);
current = next + 1;
this.processLogLine(line);
try {
while (current < end) {
next = string.indexOf('\n', current);
if (next === -1) break;
i++;
line = string.substring(current, next);
current = next + 1;
this.processLogLine(line);
}
} catch (e) {
console.error('Error occurred during parsing, trying to continue: ' + e);
}
this.finalize();
}
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.`);
}
}
processScriptSource(scriptId, url, script) {
this.#profile.addScriptSource(scriptId, url, script);
}
processLogFile(fileName) {
this.collectEntries = true;
this.lastLogFileName_ = fileName;
let i = 1;
let line;
while (line = readline()) {
this.processLogLine(line);
try {
while (line = readline()) {
this.processLogLine(line);
i++;
}
} catch (e) {
console.error(
'Error occurred during parsing line ' + i +
', trying to continue: ' + e);
}
print();
print('=====================');
print('LoadGlobal: ' + this.LoadGlobalIC);
print('StoreGlobal: ' + this.StoreGlobalIC);
print('Load: ' + this.LoadIC);
print('Store: ' + this.StoreIC);
print('KeyedLoad: ' + this.KeyedLoadIC);
print('KeyedStore: ' + this.KeyedStoreIC);
print('StoreInArrayLiteral: ' + this.StoreInArrayLiteralIC);
this.finalize();
}
finalize() {
// TODO(cbruni): print stats;
this.#mapTimeline.transitions = new Map();
let id = 0;
this.#mapTimeline.forEach(map => {
if (map.isRoot()) id = map.finalizeRootMap(id + 1);
if (map.edge && map.edge.name) {
let edge = map.edge;
let list = this.#mapTimeline.transitions.get(edge.name);
if (list === undefined) {
this.#mapTimeline.transitions.set(edge.name, [edge]);
} else {
list.push(edge);
}
}
});
}
addEntry(entry) {
this.entries.push(entry);
}
/**
* Parser for dynamic code optimization state.
*/
parseState(s) {
switch (s) {
case '':
return Profile.CodeState.COMPILED;
case '~':
return Profile.CodeState.OPTIMIZABLE;
case '*':
return Profile.CodeState.OPTIMIZED;
}
throw new Error('unknown code state: ' + s);
}
processCodeCreation(type, kind, timestamp, start, size, name, maybe_func) {
if (maybe_func.length) {
let funcAddr = parseInt(maybe_func[0]);
let state = parseState(maybe_func[1]);
let state = this.parseState(maybe_func[1]);
this.#profile.addFuncCode(
type, name, timestamp, start, size, funcAddr, state);
} else {
this.#profile.addCode(type, name, timestamp, start, size);
}
}
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.`);
}
}
processScriptSource(scriptId, url, source) {
this.#profile.addScriptSource(scriptId, url, source);
}
processCodeMove(from, to) {
this.#profile.moveCode(from, to);
}
processCodeDelete(start) {
this.#profile.deleteCode(start);
}
processFunctionMove(from, to) {
this.#profile.moveFunc(from, to);
}
formatName(entry) {
if (!entry) return '<unknown>';
let name = entry.func.getName();
......@@ -177,46 +220,6 @@ class IcProcessor extends LogReader {
return entry.getState() + array[1];
}
processPropertyIC(
type, pc, time, line, column, old_state, new_state, map, name, modifier,
slow_reason) {
this[type]++;
let entry = this.#profile.findEntry(pc);
print(
type + ' (' + old_state + '->' + new_state + modifier + ') at ' +
this.formatName(entry) + ':' + line + ':' + column + ' ' + name +
' (map 0x' + map.toString(16) + ')' +
(slow_reason ? ' ' + slow_reason : '') + 'time: ' + time);
}
getScript(url) {
return this.#profile.getScript(url);
}
}
// ================
IcProcessor.kProperties = [
'type',
'category',
'functionName',
'filePosition',
'state',
'key',
'map',
'reason',
'file'
];
class CustomIcProcessor extends IcProcessor {
#timeline = new Timeline();
functionName(pc) {
let entry = this.profile.findEntry(pc);
return this.formatName(entry);
}
processPropertyIC(
type, pc, time, line, column, old_state, new_state, map, key, modifier,
slow_reason) {
......@@ -227,72 +230,109 @@ class CustomIcProcessor extends IcProcessor {
let entry = new IcLogEvent(
type, fnName, time, line, column, key, old_state, new_state, map,
slow_reason, script);
this.#timeline.push(entry);
//TODO(zcankara) Process sourcePosition
this.#icTimeline.push(entry);
}
functionName(pc) {
let entry = this.#profile.findEntry(pc);
return this.formatName(entry);
}
formatPC(pc, line, column) {
let entry = this.#profile.findEntry(pc);
if (!entry) return '<unknown>'
if (entry.type === 'Builtin') {
return entry.name;
}
let name = entry.func.getName();
let array = this.#formatPCRegexp.exec(name);
if (array === null) {
entry = name;
} else {
entry = entry.getState() + array[1];
}
return entry + ':' + line + ':' + column;
}
get timeline() {
return this.#timeline;
processFileName(filePositionLine) {
if (!(/\s/.test(filePositionLine))) return;
filePositionLine = filePositionLine.split(' ');
let file = filePositionLine[1].split(':')[0];
return file;
}
processString(string) {
super.processString(string);
return this.timeline;
processMap(type, time, from, to, pc, line, column, reason, name) {
let time_ = parseInt(time);
if (type === 'Deprecate') return this.deprecateMap(type, time_, from);
let from_ = this.getExistingMap(from, time_);
let to_ = this.getExistingMap(to, time_);
//TODO(zcankara) Process sourcePosition
let edge = new Edge(type, name, reason, time, from_, to_);
to_.filePosition = this.formatPC(pc, line, column);
let fileName = this.processFileName(to_.filePosition);
to_.script = this.getScript(fileName);
edge.finishSetup();
}
};
class IcLogEvent extends Event {
constructor(
type, fn_file, time, line, column, key, oldState, newState, map, reason,
script, additional) {
super(type, time);
this.category = 'other';
if (this.type.indexOf('Store') !== -1) {
this.category = 'Store';
} else if (this.type.indexOf('Load') !== -1) {
this.category = 'Load';
}
let parts = fn_file.split(' ');
this.functionName = parts[0];
this.file = parts[1];
let position = line + ':' + column;
this.filePosition = this.file + ':' + position;
this.oldState = oldState;
this.newState = newState;
this.state = this.oldState + ' → ' + this.newState;
this.key = key;
this.map = map;
this.reason = reason;
this.additional = additional;
this.script = script;
deprecateMap(type, time, id) {
this.getExistingMap(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.
let map = this.createMap(id, time);
}
parseMapProperties(parts, offset) {
let next = parts[++offset];
if (!next.startsWith('dict')) return offset;
this.propertiesMode = next.substr(5) == '0' ? 'fast' : 'slow';
this.numberOfOwnProperties = parts[++offset].substr(4);
next = parts[++offset];
this.instanceType = next.substr(5, next.length - 6);
return offset;
processMapDetails(time, id, string) {
// TODO(cbruni): fix initial map logging.
let map = this.getExistingMap(id, time);
map.description = string;
}
parsePositionAndFile(parts, start) {
// find the position of 'at' in the parts array.
let offset = start;
for (let i = start + 1; i < parts.length; i++) {
offset++;
if (parts[i] == 'at') break;
}
if (parts[offset] !== 'at') return -1;
this.position = parts.slice(start, offset).join(' ');
offset += 1;
this.isNative = parts[offset] == 'native'
offset += this.isNative ? 1 : 0;
this.file = parts[offset];
return offset;
createMap(id, time) {
let map = new MapLogEvent(id, time);
//TODO(zcankara) Process sourcePosition
this.#mapTimeline.push(map);
return map;
}
getExistingMap(id, time) {
if (id === '0x000000000000') return undefined;
let map = MapLogEvent.get(id, time);
if (map === undefined) {
console.error('No map details provided: id=' + id);
// Manually patch in a map to continue running.
return this.createMap(id, time);
};
return map;
}
getScript(url) {
return this.#profile.getScript(url);
}
get icTimeline() {
return this.#icTimeline;
}
get mapTimeline() {
return this.#mapTimeline;
}
}
export { CustomIcProcessor as default, IcLogEvent };
Processor.kProperties = [
'type',
'category',
'functionName',
'filePosition',
'state',
'key',
'map',
'reason',
'file'
];
export { Processor as default };
......@@ -65,6 +65,7 @@ defineCustomElement(
let line = sourcePosition.line;
let col = sourcePosition.col;
let script = sourcePosition.script;
if (!(line && col && script)) continue;
this.highlightSourcePosition(line, col, script);
}
}
......
......@@ -6,7 +6,7 @@ import {
defineCustomElement, V8CustomElement,
typeToColor, CSSColor
} from '../helper.mjs';
import { kChunkWidth, kChunkHeight } from '../map-processor.mjs';
import { kChunkWidth, kChunkHeight } from "../log/map.mjs";
import {
SelectionEvent, FocusEvent, SelectTimeEvent,
SynchronizeSelectionEvent
......@@ -318,7 +318,8 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
if (chunk.isEmpty()) continue;
let node = this.div();
node.className = 'chunk';
node.style.left = ((chunks[i].start - start) * this.#timeToPixel) + 'px';
node.style.left =
((chunks[i].start - start) * this.#timeToPixel) + 'px';
node.style.height = height + 'px';
node.chunk = chunk;
node.addEventListener('mousemove', e => this.handleChunkMouseMove(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