e7357f1902
Indicium is a new tool that integrates all our Map and IC processing tools into one tool. This CL does not attempt to cleanly integrate the Map Processor and IC explorer, but provides an in initial starting point for further integration work. Bug: v8:10644 Change-Id: I753c116fd409c8c07613bf15f22e14aa1e8c8a0e Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2259935 Commit-Queue: Sathya Gunasekaran <gsathya@chromium.org> Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org> Reviewed-by: Camillo Bruni <cbruni@chromium.org> Cr-Commit-Position: refs/heads/master@{#68605}
248 lines
6.9 KiB
JavaScript
248 lines
6.9 KiB
JavaScript
// 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.
|
|
|
|
/**
|
|
* 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 {
|
|
constructor() {
|
|
super();
|
|
let propertyICParser = [
|
|
parseInt, parseInt, parseInt, parseString, parseString, parseInt,
|
|
parseString, parseString, parseString
|
|
];
|
|
LogReader.call(this, {
|
|
'code-creation': {
|
|
parsers: [
|
|
parseString, parseInt, parseInt, parseInt, parseInt, parseString,
|
|
parseVarArgs
|
|
],
|
|
processor: this.processCodeCreation
|
|
},
|
|
'code-move':
|
|
{parsers: [parseInt, parseInt], processor: this.processCodeMove},
|
|
'code-delete': {parsers: [parseInt], processor: this.processCodeDelete},
|
|
'sfi-move':
|
|
{parsers: [parseInt, parseInt], processor: this.processFunctionMove},
|
|
'LoadGlobalIC': {
|
|
parsers: propertyICParser,
|
|
processor: this.processPropertyIC.bind(this, 'LoadGlobalIC')
|
|
},
|
|
'StoreGlobalIC': {
|
|
parsers: propertyICParser,
|
|
processor: this.processPropertyIC.bind(this, 'StoreGlobalIC')
|
|
},
|
|
'LoadIC': {
|
|
parsers: propertyICParser,
|
|
processor: this.processPropertyIC.bind(this, 'LoadIC')
|
|
},
|
|
'StoreIC': {
|
|
parsers: propertyICParser,
|
|
processor: this.processPropertyIC.bind(this, 'StoreIC')
|
|
},
|
|
'KeyedLoadIC': {
|
|
parsers: propertyICParser,
|
|
processor: this.processPropertyIC.bind(this, 'KeyedLoadIC')
|
|
},
|
|
'KeyedStoreIC': {
|
|
parsers: propertyICParser,
|
|
processor: this.processPropertyIC.bind(this, 'KeyedStoreIC')
|
|
},
|
|
'StoreInArrayLiteralIC': {
|
|
parsers: 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;
|
|
}
|
|
/**
|
|
* @override
|
|
*/
|
|
printError(str) {
|
|
print(str);
|
|
}
|
|
processString(string) {
|
|
let end = string.length;
|
|
let current = 0;
|
|
let next = 0;
|
|
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);
|
|
}
|
|
}
|
|
processLogFile(fileName) {
|
|
this.collectEntries = true;
|
|
this.lastLogFileName_ = fileName;
|
|
let line;
|
|
while (line = readline()) {
|
|
this.processLogLine(line);
|
|
}
|
|
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);
|
|
}
|
|
addEntry(entry) {
|
|
this.entries.push(entry);
|
|
}
|
|
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]);
|
|
this.profile_.addFuncCode(
|
|
type, name, timestamp, start, size, funcAddr, state);
|
|
} else {
|
|
this.profile_.addCode(type, name, timestamp, start, size);
|
|
}
|
|
}
|
|
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();
|
|
let re = /(.*):[0-9]+:[0-9]+$/;
|
|
let array = re.exec(name);
|
|
if (!array) return name;
|
|
return entry.getState() + array[1];
|
|
}
|
|
// TODO(zc): Process the IC event togather with time
|
|
processPropertyIC(
|
|
type, pc, 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 : ''));
|
|
}
|
|
}
|
|
|
|
// ================
|
|
let entries = [];
|
|
|
|
let properties = [
|
|
'type',
|
|
'category',
|
|
'functionName',
|
|
'filePosition',
|
|
'state',
|
|
'key',
|
|
'map',
|
|
'reason',
|
|
'file',
|
|
];
|
|
class CustomIcProcessor extends IcProcessor {
|
|
constructor() {
|
|
super();
|
|
this.entries = [];
|
|
}
|
|
|
|
functionName(pc) {
|
|
let entry = this.profile_.findEntry(pc);
|
|
return this.formatName(entry);
|
|
}
|
|
|
|
processPropertyIC(
|
|
type, pc, line, column, old_state, new_state, map, key, modifier,
|
|
slow_reason) {
|
|
let fnName = this.functionName(pc);
|
|
this.entries.push(new Entry(
|
|
type, fnName, line, column, key, old_state, new_state, map,
|
|
slow_reason));
|
|
}
|
|
};
|
|
|
|
class Entry {
|
|
constructor(
|
|
type, fn_file, line, column, key, oldState, newState, map, reason,
|
|
additional) {
|
|
this.type = type;
|
|
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.toString(16);
|
|
this.reason = reason;
|
|
this.additional = additional;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|