Commit a8eea879 authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[tools] Port more tools to ES6 classes

Convert Profile, CodeMap and their helpers to ES6 classes.
Code cleanup will happen in a separate step.

Bug: v8:10667
Change-Id: Icfb28f6d9ef7f00efba93b347fdf210a9af36a49
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2509591
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70969}
parent 73ed5430
...@@ -25,12 +25,7 @@ ...@@ -25,12 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import { CodeMap } from "../../../tools/codemap.mjs"; import { CodeMap, CodeEntry } from "../../../tools/codemap.mjs";
function newCodeEntry(size, name) {
return new CodeMap.CodeEntry(size, name);
};
function assertEntry(codeMap, expected_name, addr) { function assertEntry(codeMap, expected_name, addr) {
var entry = codeMap.findEntry(addr); var entry = codeMap.findEntry(addr);
...@@ -46,9 +41,9 @@ function assertNoEntry(codeMap, addr) { ...@@ -46,9 +41,9 @@ function assertNoEntry(codeMap, addr) {
(function testLibrariesAndStaticCode() { (function testLibrariesAndStaticCode() {
var codeMap = new CodeMap(); var codeMap = new CodeMap();
codeMap.addLibrary(0x1500, newCodeEntry(0x3000, 'lib1')); codeMap.addLibrary(0x1500, new CodeEntry(0x3000, 'lib1'));
codeMap.addLibrary(0x15500, newCodeEntry(0x5000, 'lib2')); codeMap.addLibrary(0x15500, new CodeEntry(0x5000, 'lib2'));
codeMap.addLibrary(0x155500, newCodeEntry(0x10000, 'lib3')); codeMap.addLibrary(0x155500, new CodeEntry(0x10000, 'lib3'));
assertNoEntry(codeMap, 0); assertNoEntry(codeMap, 0);
assertNoEntry(codeMap, 0x1500 - 1); assertNoEntry(codeMap, 0x1500 - 1);
assertEntry(codeMap, 'lib1', 0x1500); assertEntry(codeMap, 'lib1', 0x1500);
...@@ -70,9 +65,9 @@ function assertNoEntry(codeMap, addr) { ...@@ -70,9 +65,9 @@ function assertNoEntry(codeMap, addr) {
assertNoEntry(codeMap, 0x155500 + 0x10000); assertNoEntry(codeMap, 0x155500 + 0x10000);
assertNoEntry(codeMap, 0xFFFFFFFF); assertNoEntry(codeMap, 0xFFFFFFFF);
codeMap.addStaticCode(0x1510, newCodeEntry(0x30, 'lib1-f1')); codeMap.addStaticCode(0x1510, new CodeEntry(0x30, 'lib1-f1'));
codeMap.addStaticCode(0x1600, newCodeEntry(0x50, 'lib1-f2')); codeMap.addStaticCode(0x1600, new CodeEntry(0x50, 'lib1-f2'));
codeMap.addStaticCode(0x15520, newCodeEntry(0x100, 'lib2-f1')); codeMap.addStaticCode(0x15520, new CodeEntry(0x100, 'lib2-f1'));
assertEntry(codeMap, 'lib1', 0x1500); assertEntry(codeMap, 'lib1', 0x1500);
assertEntry(codeMap, 'lib1', 0x1510 - 1); assertEntry(codeMap, 'lib1', 0x1510 - 1);
assertEntry(codeMap, 'lib1-f1', 0x1510); assertEntry(codeMap, 'lib1-f1', 0x1510);
...@@ -96,10 +91,10 @@ function assertNoEntry(codeMap, addr) { ...@@ -96,10 +91,10 @@ function assertNoEntry(codeMap, addr) {
(function testDynamicCode() { (function testDynamicCode() {
var codeMap = new CodeMap(); var codeMap = new CodeMap();
codeMap.addCode(0x1500, newCodeEntry(0x200, 'code1')); codeMap.addCode(0x1500, new CodeEntry(0x200, 'code1'));
codeMap.addCode(0x1700, newCodeEntry(0x100, 'code2')); codeMap.addCode(0x1700, new CodeEntry(0x100, 'code2'));
codeMap.addCode(0x1900, newCodeEntry(0x50, 'code3')); codeMap.addCode(0x1900, new CodeEntry(0x50, 'code3'));
codeMap.addCode(0x1950, newCodeEntry(0x10, 'code4')); codeMap.addCode(0x1950, new CodeEntry(0x10, 'code4'));
assertNoEntry(codeMap, 0); assertNoEntry(codeMap, 0);
assertNoEntry(codeMap, 0x1500 - 1); assertNoEntry(codeMap, 0x1500 - 1);
assertEntry(codeMap, 'code1', 0x1500); assertEntry(codeMap, 'code1', 0x1500);
...@@ -122,8 +117,8 @@ function assertNoEntry(codeMap, addr) { ...@@ -122,8 +117,8 @@ function assertNoEntry(codeMap, addr) {
(function testCodeMovesAndDeletions() { (function testCodeMovesAndDeletions() {
var codeMap = new CodeMap(); var codeMap = new CodeMap();
codeMap.addCode(0x1500, newCodeEntry(0x200, 'code1')); codeMap.addCode(0x1500, new CodeEntry(0x200, 'code1'));
codeMap.addCode(0x1700, newCodeEntry(0x100, 'code2')); codeMap.addCode(0x1700, new CodeEntry(0x100, 'code2'));
assertEntry(codeMap, 'code1', 0x1500); assertEntry(codeMap, 'code1', 0x1500);
assertEntry(codeMap, 'code2', 0x1700); assertEntry(codeMap, 'code2', 0x1700);
codeMap.moveCode(0x1500, 0x1800); codeMap.moveCode(0x1500, 0x1800);
...@@ -139,8 +134,8 @@ function assertNoEntry(codeMap, addr) { ...@@ -139,8 +134,8 @@ function assertNoEntry(codeMap, addr) {
(function testDynamicNamesDuplicates() { (function testDynamicNamesDuplicates() {
var codeMap = new CodeMap(); var codeMap = new CodeMap();
// Code entries with same names but different addresses. // Code entries with same names but different addresses.
codeMap.addCode(0x1500, newCodeEntry(0x200, 'code')); codeMap.addCode(0x1500, new CodeEntry(0x200, 'code'));
codeMap.addCode(0x1700, newCodeEntry(0x100, 'code')); codeMap.addCode(0x1700, new CodeEntry(0x100, 'code'));
assertEntry(codeMap, 'code', 0x1500); assertEntry(codeMap, 'code', 0x1500);
assertEntry(codeMap, 'code {1}', 0x1700); assertEntry(codeMap, 'code {1}', 0x1700);
// Test name stability. // Test name stability.
...@@ -151,9 +146,9 @@ function assertNoEntry(codeMap, addr) { ...@@ -151,9 +146,9 @@ function assertNoEntry(codeMap, addr) {
(function testStaticEntriesExport() { (function testStaticEntriesExport() {
var codeMap = new CodeMap(); var codeMap = new CodeMap();
codeMap.addStaticCode(0x1500, newCodeEntry(0x3000, 'lib1')); codeMap.addStaticCode(0x1500, new CodeEntry(0x3000, 'lib1'));
codeMap.addStaticCode(0x15500, newCodeEntry(0x5000, 'lib2')); codeMap.addStaticCode(0x15500, new CodeEntry(0x5000, 'lib2'));
codeMap.addStaticCode(0x155500, newCodeEntry(0x10000, 'lib3')); codeMap.addStaticCode(0x155500, new CodeEntry(0x10000, 'lib3'));
var allStatics = codeMap.getAllStaticEntries(); var allStatics = codeMap.getAllStaticEntries();
allStatics = allStatics.map(String); allStatics = allStatics.map(String);
allStatics.sort(); allStatics.sort();
...@@ -163,9 +158,9 @@ function assertNoEntry(codeMap, addr) { ...@@ -163,9 +158,9 @@ function assertNoEntry(codeMap, addr) {
(function testDynamicEntriesExport() { (function testDynamicEntriesExport() {
var codeMap = new CodeMap(); var codeMap = new CodeMap();
codeMap.addCode(0x1500, newCodeEntry(0x200, 'code1')); codeMap.addCode(0x1500, new CodeEntry(0x200, 'code1'));
codeMap.addCode(0x1700, newCodeEntry(0x100, 'code2')); codeMap.addCode(0x1700, new CodeEntry(0x100, 'code2'));
codeMap.addCode(0x1900, newCodeEntry(0x50, 'code3')); codeMap.addCode(0x1900, new CodeEntry(0x50, 'code3'));
var allDynamics = codeMap.getAllDynamicEntries(); var allDynamics = codeMap.getAllDynamicEntries();
allDynamics = allDynamics.map(String); allDynamics = allDynamics.map(String);
allDynamics.sort(); allDynamics.sort();
......
...@@ -32,252 +32,232 @@ import { SplayTree } from "./splaytree.mjs"; ...@@ -32,252 +32,232 @@ import { SplayTree } from "./splaytree.mjs";
* *
* @constructor * @constructor
*/ */
export function CodeMap() { export class CodeMap {
/** /**
* Dynamic code entries. Used for JIT compiled code. * Dynamic code entries. Used for JIT compiled code.
*/ */
this.dynamics_ = new SplayTree(); dynamics_ = new SplayTree();
/** /**
* Name generator for entries having duplicate names. * Name generator for entries having duplicate names.
*/ */
this.dynamicsNameGen_ = new CodeMap.NameGenerator(); dynamicsNameGen_ = new NameGenerator();
/** /**
* Static code entries. Used for statically compiled code. * Static code entries. Used for statically compiled code.
*/ */
this.statics_ = new SplayTree(); statics_ = new SplayTree();
/** /**
* Libraries entries. Used for the whole static code libraries. * Libraries entries. Used for the whole static code libraries.
*/ */
this.libraries_ = new SplayTree(); libraries_ = new SplayTree();
/** /**
* Map of memory pages occupied with static code. * Map of memory pages occupied with static code.
*/ */
this.pages_ = []; pages_ = [];
};
/** /**
* The number of alignment bits in a page address. * The number of alignment bits in a page address.
*/ */
CodeMap.PAGE_ALIGNMENT = 12; static PAGE_ALIGNMENT = 12;
/**
* Page size in bytes.
*/
CodeMap.PAGE_SIZE =
1 << CodeMap.PAGE_ALIGNMENT;
/**
* Adds a dynamic (i.e. moveable and discardable) code entry.
*
* @param {number} start The starting address.
* @param {CodeMap.CodeEntry} codeEntry Code entry object.
*/
CodeMap.prototype.addCode = function(start, codeEntry) {
this.deleteAllCoveredNodes_(this.dynamics_, start, start + codeEntry.size);
this.dynamics_.insert(start, codeEntry);
};
/** /**
* Moves a dynamic code entry. Throws an exception if there is no dynamic * Page size in bytes.
* code entry with the specified starting address. */
* static PAGE_SIZE = 1 << CodeMap.PAGE_ALIGNMENT;
* @param {number} from The starting address of the entry being moved.
* @param {number} to The destination address.
*/
CodeMap.prototype.moveCode = function(from, to) {
var removedNode = this.dynamics_.remove(from);
this.deleteAllCoveredNodes_(this.dynamics_, to, to + removedNode.value.size);
this.dynamics_.insert(to, removedNode.value);
};
/** /**
* Discards a dynamic code entry. Throws an exception if there is no dynamic * Adds a dynamic (i.e. moveable and discardable) code entry.
* code entry with the specified starting address. *
* * @param {number} start The starting address.
* @param {number} start The starting address of the entry being deleted. * @param {CodeMap.CodeEntry} codeEntry Code entry object.
*/ */
CodeMap.prototype.deleteCode = function(start) { addCode(start, codeEntry) {
var removedNode = this.dynamics_.remove(start); this.deleteAllCoveredNodes_(this.dynamics_, start, start + codeEntry.size);
}; this.dynamics_.insert(start, codeEntry);
}
/**
* Moves a dynamic code entry. Throws an exception if there is no dynamic
* code entry with the specified starting address.
*
* @param {number} from The starting address of the entry being moved.
* @param {number} to The destination address.
*/
moveCode(from, to) {
var removedNode = this.dynamics_.remove(from);
this.deleteAllCoveredNodes_(this.dynamics_, to, to + removedNode.value.size);
this.dynamics_.insert(to, removedNode.value);
}
/** /**
* Adds a library entry. * Discards a dynamic code entry. Throws an exception if there is no dynamic
* * code entry with the specified starting address.
* @param {number} start The starting address. *
* @param {CodeMap.CodeEntry} codeEntry Code entry object. * @param {number} start The starting address of the entry being deleted.
*/ */
CodeMap.prototype.addLibrary = function( deleteCode(start) {
start, codeEntry) { var removedNode = this.dynamics_.remove(start);
this.markPages_(start, start + codeEntry.size); }
this.libraries_.insert(start, codeEntry);
};
/**
* Adds a library entry.
*
* @param {number} start The starting address.
* @param {CodeMap.CodeEntry} codeEntry Code entry object.
*/
addLibrary(start, codeEntry) {
this.markPages_(start, start + codeEntry.size);
this.libraries_.insert(start, codeEntry);
}
/** /**
* Adds a static code entry. * Adds a static code entry.
* *
* @param {number} start The starting address. * @param {number} start The starting address.
* @param {CodeMap.CodeEntry} codeEntry Code entry object. * @param {CodeMap.CodeEntry} codeEntry Code entry object.
*/ */
CodeMap.prototype.addStaticCode = function( addStaticCode(start, codeEntry) {
start, codeEntry) { this.statics_.insert(start, codeEntry);
this.statics_.insert(start, codeEntry); }
};
/**
* @private
*/
markPages_(start, end) {
for (var addr = start; addr <= end;
addr += CodeMap.PAGE_SIZE) {
this.pages_[(addr / CodeMap.PAGE_SIZE)|0] = 1;
}
}
/** /**
* @private * @private
*/ */
CodeMap.prototype.markPages_ = function(start, end) { deleteAllCoveredNodes_(tree, start, end) {
for (var addr = start; addr <= end; var to_delete = [];
addr += CodeMap.PAGE_SIZE) { var addr = end - 1;
this.pages_[(addr / CodeMap.PAGE_SIZE)|0] = 1; while (addr >= start) {
var node = tree.findGreatestLessThan(addr);
if (!node) break;
var start2 = node.key, end2 = start2 + node.value.size;
if (start2 < end && start < end2) to_delete.push(start2);
addr = start2 - 1;
}
for (var i = 0, l = to_delete.length; i < l; ++i) tree.remove(to_delete[i]);
} }
};
/**
* @private
*/
isAddressBelongsTo_(addr, node) {
return addr >= node.key && addr < (node.key + node.value.size);
}
/** /**
* @private * @private
*/ */
CodeMap.prototype.deleteAllCoveredNodes_ = function(tree, start, end) { findInTree_(tree, addr) {
var to_delete = [];
var addr = end - 1;
while (addr >= start) {
var node = tree.findGreatestLessThan(addr); var node = tree.findGreatestLessThan(addr);
if (!node) break; return node && this.isAddressBelongsTo_(addr, node) ? node : null;
var start2 = node.key, end2 = start2 + node.value.size;
if (start2 < end && start < end2) to_delete.push(start2);
addr = start2 - 1;
} }
for (var i = 0, l = to_delete.length; i < l; ++i) tree.remove(to_delete[i]);
};
/**
* @private
*/
CodeMap.prototype.isAddressBelongsTo_ = function(addr, node) {
return addr >= node.key && addr < (node.key + node.value.size);
};
/** /**
* @private * Finds a code entry that contains the specified address. Both static and
*/ * dynamic code entries are considered. Returns the code entry and the offset
CodeMap.prototype.findInTree_ = function(tree, addr) { * within the entry.
var node = tree.findGreatestLessThan(addr); *
return node && this.isAddressBelongsTo_(addr, node) ? node : null; * @param {number} addr Address.
}; */
findAddress(addr) {
var pageAddr = (addr / CodeMap.PAGE_SIZE)|0;
/** if (pageAddr in this.pages_) {
* Finds a code entry that contains the specified address. Both static and // Static code entries can contain "holes" of unnamed code.
* dynamic code entries are considered. Returns the code entry and the offset // In this case, the whole library is assigned to this address.
* within the entry. var result = this.findInTree_(this.statics_, addr);
* if (!result) {
* @param {number} addr Address. result = this.findInTree_(this.libraries_, addr);
*/ if (!result) return null;
CodeMap.prototype.findAddress = function(addr) { }
var pageAddr = (addr / CodeMap.PAGE_SIZE)|0; return { entry : result.value, offset : addr - result.key };
if (pageAddr in this.pages_) {
// Static code entries can contain "holes" of unnamed code.
// In this case, the whole library is assigned to this address.
var result = this.findInTree_(this.statics_, addr);
if (!result) {
result = this.findInTree_(this.libraries_, addr);
if (!result) return null;
} }
return { entry : result.value, offset : addr - result.key }; var min = this.dynamics_.findMin();
} var max = this.dynamics_.findMax();
var min = this.dynamics_.findMin(); if (max != null && addr < (max.key + max.value.size) && addr >= min.key) {
var max = this.dynamics_.findMax(); var dynaEntry = this.findInTree_(this.dynamics_, addr);
if (max != null && addr < (max.key + max.value.size) && addr >= min.key) { if (dynaEntry == null) return null;
var dynaEntry = this.findInTree_(this.dynamics_, addr); // Dedupe entry name.
if (dynaEntry == null) return null; var entry = dynaEntry.value;
// Dedupe entry name. if (!entry.nameUpdated_) {
var entry = dynaEntry.value; entry.name = this.dynamicsNameGen_.getName(entry.name);
if (!entry.nameUpdated_) { entry.nameUpdated_ = true;
entry.name = this.dynamicsNameGen_.getName(entry.name); }
entry.nameUpdated_ = true; return { entry : entry, offset : addr - dynaEntry.key };
} }
return { entry : entry, offset : addr - dynaEntry.key }; return null;
} }
return null;
};
/**
* Finds a code entry that contains the specified address. Both static and
* dynamic code entries are considered.
*
* @param {number} addr Address.
*/
CodeMap.prototype.findEntry = function(addr) {
var result = this.findAddress(addr);
return result ? result.entry : null;
};
/** /**
* Returns a dynamic code entry using its starting address. * Finds a code entry that contains the specified address. Both static and
* * dynamic code entries are considered.
* @param {number} addr Address. *
*/ * @param {number} addr Address.
CodeMap.prototype.findDynamicEntryByStartAddress = */
function(addr) { findEntry(addr) {
var node = this.dynamics_.find(addr); var result = this.findAddress(addr);
return node ? node.value : null; return result ? result.entry : null;
}; }
/**
* Returns an array of all dynamic code entries.
*/
CodeMap.prototype.getAllDynamicEntries = function() {
return this.dynamics_.exportValues();
};
/**
* Returns an array of pairs of all dynamic code entries and their addresses.
*/
CodeMap.prototype.getAllDynamicEntriesWithAddresses = function() {
return this.dynamics_.exportKeysAndValues();
};
/**
* Returns a dynamic code entry using its starting address.
*
* @param {number} addr Address.
*/
findDynamicEntryByStartAddress(addr) {
var node = this.dynamics_.find(addr);
return node ? node.value : null;
}
/** /**
* Returns an array of all static code entries. * Returns an array of all dynamic code entries.
*/ */
CodeMap.prototype.getAllStaticEntries = function() { getAllDynamicEntries() {
return this.statics_.exportValues(); return this.dynamics_.exportValues();
}; }
/**
* Returns an array of pairs of all dynamic code entries and their addresses.
*/
getAllDynamicEntriesWithAddresses() {
return this.dynamics_.exportKeysAndValues();
}
/** /**
* Returns an array of pairs of all static code entries and their addresses. * Returns an array of all static code entries.
*/ */
CodeMap.prototype.getAllStaticEntriesWithAddresses = function() { getAllStaticEntries() {
return this.statics_.exportKeysAndValues(); return this.statics_.exportValues();
}; }
/**
* Returns an array of pairs of all static code entries and their addresses.
*/
getAllStaticEntriesWithAddresses() {
return this.statics_.exportKeysAndValues();
}
/** /**
* Returns an array of all libraries entries. * Returns an array of all libraries entries.
*/ */
CodeMap.prototype.getAllLibrariesEntries = function() { getAllLibrariesEntries() {
return this.libraries_.exportValues(); return this.libraries_.exportValues();
}; }
}
/** /**
...@@ -288,34 +268,31 @@ CodeMap.prototype.getAllLibrariesEntries = function() { ...@@ -288,34 +268,31 @@ CodeMap.prototype.getAllLibrariesEntries = function() {
* @param {string} opt_type Code entry type, e.g. SHARED_LIB, CPP. * @param {string} opt_type Code entry type, e.g. SHARED_LIB, CPP.
* @constructor * @constructor
*/ */
CodeMap.CodeEntry = function(size, opt_name, opt_type) { export class CodeEntry {
this.size = size; constructor(size, opt_name, opt_type) {
this.name = opt_name || ''; this.size = size;
this.type = opt_type || ''; this.name = opt_name || '';
this.nameUpdated_ = false; this.type = opt_type || '';
}; this.nameUpdated_ = false;
}
CodeMap.CodeEntry.prototype.getName = function() {
return this.name;
};
CodeMap.CodeEntry.prototype.toString = function() {
return this.name + ': ' + this.size.toString(16);
};
CodeMap.NameGenerator = function() {
this.knownNames_ = {};
};
getName() {
return this.name;
}
CodeMap.NameGenerator.prototype.getName = function(name) { toString() {
if (!(name in this.knownNames_)) { return this.name + ': ' + this.size.toString(16);
this.knownNames_[name] = 0;
return name;
} }
var count = ++this.knownNames_[name]; }
return name + ' {' + count + '}';
}; class NameGenerator {
knownNames_ = { __proto__:null }
getName(name) {
if (!(name in this.knownNames_)) {
this.knownNames_[name] = 0;
return name;
}
var count = ++this.knownNames_[name];
return name + ' {' + count + '}';
};
}
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import { LogReader, parseString } from "./logreader.mjs"; import { LogReader, parseString } from "./logreader.mjs";
import { CodeMap } from "./codemap.mjs"; import { CodeMap, CodeEntry } from "./codemap.mjs";
export { export {
ArgumentsProcessor, UnixCppEntriesProvider, ArgumentsProcessor, UnixCppEntriesProvider,
WindowsCppEntriesProvider, MacCppEntriesProvider, WindowsCppEntriesProvider, MacCppEntriesProvider,
...@@ -11,57 +11,58 @@ export { ...@@ -11,57 +11,58 @@ export {
import { inherits } from "./tickprocessor.mjs"; import { inherits } from "./tickprocessor.mjs";
export function CppProcessor(cppEntriesProvider, timedRange, pairwiseTimedRange) { export class CppProcessor extends LogReader {
LogReader.call(this, { constructor(cppEntriesProvider, timedRange, pairwiseTimedRange) {
'shared-library': { parsers: [parseString, parseInt, parseInt, parseInt], super({}, timedRange, pairwiseTimedRange);
this.dispatchTable_ = {
'shared-library': {
parsers: [parseString, parseInt, parseInt, parseInt],
processor: this.processSharedLibrary } processor: this.processSharedLibrary }
}, timedRange, pairwiseTimedRange); };
this.cppEntriesProvider_ = cppEntriesProvider;
this.codeMap_ = new CodeMap();
this.lastLogFileName_ = null;
}
this.cppEntriesProvider_ = cppEntriesProvider; /**
this.codeMap_ = new CodeMap(); * @override
this.lastLogFileName_ = null; */
} printError(str) {
inherits(CppProcessor, LogReader); print(str);
};
/** processLogFile(fileName) {
* @override this.lastLogFileName_ = fileName;
*/ var line;
CppProcessor.prototype.printError = function(str) { while (line = readline()) {
print(str); this.processLogLine(line);
}; }
};
CppProcessor.prototype.processLogFile = function(fileName) { processLogFileInTest(fileName) {
this.lastLogFileName_ = fileName; // Hack file name to avoid dealing with platform specifics.
var line; this.lastLogFileName_ = 'v8.log';
while (line = readline()) { var contents = readFile(fileName);
this.processLogLine(line); this.processLogChunk(contents);
} };
};
CppProcessor.prototype.processLogFileInTest = function(fileName) { processSharedLibrary(name, startAddr, endAddr, aslrSlide) {
// Hack file name to avoid dealing with platform specifics. var self = this;
this.lastLogFileName_ = 'v8.log'; var libFuncs = this.cppEntriesProvider_.parseVmSymbols(
var contents = readFile(fileName); name, startAddr, endAddr, aslrSlide, function(fName, fStart, fEnd) {
this.processLogChunk(contents); var entry = new CodeEntry(fEnd - fStart, fName, 'CPP');
}; self.codeMap_.addStaticCode(fStart, entry);
});
};
CppProcessor.prototype.processSharedLibrary = function( dumpCppSymbols() {
name, startAddr, endAddr, aslrSlide) { var staticEntries = this.codeMap_.getAllStaticEntriesWithAddresses();
var self = this; var total = staticEntries.length;
var libFuncs = this.cppEntriesProvider_.parseVmSymbols( for (var i = 0; i < total; ++i) {
name, startAddr, endAddr, aslrSlide, function(fName, fStart, fEnd) { var entry = staticEntries[i];
var entry = new CodeMap.CodeEntry(fEnd - fStart, fName, 'CPP'); var printValues = ['cpp', '0x' + entry[0].toString(16), entry[1].size,
self.codeMap_.addStaticCode(fStart, entry); '"' + entry[1].name + '"'];
}); print(printValues.join(','));
}; }
CppProcessor.prototype.dumpCppSymbols = function() {
var staticEntries = this.codeMap_.getAllStaticEntriesWithAddresses();
var total = staticEntries.length;
for (var i = 0; i < total; ++i) {
var entry = staticEntries[i];
var printValues = ['cpp', '0x' + entry[0].toString(16), entry[1].size,
'"' + entry[1].name + '"'];
print(printValues.join(','));
} }
}; }
...@@ -34,4 +34,4 @@ fi ...@@ -34,4 +34,4 @@ fi
# nm spits out 'no symbols found' messages to stderr. # nm spits out 'no symbols found' messages to stderr.
cat $log_file | $d8_exec --enable-os-system \ cat $log_file | $d8_exec --enable-os-system \
--module $tools_path/tickprocessor-driver.mjs -- $@ 2>/dev/null --module $tools_path/tickprocessor-driver.mjs -- $@
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import { CodeMap } from "./codemap.mjs"; import { CodeMap, CodeEntry } from "./codemap.mjs";
import { ConsArray } from "./consarray.mjs"; import { ConsArray } from "./consarray.mjs";
// TODO: move to separate modules // TODO: move to separate modules
...@@ -42,7 +42,6 @@ export class SourcePosition { ...@@ -42,7 +42,6 @@ export class SourcePosition {
} }
export class Script { export class Script {
constructor(id, name, source) { constructor(id, name, source) {
this.id = id; this.id = id;
this.name = name; this.name = name;
...@@ -81,455 +80,426 @@ export class Script { ...@@ -81,455 +80,426 @@ export class Script {
* *
* @constructor * @constructor
*/ */
export function Profile() { export class Profile {
this.codeMap_ = new CodeMap(); codeMap_ = new CodeMap();
this.topDownTree_ = new CallTree(); topDownTree_ = new CallTree();
this.bottomUpTree_ = new CallTree(); bottomUpTree_ = new CallTree();
this.c_entries_ = {}; c_entries_ = {};
this.ticks_ = []; ticks_ = [];
this.scripts_ = []; scripts_ = [];
this.urlToScript_ = new Map(); urlToScript_ = new Map();
};
/**
* Returns whether a function with the specified name must be skipped.
/** * Should be overriden by subclasses.
* Returns whether a function with the specified name must be skipped. *
* Should be overriden by subclasses. * @param {string} name Function name.
* */
* @param {string} name Function name. skipThisFunction(name) {
*/ return false;
Profile.prototype.skipThisFunction = function (name) { }
return false;
};
/**
* Enum for profiler operations that involve looking up existing
* code entries.
*
* @enum {number}
*/
Profile.Operation = {
MOVE: 0,
DELETE: 1,
TICK: 2
};
/**
* Enum for code state regarding its dynamic optimization.
*
* @enum {number}
*/
Profile.CodeState = {
COMPILED: 0,
OPTIMIZABLE: 1,
OPTIMIZED: 2
};
/**
* Called whenever the specified operation has failed finding a function
* containing the specified address. Should be overriden by subclasses.
* See the Profile.Operation enum for the list of
* possible operations.
*
* @param {number} operation Operation.
* @param {number} addr Address of the unknown code.
* @param {number} opt_stackPos If an unknown address is encountered
* during stack strace processing, specifies a position of the frame
* containing the address.
*/
Profile.prototype.handleUnknownCode = function (
operation, addr, opt_stackPos) {
};
/**
* Enum for profiler operations that involve looking up existing
* code entries.
*
* @enum {number}
*/
static Operation = {
MOVE: 0,
DELETE: 1,
TICK: 2
}
/** /**
* Registers a library. * Enum for code state regarding its dynamic optimization.
* *
* @param {string} name Code entry name. * @enum {number}
* @param {number} startAddr Starting address. */
* @param {number} endAddr Ending address. static CodeState = {
*/ COMPILED: 0,
Profile.prototype.addLibrary = function ( OPTIMIZABLE: 1,
name, startAddr, endAddr) { OPTIMIZED: 2
var entry = new CodeMap.CodeEntry( }
endAddr - startAddr, name, 'SHARED_LIB');
this.codeMap_.addLibrary(startAddr, entry);
return entry;
};
/**
* Called whenever the specified operation has failed finding a function
* containing the specified address. Should be overriden by subclasses.
* See the Profile.Operation enum for the list of
* possible operations.
*
* @param {number} operation Operation.
* @param {number} addr Address of the unknown code.
* @param {number} opt_stackPos If an unknown address is encountered
* during stack strace processing, specifies a position of the frame
* containing the address.
*/
handleUnknownCode(operation, addr, opt_stackPos) {}
/**
* Registers a library.
*
* @param {string} name Code entry name.
* @param {number} startAddr Starting address.
* @param {number} endAddr Ending address.
*/
addLibrary(name, startAddr, endAddr) {
var entry = new CodeEntry(endAddr - startAddr, name, 'SHARED_LIB');
this.codeMap_.addLibrary(startAddr, entry);
return entry;
}
/** /**
* Registers statically compiled code entry. * Registers statically compiled code entry.
* *
* @param {string} name Code entry name. * @param {string} name Code entry name.
* @param {number} startAddr Starting address. * @param {number} startAddr Starting address.
* @param {number} endAddr Ending address. * @param {number} endAddr Ending address.
*/ */
Profile.prototype.addStaticCode = function ( addStaticCode(name, startAddr, endAddr) {
name, startAddr, endAddr) { var entry = new CodeEntry(endAddr - startAddr, name, 'CPP');
var entry = new CodeMap.CodeEntry( this.codeMap_.addStaticCode(startAddr, entry);
endAddr - startAddr, name, 'CPP'); return entry;
this.codeMap_.addStaticCode(startAddr, entry); }
return entry;
};
/**
* Registers dynamic (JIT-compiled) code entry.
*
* @param {string} type Code entry type.
* @param {string} name Code entry name.
* @param {number} start Starting address.
* @param {number} size Code entry size.
*/
addCode(type, name, timestamp, start, size) {
var entry = new DynamicCodeEntry(size, type, name);
this.codeMap_.addCode(start, entry);
return entry;
}
/** /**
* Registers dynamic (JIT-compiled) code entry. * Registers dynamic (JIT-compiled) code entry.
* *
* @param {string} type Code entry type. * @param {string} type Code entry type.
* @param {string} name Code entry name. * @param {string} name Code entry name.
* @param {number} start Starting address. * @param {number} start Starting address.
* @param {number} size Code entry size. * @param {number} size Code entry size.
*/ * @param {number} funcAddr Shared function object address.
Profile.prototype.addCode = function ( * @param {Profile.CodeState} state Optimization state.
type, name, timestamp, start, size) { */
var entry = new Profile.DynamicCodeEntry(size, type, name); addFuncCode(type, name, timestamp, start, size, funcAddr, state) {
this.codeMap_.addCode(start, entry); // As code and functions are in the same address space,
return entry; // it is safe to put them in a single code map.
}; var func = this.codeMap_.findDynamicEntryByStartAddress(funcAddr);
if (!func) {
func = new FunctionEntry(name);
this.codeMap_.addCode(funcAddr, func);
} else if (func.name !== name) {
// Function object has been overwritten with a new one.
func.name = name;
}
var entry = this.codeMap_.findDynamicEntryByStartAddress(start);
if (entry) {
if (entry.size === size && entry.func === func) {
// Entry state has changed.
entry.state = state;
} else {
this.codeMap_.deleteCode(start);
entry = null;
}
}
if (!entry) {
entry = new DynamicFuncCodeEntry(size, type, func, state);
this.codeMap_.addCode(start, entry);
}
return entry;
}
/**
* Reports about moving of a dynamic code entry.
*
* @param {number} from Current code entry address.
* @param {number} to New code entry address.
*/
moveCode(from, to) {
try {
this.codeMap_.moveCode(from, to);
} catch (e) {
this.handleUnknownCode(Profile.Operation.MOVE, from);
}
}
/** deoptCode( timestamp, code, inliningId, scriptOffset, bailoutType,
* Registers dynamic (JIT-compiled) code entry. sourcePositionText, deoptReasonText) {
*
* @param {string} type Code entry type.
* @param {string} name Code entry name.
* @param {number} start Starting address.
* @param {number} size Code entry size.
* @param {number} funcAddr Shared function object address.
* @param {Profile.CodeState} state Optimization state.
*/
Profile.prototype.addFuncCode = function (
type, name, timestamp, start, size, funcAddr, state) {
// As code and functions are in the same address space,
// it is safe to put them in a single code map.
var func = this.codeMap_.findDynamicEntryByStartAddress(funcAddr);
if (!func) {
func = new Profile.FunctionEntry(name);
this.codeMap_.addCode(funcAddr, func);
} else if (func.name !== name) {
// Function object has been overwritten with a new one.
func.name = name;
} }
var entry = this.codeMap_.findDynamicEntryByStartAddress(start);
if (entry) { /**
if (entry.size === size && entry.func === func) { * Reports about deletion of a dynamic code entry.
// Entry state has changed. *
entry.state = state; * @param {number} start Starting address.
} else { */
deleteCode(start) {
try {
this.codeMap_.deleteCode(start); this.codeMap_.deleteCode(start);
entry = null; } catch (e) {
this.handleUnknownCode(Profile.Operation.DELETE, start);
} }
} }
if (!entry) {
entry = new Profile.DynamicFuncCodeEntry(size, type, func, state);
this.codeMap_.addCode(start, entry);
}
return entry;
};
/** /**
* Reports about moving of a dynamic code entry. * Adds source positions for given code.
* */
* @param {number} from Current code entry address. addSourcePositions(start, script, startPos, endPos, sourcePositions,
* @param {number} to New code entry address. inliningPositions, inlinedFunctions) {
*/ // CLI does not need source code => ignore.
Profile.prototype.moveCode = function (from, to) {
try {
this.codeMap_.moveCode(from, to);
} catch (e) {
this.handleUnknownCode(Profile.Operation.MOVE, from);
} }
};
Profile.prototype.deoptCode = function ( /**
timestamp, code, inliningId, scriptOffset, bailoutType, * Adds script source code.
sourcePositionText, deoptReasonText) { */
}; addScriptSource(id, url, source) {
const script = new Script(id, url, source);
/** this.scripts_[id] = script;
* Reports about deletion of a dynamic code entry. this.urlToScript_.set(url, script);
*
* @param {number} start Starting address.
*/
Profile.prototype.deleteCode = function (start) {
try {
this.codeMap_.deleteCode(start);
} catch (e) {
this.handleUnknownCode(Profile.Operation.DELETE, start);
} }
};
/**
* Adds source positions for given code.
*/
Profile.prototype.addSourcePositions = function (
start, script, startPos, endPos, sourcePositions, inliningPositions,
inlinedFunctions) {
// CLI does not need source code => ignore.
};
/**
* Adds script source code.
*/
Profile.prototype.addScriptSource = function (id, url, source) {
const script = new Script(id, url, source);
this.scripts_[id] = script;
this.urlToScript_.set(url, script);
};
/**
* Adds script source code.
*/
Profile.prototype.getScript = function (url) {
return this.urlToScript_.get(url);
};
/** /**
* Reports about moving of a dynamic code entry. * Adds script source code.
* */
* @param {number} from Current code entry address. getScript(url) {
* @param {number} to New code entry address. return this.urlToScript_.get(url);
*/
Profile.prototype.moveFunc = function (from, to) {
if (this.codeMap_.findDynamicEntryByStartAddress(from)) {
this.codeMap_.moveCode(from, to);
} }
};
/**
* Retrieves a code entry by an address.
*
* @param {number} addr Entry address.
*/
Profile.prototype.findEntry = function (addr) {
return this.codeMap_.findEntry(addr);
};
/**
* Reports about moving of a dynamic code entry.
*
* @param {number} from Current code entry address.
* @param {number} to New code entry address.
*/
moveFunc(from, to) {
if (this.codeMap_.findDynamicEntryByStartAddress(from)) {
this.codeMap_.moveCode(from, to);
}
}
/** /**
* Records a tick event. Stack must contain a sequence of * Retrieves a code entry by an address.
* addresses starting with the program counter value. *
* * @param {number} addr Entry address.
* @param {Array<number>} stack Stack sample. */
*/ findEntry(addr) {
Profile.prototype.recordTick = function (time_ns, vmState, stack) { return this.codeMap_.findEntry(addr);
var processedStack = this.resolveAndFilterFuncs_(stack); }
this.bottomUpTree_.addPath(processedStack);
processedStack.reverse();
this.topDownTree_.addPath(processedStack);
};
/**
* Records a tick event. Stack must contain a sequence of
* addresses starting with the program counter value.
*
* @param {Array<number>} stack Stack sample.
*/
recordTick(time_ns, vmState, stack) {
var processedStack = this.resolveAndFilterFuncs_(stack);
this.bottomUpTree_.addPath(processedStack);
processedStack.reverse();
this.topDownTree_.addPath(processedStack);
}
/** /**
* Translates addresses into function names and filters unneeded * Translates addresses into function names and filters unneeded
* functions. * functions.
* *
* @param {Array<number>} stack Stack sample. * @param {Array<number>} stack Stack sample.
*/ */
Profile.prototype.resolveAndFilterFuncs_ = function (stack) { resolveAndFilterFuncs_(stack) {
var result = []; var result = [];
var last_seen_c_function = ''; var last_seen_c_function = '';
var look_for_first_c_function = false; var look_for_first_c_function = false;
for (var i = 0; i < stack.length; ++i) { for (var i = 0; i < stack.length; ++i) {
var entry = this.codeMap_.findEntry(stack[i]); var entry = this.codeMap_.findEntry(stack[i]);
if (entry) { if (entry) {
var name = entry.getName(); var name = entry.getName();
if (i === 0 && (entry.type === 'CPP' || entry.type === 'SHARED_LIB')) { if (i === 0 && (entry.type === 'CPP' || entry.type === 'SHARED_LIB')) {
look_for_first_c_function = true; look_for_first_c_function = true;
} }
if (look_for_first_c_function && entry.type === 'CPP') { if (look_for_first_c_function && entry.type === 'CPP') {
last_seen_c_function = name; last_seen_c_function = name;
} }
if (!this.skipThisFunction(name)) { if (!this.skipThisFunction(name)) {
result.push(name); result.push(name);
}
} else {
this.handleUnknownCode(Profile.Operation.TICK, stack[i], i);
if (i === 0) result.push("UNKNOWN");
} }
} else { if (look_for_first_c_function &&
this.handleUnknownCode(Profile.Operation.TICK, stack[i], i); i > 0 &&
if (i === 0) result.push("UNKNOWN"); (!entry || entry.type !== 'CPP') &&
} last_seen_c_function !== '') {
if (look_for_first_c_function && if (this.c_entries_[last_seen_c_function] === undefined) {
i > 0 && this.c_entries_[last_seen_c_function] = 0;
(!entry || entry.type !== 'CPP') && }
last_seen_c_function !== '') { this.c_entries_[last_seen_c_function]++;
if (this.c_entries_[last_seen_c_function] === undefined) { look_for_first_c_function = false; // Found it, we're done.
this.c_entries_[last_seen_c_function] = 0;
} }
this.c_entries_[last_seen_c_function]++;
look_for_first_c_function = false; // Found it, we're done.
} }
return result;
} }
return result;
};
/**
* Performs a BF traversal of the top down call graph.
*
* @param {function(CallTree.Node)} f Visitor function.
*/
Profile.prototype.traverseTopDownTree = function (f) {
this.topDownTree_.traverse(f);
};
/**
* Performs a BF traversal of the bottom up call graph.
*
* @param {function(CallTree.Node)} f Visitor function.
*/
Profile.prototype.traverseBottomUpTree = function (f) {
this.bottomUpTree_.traverse(f);
};
/**
* Calculates a top down profile for a node with the specified label.
* If no name specified, returns the whole top down calls tree.
*
* @param {string} opt_label Node label.
*/
Profile.prototype.getTopDownProfile = function (opt_label) {
return this.getTreeProfile_(this.topDownTree_, opt_label);
};
/**
* Performs a BF traversal of the top down call graph.
*
* @param {function(CallTreeNode)} f Visitor function.
*/
traverseTopDownTree(f) {
this.topDownTree_.traverse(f);
}
/** /**
* Calculates a bottom up profile for a node with the specified label. * Performs a BF traversal of the bottom up call graph.
* If no name specified, returns the whole bottom up calls tree. *
* * @param {function(CallTreeNode)} f Visitor function.
* @param {string} opt_label Node label. */
*/ traverseBottomUpTree(f) {
Profile.prototype.getBottomUpProfile = function (opt_label) { this.bottomUpTree_.traverse(f);
return this.getTreeProfile_(this.bottomUpTree_, opt_label); }
};
/**
* Calculates a top down profile for a node with the specified label.
* If no name specified, returns the whole top down calls tree.
*
* @param {string} opt_label Node label.
*/
getTopDownProfile(opt_label) {
return this.getTreeProfile_(this.topDownTree_, opt_label);
}
/** /**
* Helper function for calculating a tree profile. * Calculates a bottom up profile for a node with the specified label.
* * If no name specified, returns the whole bottom up calls tree.
* @param {Profile.CallTree} tree Call tree. *
* @param {string} opt_label Node label. * @param {string} opt_label Node label.
*/ */
Profile.prototype.getTreeProfile_ = function (tree, opt_label) { getBottomUpProfile(opt_label) {
if (!opt_label) { return this.getTreeProfile_(this.bottomUpTree_, opt_label);
tree.computeTotalWeights();
return tree;
} else {
var subTree = tree.cloneSubtree(opt_label);
subTree.computeTotalWeights();
return subTree;
} }
};
/**
* Helper function for calculating a tree profile.
*
* @param {Profile.CallTree} tree Call tree.
* @param {string} opt_label Node label.
*/
getTreeProfile_(tree, opt_label) {
if (!opt_label) {
tree.computeTotalWeights();
return tree;
} else {
var subTree = tree.cloneSubtree(opt_label);
subTree.computeTotalWeights();
return subTree;
}
}
/** /**
* Calculates a flat profile of callees starting from a node with * Calculates a flat profile of callees starting from a node with
* the specified label. If no name specified, starts from the root. * the specified label. If no name specified, starts from the root.
* *
* @param {string} opt_label Starting node label. * @param {string} opt_label Starting node label.
*/ */
Profile.prototype.getFlatProfile = function (opt_label) { getFlatProfile(opt_label) {
var counters = new CallTree(); var counters = new CallTree();
var rootLabel = opt_label || CallTree.ROOT_NODE_LABEL; var rootLabel = opt_label || CallTree.ROOT_NODE_LABEL;
var precs = {}; var precs = {};
precs[rootLabel] = 0; precs[rootLabel] = 0;
var root = counters.findOrAddChild(rootLabel); var root = counters.findOrAddChild(rootLabel);
this.topDownTree_.computeTotalWeights(); this.topDownTree_.computeTotalWeights();
this.topDownTree_.traverseInDepth( this.topDownTree_.traverseInDepth(
function onEnter(node) { function onEnter(node) {
if (!(node.label in precs)) { if (!(node.label in precs)) {
precs[node.label] = 0; precs[node.label] = 0;
} }
var nodeLabelIsRootLabel = node.label == rootLabel; var nodeLabelIsRootLabel = node.label == rootLabel;
if (nodeLabelIsRootLabel || precs[rootLabel] > 0) { if (nodeLabelIsRootLabel || precs[rootLabel] > 0) {
if (precs[rootLabel] == 0) { if (precs[rootLabel] == 0) {
root.selfWeight += node.selfWeight; root.selfWeight += node.selfWeight;
root.totalWeight += node.totalWeight; root.totalWeight += node.totalWeight;
} else { } else {
var rec = root.findOrAddChild(node.label); var rec = root.findOrAddChild(node.label);
rec.selfWeight += node.selfWeight; rec.selfWeight += node.selfWeight;
if (nodeLabelIsRootLabel || precs[node.label] == 0) { if (nodeLabelIsRootLabel || precs[node.label] == 0) {
rec.totalWeight += node.totalWeight; rec.totalWeight += node.totalWeight;
}
} }
precs[node.label]++;
} }
precs[node.label]++; },
} function onExit(node) {
}, if (node.label == rootLabel || precs[rootLabel] > 0) {
function onExit(node) { precs[node.label]--;
if (node.label == rootLabel || precs[rootLabel] > 0) { }
precs[node.label]--; },
} null);
},
null); if (!opt_label) {
// If we have created a flat profile for the whole program, we don't
if (!opt_label) { // need an explicit root in it. Thus, replace the counters tree
// If we have created a flat profile for the whole program, we don't // root with the node corresponding to the whole program.
// need an explicit root in it. Thus, replace the counters tree counters.root_ = root;
// root with the node corresponding to the whole program. } else {
counters.root_ = root; // Propagate weights so percents can be calculated correctly.
} else { counters.getRoot().selfWeight = root.selfWeight;
// Propagate weights so percents can be calculated correctly. counters.getRoot().totalWeight = root.totalWeight;
counters.getRoot().selfWeight = root.selfWeight; }
counters.getRoot().totalWeight = root.totalWeight; return counters;
} }
return counters;
};
getCEntryProfile() {
Profile.CEntryNode = function (name, ticks) { var result = [new CEntryNode("TOTAL", 0)];
this.name = name; var total_ticks = 0;
this.ticks = ticks; for (var f in this.c_entries_) {
} var ticks = this.c_entries_[f];
total_ticks += ticks;
result.push(new CEntryNode(f, ticks));
Profile.prototype.getCEntryProfile = function () { }
var result = [new Profile.CEntryNode("TOTAL", 0)]; result[0].ticks = total_ticks; // Sorting will keep this at index 0.
var total_ticks = 0; result.sort(function (n1, n2) {
for (var f in this.c_entries_) { return n2.ticks - n1.ticks || (n2.name < n1.name ? -1 : 1)
var ticks = this.c_entries_[f]; });
total_ticks += ticks; return result;
result.push(new Profile.CEntryNode(f, ticks));
} }
result[0].ticks = total_ticks; // Sorting will keep this at index 0.
result.sort(function (n1, n2) {
return n2.ticks - n1.ticks || (n2.name < n1.name ? -1 : 1)
});
return result;
}
/** /**
* Cleans up function entries that are not referenced by code entries. * Cleans up function entries that are not referenced by code entries.
*/ */
Profile.prototype.cleanUpFuncEntries = function () { cleanUpFuncEntries() {
var referencedFuncEntries = []; var referencedFuncEntries = [];
var entries = this.codeMap_.getAllDynamicEntriesWithAddresses(); var entries = this.codeMap_.getAllDynamicEntriesWithAddresses();
for (var i = 0, l = entries.length; i < l; ++i) { for (var i = 0, l = entries.length; i < l; ++i) {
if (entries[i][1].constructor === Profile.FunctionEntry) { if (entries[i][1].constructor === FunctionEntry) {
entries[i][1].used = false; entries[i][1].used = false;
}
} }
} for (var i = 0, l = entries.length; i < l; ++i) {
for (var i = 0, l = entries.length; i < l; ++i) { if ("func" in entries[i][1]) {
if ("func" in entries[i][1]) { entries[i][1].func.used = true;
entries[i][1].func.used = true; }
} }
} for (var i = 0, l = entries.length; i < l; ++i) {
for (var i = 0, l = entries.length; i < l; ++i) { if (entries[i][1].constructor === FunctionEntry &&
if (entries[i][1].constructor === Profile.FunctionEntry && !entries[i][1].used) {
!entries[i][1].used) { this.codeMap_.deleteCode(entries[i][0]);
this.codeMap_.deleteCode(entries[i][0]); }
} }
} }
}; }
class CEntryNode {
constructor(name, ticks) {
this.name = name;
this.ticks = ticks;
}
}
/** /**
...@@ -540,35 +510,30 @@ Profile.prototype.cleanUpFuncEntries = function () { ...@@ -540,35 +510,30 @@ Profile.prototype.cleanUpFuncEntries = function () {
* @param {string} name Function name. * @param {string} name Function name.
* @constructor * @constructor
*/ */
Profile.DynamicCodeEntry = function (size, type, name) { class DynamicCodeEntry extends CodeEntry {
CodeMap.CodeEntry.call(this, size, name, type); constructor(size, type, name) {
}; super(size, name, type);
}
/** getName() {
* Returns node name. return this.type + ': ' + this.name;
*/ }
Profile.DynamicCodeEntry.prototype.getName = function () {
return this.type + ': ' + this.name;
};
/**
* Returns raw node name (without type decoration).
*/
Profile.DynamicCodeEntry.prototype.getRawName = function () {
return this.name;
};
Profile.DynamicCodeEntry.prototype.isJSFunction = function () { /**
return false; * Returns raw node name (without type decoration).
}; */
getRawName() {
return this.name;
}
isJSFunction() {
return false;
}
Profile.DynamicCodeEntry.prototype.toString = function () { toString() {
return this.getName() + ': ' + this.size.toString(16); return this.getName() + ': ' + this.size.toString(16);
}; }
}
/** /**
...@@ -576,51 +541,42 @@ Profile.DynamicCodeEntry.prototype.toString = function () { ...@@ -576,51 +541,42 @@ Profile.DynamicCodeEntry.prototype.toString = function () {
* *
* @param {number} size Code size. * @param {number} size Code size.
* @param {string} type Code type. * @param {string} type Code type.
* @param {Profile.FunctionEntry} func Shared function entry. * @param {FunctionEntry} func Shared function entry.
* @param {Profile.CodeState} state Code optimization state. * @param {Profile.CodeState} state Code optimization state.
* @constructor * @constructor
*/ */
Profile.DynamicFuncCodeEntry = function (size, type, func, state) { class DynamicFuncCodeEntry extends CodeEntry {
CodeMap.CodeEntry.call(this, size, '', type); constructor(size, type, func, state) {
this.func = func; super(size, '', type);
this.state = state; this.func = func;
}; this.state = state;
}
Profile.DynamicFuncCodeEntry.STATE_PREFIX = ["", "~", "*"];
/**
* Returns state.
*/
Profile.DynamicFuncCodeEntry.prototype.getState = function () {
return Profile.DynamicFuncCodeEntry.STATE_PREFIX[this.state];
};
/**
* Returns node name.
*/
Profile.DynamicFuncCodeEntry.prototype.getName = function () {
var name = this.func.getName();
return this.type + ': ' + this.getState() + name;
};
/**
* Returns raw node name (without type decoration).
*/
Profile.DynamicFuncCodeEntry.prototype.getRawName = function () {
return this.func.getName();
};
Profile.DynamicFuncCodeEntry.prototype.isJSFunction = function () { static STATE_PREFIX = ["", "~", "*"];
return true; getState() {
}; return DynamicFuncCodeEntry.STATE_PREFIX[this.state];
}
getName() {
var name = this.func.getName();
return this.type + ': ' + this.getState() + name;
}
/**
* Returns raw node name (without type decoration).
*/
getRawName() {
return this.func.getName();
}
Profile.DynamicFuncCodeEntry.prototype.toString = function () { isJSFunction() {
return this.getName() + ': ' + this.size.toString(16); return true;
}; }
toString() {
return this.getName() + ': ' + this.size.toString(16);
}
}
/** /**
* Creates a shared function object entry. * Creates a shared function object entry.
...@@ -628,304 +584,278 @@ Profile.DynamicFuncCodeEntry.prototype.toString = function () { ...@@ -628,304 +584,278 @@ Profile.DynamicFuncCodeEntry.prototype.toString = function () {
* @param {string} name Function name. * @param {string} name Function name.
* @constructor * @constructor
*/ */
Profile.FunctionEntry = function (name) { class FunctionEntry extends CodeEntry {
CodeMap.CodeEntry.call(this, 0, name); constructor(name) {
}; super(0, name);
}
/**
* Returns node name.
*/
Profile.FunctionEntry.prototype.getName = function () {
var name = this.name;
if (name.length == 0) {
name = '<anonymous>';
} else if (name.charAt(0) == ' ') {
// An anonymous function with location: " aaa.js:10".
name = '<anonymous>' + name;
}
return name;
};
Profile.FunctionEntry.prototype.toString = CodeMap.CodeEntry.prototype.toString; /**
* Returns node name.
*/
getName() {
var name = this.name;
if (name.length == 0) {
name = '<anonymous>';
} else if (name.charAt(0) == ' ') {
// An anonymous function with location: " aaa.js:10".
name = '<anonymous>' + name;
}
return name;
}
}
/** /**
* Constructs a call graph. * Constructs a call graph.
* *
* @constructor * @constructor
*/ */
function CallTree() { class CallTree {
this.root_ = new CallTree.Node( root_ = new CallTreeNode(CallTree.ROOT_NODE_LABEL);
CallTree.ROOT_NODE_LABEL); totalsComputed_ = false;
};
/**
* The label of the root node.
/** */
* The label of the root node. static ROOT_NODE_LABEL = '';
*/
CallTree.ROOT_NODE_LABEL = ''; /**
* Returns the tree root.
*/
/** getRoot() {
* @private return this.root_;
*/
CallTree.prototype.totalsComputed_ = false;
/**
* Returns the tree root.
*/
CallTree.prototype.getRoot = function () {
return this.root_;
};
/**
* Adds the specified call path, constructing nodes as necessary.
*
* @param {Array<string>} path Call path.
*/
CallTree.prototype.addPath = function (path) {
if (path.length == 0) {
return;
}
var curr = this.root_;
for (var i = 0; i < path.length; ++i) {
curr = curr.findOrAddChild(path[i]);
} }
curr.selfWeight++;
this.totalsComputed_ = false;
};
/** /**
* Finds an immediate child of the specified parent with the specified * Adds the specified call path, constructing nodes as necessary.
* label, creates a child node if necessary. If a parent node isn't *
* specified, uses tree root. * @param {Array<string>} path Call path.
* */
* @param {string} label Child node label. addPath(path) {
*/ if (path.length == 0) {
CallTree.prototype.findOrAddChild = function (label) { return;
return this.root_.findOrAddChild(label);
};
/**
* Creates a subtree by cloning and merging all subtrees rooted at nodes
* with a given label. E.g. cloning the following call tree on label 'A'
* will give the following result:
*
* <A>--<B> <B>
* / /
* <root> == clone on 'A' ==> <root>--<A>
* \ \
* <C>--<A>--<D> <D>
*
* And <A>'s selfWeight will be the sum of selfWeights of <A>'s from the
* source call tree.
*
* @param {string} label The label of the new root node.
*/
CallTree.prototype.cloneSubtree = function (label) {
var subTree = new CallTree();
this.traverse(function (node, parent) {
if (!parent && node.label != label) {
return null;
} }
var child = (parent ? parent : subTree).findOrAddChild(node.label); var curr = this.root_;
child.selfWeight += node.selfWeight; for (var i = 0; i < path.length; ++i) {
return child; curr = curr.findOrAddChild(path[i]);
}); }
return subTree; curr.selfWeight++;
}; this.totalsComputed_ = false;
/**
* Computes total weights in the call graph.
*/
CallTree.prototype.computeTotalWeights = function () {
if (this.totalsComputed_) {
return;
} }
this.root_.computeTotalWeight();
this.totalsComputed_ = true;
};
/**
* Finds an immediate child of the specified parent with the specified
* label, creates a child node if necessary. If a parent node isn't
* specified, uses tree root.
*
* @param {string} label Child node label.
*/
findOrAddChild(label) {
return this.root_.findOrAddChild(label);
}
/** /**
* Traverses the call graph in preorder. This function can be used for * Creates a subtree by cloning and merging all subtrees rooted at nodes
* building optionally modified tree clones. This is the boilerplate code * with a given label. E.g. cloning the following call tree on label 'A'
* for this scenario: * will give the following result:
* *
* callTree.traverse(function(node, parentClone) { * <A>--<B> <B>
* var nodeClone = cloneNode(node); * / /
* if (parentClone) * <root> == clone on 'A' ==> <root>--<A>
* parentClone.addChild(nodeClone); * \ \
* return nodeClone; * <C>--<A>--<D> <D>
* }); *
* * And <A>'s selfWeight will be the sum of selfWeights of <A>'s from the
* @param {function(CallTree.Node, *)} f Visitor function. * source call tree.
* The second parameter is the result of calling 'f' on the parent node. *
*/ * @param {string} label The label of the new root node.
CallTree.prototype.traverse = function (f) { */
var pairsToProcess = new ConsArray(); cloneSubtree(label) {
pairsToProcess.concat([{ node: this.root_, param: null }]); var subTree = new CallTree();
while (!pairsToProcess.atEnd()) { this.traverse((node, parent) => {
var pair = pairsToProcess.next(); if (!parent && node.label != label) {
var node = pair.node; return null;
var newParam = f(node, pair.param); }
var morePairsToProcess = []; var child = (parent ? parent : subTree).findOrAddChild(node.label);
node.forEachChild(function (child) { child.selfWeight += node.selfWeight;
morePairsToProcess.push({ node: child, param: newParam }); return child;
}); });
pairsToProcess.concat(morePairsToProcess); return subTree;
} }
};
/**
* Computes total weights in the call graph.
*/
computeTotalWeights() {
if (this.totalsComputed_) return;
this.root_.computeTotalWeight();
this.totalsComputed_ = true;
}
/** /**
* Performs an indepth call graph traversal. * Traverses the call graph in preorder. This function can be used for
* * building optionally modified tree clones. This is the boilerplate code
* @param {function(CallTree.Node)} enter A function called * for this scenario:
* prior to visiting node's children. *
* @param {function(CallTree.Node)} exit A function called * callTree.traverse(function(node, parentClone) {
* after visiting node's children. * var nodeClone = cloneNode(node);
*/ * if (parentClone)
CallTree.prototype.traverseInDepth = function (enter, exit) { * parentClone.addChild(nodeClone);
function traverse(node) { * return nodeClone;
enter(node); * });
node.forEachChild(traverse); *
exit(node); * @param {function(CallTreeNode, *)} f Visitor function.
* The second parameter is the result of calling 'f' on the parent node.
*/
traverse(f) {
var pairsToProcess = new ConsArray();
pairsToProcess.concat([{ node: this.root_, param: null }]);
while (!pairsToProcess.atEnd()) {
var pair = pairsToProcess.next();
var node = pair.node;
var newParam = f(node, pair.param);
var morePairsToProcess = [];
node.forEachChild((child) => {
morePairsToProcess.push({ node: child, param: newParam });
});
pairsToProcess.concat(morePairsToProcess);
}
} }
traverse(this.root_);
}; /**
* Performs an indepth call graph traversal.
*
* @param {function(CallTreeNode)} enter A function called
* prior to visiting node's children.
* @param {function(CallTreeNode)} exit A function called
* after visiting node's children.
*/
traverseInDepth(enter, exit) {
function traverse(node) {
enter(node);
node.forEachChild(traverse);
exit(node);
}
traverse(this.root_);
}
}
/** /**
* Constructs a call graph node. * Constructs a call graph node.
* *
* @param {string} label Node label. * @param {string} label Node label.
* @param {CallTree.Node} opt_parent Node parent. * @param {CallTreeNode} opt_parent Node parent.
*/
CallTree.Node = function (label, opt_parent) {
this.label = label;
this.parent = opt_parent;
this.children = {};
};
/**
* Node self weight (how many times this node was the last node in
* a call path).
* @type {number}
*/
CallTree.Node.prototype.selfWeight = 0;
/**
* Node total weight (includes weights of all children).
* @type {number}
*/ */
CallTree.Node.prototype.totalWeight = 0; class CallTreeNode {
/**
* Node self weight (how many times this node was the last node in
* a call path).
* @type {number}
*/
selfWeight = 0;
/**
* Node total weight (includes weights of all children).
* @type {number}
*/
totalWeight = 0;
children = {};
constructor(label, opt_parent) {
this.label = label;
this.parent = opt_parent;
}
/** /**
* Adds a child node. * Adds a child node.
* *
* @param {string} label Child node label. * @param {string} label Child node label.
*/ */
CallTree.Node.prototype.addChild = function (label) { addChild(label) {
var child = new CallTree.Node(label, this); var child = new CallTreeNode(label, this);
this.children[label] = child; this.children[label] = child;
return child; return child;
}; }
/** /**
* Computes node's total weight. * Computes node's total weight.
*/ */
CallTree.Node.prototype.computeTotalWeight = computeTotalWeight() {
function () {
var totalWeight = this.selfWeight; var totalWeight = this.selfWeight;
this.forEachChild(function (child) { this.forEachChild(function (child) {
totalWeight += child.computeTotalWeight(); totalWeight += child.computeTotalWeight();
}); });
return this.totalWeight = totalWeight; return this.totalWeight = totalWeight;
}; }
/**
* Returns all node's children as an array.
*/
CallTree.Node.prototype.exportChildren = function () {
var result = [];
this.forEachChild(function (node) { result.push(node); });
return result;
};
/**
* Finds an immediate child with the specified label.
*
* @param {string} label Child node label.
*/
CallTree.Node.prototype.findChild = function (label) {
return this.children[label] || null;
};
/**
* Finds an immediate child with the specified label, creates a child
* node if necessary.
*
* @param {string} label Child node label.
*/
CallTree.Node.prototype.findOrAddChild = function (label) {
return this.findChild(label) || this.addChild(label);
};
/**
* Returns all node's children as an array.
*/
exportChildren() {
var result = [];
this.forEachChild(function (node) { result.push(node); });
return result;
}
/** /**
* Calls the specified function for every child. * Finds an immediate child with the specified label.
* *
* @param {function(CallTree.Node)} f Visitor function. * @param {string} label Child node label.
*/ */
CallTree.Node.prototype.forEachChild = function (f) { findChild(label) {
for (var c in this.children) { return this.children[label] || null;
f(this.children[c]);
} }
};
/**
* Finds an immediate child with the specified label, creates a child
* node if necessary.
*
* @param {string} label Child node label.
*/
findOrAddChild(label) {
return this.findChild(label) || this.addChild(label);
}
/** /**
* Walks up from the current node up to the call tree root. * Calls the specified function for every child.
* *
* @param {function(CallTree.Node)} f Visitor function. * @param {function(CallTreeNode)} f Visitor function.
*/ */
CallTree.Node.prototype.walkUpToRoot = function (f) { forEachChild(f) {
for (var curr = this; curr != null; curr = curr.parent) { for (var c in this.children) {
f(curr); f(this.children[c]);
}
} }
};
/**
* Walks up from the current node up to the call tree root.
*
* @param {function(CallTreeNode)} f Visitor function.
*/
walkUpToRoot(f) {
for (var curr = this; curr != null; curr = curr.parent) {
f(curr);
}
}
/** /**
* Tries to find a node with the specified path. * Tries to find a node with the specified path.
* *
* @param {Array<string>} labels The path. * @param {Array<string>} labels The path.
* @param {function(CallTree.Node)} opt_f Visitor function. * @param {function(CallTreeNode)} opt_f Visitor function.
*/ */
CallTree.Node.prototype.descendToChild = function ( descendToChild(labels, opt_f) {
labels, opt_f) { for (var pos = 0, curr = this; pos < labels.length && curr != null; pos++) {
for (var pos = 0, curr = this; pos < labels.length && curr != null; pos++) { var child = curr.findChild(labels[pos]);
var child = curr.findChild(labels[pos]); if (opt_f) {
if (opt_f) { opt_f(child, pos);
opt_f(child, pos); }
curr = child;
} }
curr = child; return curr;
} }
return curr; }
};
export function JsonProfile() { export function JsonProfile() {
this.codeMap_ = new CodeMap(); this.codeMap_ = new CodeMap();
...@@ -937,7 +867,7 @@ export function JsonProfile() { ...@@ -937,7 +867,7 @@ export function JsonProfile() {
JsonProfile.prototype.addLibrary = function ( JsonProfile.prototype.addLibrary = function (
name, startAddr, endAddr) { name, startAddr, endAddr) {
var entry = new CodeMap.CodeEntry( var entry = new CodeEntry(
endAddr - startAddr, name, 'SHARED_LIB'); endAddr - startAddr, name, 'SHARED_LIB');
this.codeMap_.addLibrary(startAddr, entry); this.codeMap_.addLibrary(startAddr, entry);
...@@ -948,7 +878,7 @@ JsonProfile.prototype.addLibrary = function ( ...@@ -948,7 +878,7 @@ JsonProfile.prototype.addLibrary = function (
JsonProfile.prototype.addStaticCode = function ( JsonProfile.prototype.addStaticCode = function (
name, startAddr, endAddr) { name, startAddr, endAddr) {
var entry = new CodeMap.CodeEntry( var entry = new CodeEntry(
endAddr - startAddr, name, 'CPP'); endAddr - startAddr, name, 'CPP');
this.codeMap_.addStaticCode(startAddr, entry); this.codeMap_.addStaticCode(startAddr, entry);
...@@ -967,7 +897,7 @@ JsonProfile.prototype.addCode = function ( ...@@ -967,7 +897,7 @@ JsonProfile.prototype.addCode = function (
codeId = staticEntry.entry.codeId; codeId = staticEntry.entry.codeId;
} }
var entry = new CodeMap.CodeEntry(size, name, 'CODE'); var entry = new CodeEntry(size, name, 'CODE');
this.codeMap_.addCode(start, entry); this.codeMap_.addCode(start, entry);
entry.codeId = codeId; entry.codeId = codeId;
...@@ -987,7 +917,7 @@ JsonProfile.prototype.addFuncCode = function ( ...@@ -987,7 +917,7 @@ JsonProfile.prototype.addFuncCode = function (
// it is safe to put them in a single code map. // it is safe to put them in a single code map.
var func = this.codeMap_.findDynamicEntryByStartAddress(funcAddr); var func = this.codeMap_.findDynamicEntryByStartAddress(funcAddr);
if (!func) { if (!func) {
var func = new CodeMap.CodeEntry(0, name, 'SFI'); var func = new CodeEntry(0, name, 'SFI');
this.codeMap_.addCode(funcAddr, func); this.codeMap_.addCode(funcAddr, func);
func.funcId = this.functionEntries_.length; func.funcId = this.functionEntries_.length;
...@@ -1011,7 +941,7 @@ JsonProfile.prototype.addFuncCode = function ( ...@@ -1011,7 +941,7 @@ JsonProfile.prototype.addFuncCode = function (
} }
} }
if (!entry) { if (!entry) {
entry = new CodeMap.CodeEntry(size, name, 'JS'); entry = new CodeEntry(size, name, 'JS');
this.codeMap_.addCode(start, entry); this.codeMap_.addCode(start, entry);
entry.codeId = this.codeEntries_.length; entry.codeId = this.codeEntries_.length;
...@@ -1083,7 +1013,6 @@ JsonProfile.prototype.addScriptSource = function (id, url, source) { ...@@ -1083,7 +1013,6 @@ JsonProfile.prototype.addScriptSource = function (id, url, source) {
this.scripts_[id] = new Script(id, url, source); this.scripts_[id] = new Script(id, url, source);
}; };
JsonProfile.prototype.deoptCode = function ( JsonProfile.prototype.deoptCode = function (
timestamp, code, inliningId, scriptOffset, bailoutType, timestamp, code, inliningId, scriptOffset, bailoutType,
sourcePositionText, deoptReasonText) { sourcePositionText, deoptReasonText) {
......
...@@ -36,31 +36,30 @@ export function inherits(childCtor, parentCtor) { ...@@ -36,31 +36,30 @@ export function inherits(childCtor, parentCtor) {
}; };
function V8Profile(separateIc, separateBytecodes, separateBuiltins, class V8Profile extends Profile {
separateStubs) { static IC_RE =
Profile.call(this); /^(LoadGlobalIC: )|(Handler: )|(?:CallIC|LoadIC|StoreIC)|(?:Builtin: (?:Keyed)?(?:Load|Store)IC_)/;
var regexps = []; static BYTECODES_RE = /^(BytecodeHandler: )/;
if (!separateIc) regexps.push(V8Profile.IC_RE); static BUILTINS_RE = /^(Builtin: )/;
if (!separateBytecodes) regexps.push(V8Profile.BYTECODES_RE); static STUBS_RE = /^(Stub: )/;
if (!separateBuiltins) regexps.push(V8Profile.BUILTINS_RE);
if (!separateStubs) regexps.push(V8Profile.STUBS_RE); constructor(separateIc, separateBytecodes, separateBuiltins, separateStubs) {
if (regexps.length > 0) { super();
this.skipThisFunction = function(name) { var regexps = [];
for (var i=0; i<regexps.length; i++) { if (!separateIc) regexps.push(V8Profile.IC_RE);
if (regexps[i].test(name)) return true; if (!separateBytecodes) regexps.push(V8Profile.BYTECODES_RE);
} if (!separateBuiltins) regexps.push(V8Profile.BUILTINS_RE);
return false; if (!separateStubs) regexps.push(V8Profile.STUBS_RE);
}; if (regexps.length > 0) {
this.skipThisFunction = function(name) {
for (var i=0; i<regexps.length; i++) {
if (regexps[i].test(name)) return true;
}
return false;
};
}
} }
}; }
inherits(V8Profile, Profile);
V8Profile.IC_RE =
/^(LoadGlobalIC: )|(Handler: )|(?:CallIC|LoadIC|StoreIC)|(?:Builtin: (?:Keyed)?(?:Load|Store)IC_)/;
V8Profile.BYTECODES_RE = /^(BytecodeHandler: )/
V8Profile.BUILTINS_RE = /^(Builtin: )/
V8Profile.STUBS_RE = /^(Stub: )/
/** /**
......
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