[tools] Migrate more tools to ES6 classes

For simplicity this CL includes a first crude conversion of
tickprocessor.mjs. Later CLs will introduce more ES6 syntax and clean
up more code.

Bug: v8:10667
Change-Id: Ief2ca623f5562114fb976a95d156e2ab3f961114
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2611252
Reviewed-by: Sathya Gunasekaran  <gsathya@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72013}
This commit is contained in:
Camillo Bruni 2021-01-11 15:16:54 +01:00 committed by Commit Bot
parent 73875e9585
commit d5d45c611a
5 changed files with 589 additions and 617 deletions

View File

@ -36,49 +36,47 @@
*
* @constructor
*/
export function ConsArray() {
this.tail_ = new ConsArray.Cell(null, null);
this.currCell_ = this.tail_;
this.currCellPos_ = 0;
};
/**
* Concatenates another array for iterating. Empty arrays are ignored.
* This operation can be safely performed during ongoing ConsArray
* iteration.
*
* @param {Array} arr Array to concatenate.
*/
ConsArray.prototype.concat = function(arr) {
if (arr.length > 0) {
this.tail_.data = arr;
this.tail_ = this.tail_.next = new ConsArray.Cell(null, null);
}
};
/**
* Whether the end of iteration is reached.
*/
ConsArray.prototype.atEnd = function() {
return this.currCell_ === null ||
this.currCell_.data === null ||
this.currCellPos_ >= this.currCell_.data.length;
};
/**
* Returns the current item, moves to the next one.
*/
ConsArray.prototype.next = function() {
const result = this.currCell_.data[this.currCellPos_++];
if (this.currCellPos_ >= this.currCell_.data.length) {
this.currCell_ = this.currCell_.next;
export class ConsArray {
constructor() {
this.tail_ = new ConsArrayCell(null, null);
this.currCell_ = this.tail_;
this.currCellPos_ = 0;
}
return result;
};
/**
* Concatenates another array for iterating. Empty arrays are ignored.
* This operation can be safely performed during ongoing ConsArray
* iteration.
*
* @param {Array} arr Array to concatenate.
*/
concat(arr) {
if (arr.length > 0) {
this.tail_.data = arr;
this.tail_ = this.tail_.next = new ConsArrayCell(null, null);
}
}
/**
* Whether the end of iteration is reached.
*/
atEnd() {
return this.currCell_ === null ||
this.currCell_.data === null ||
this.currCellPos_ >= this.currCell_.data.length;
}
/**
* Returns the current item, moves to the next one.
*/
next() {
const result = this.currCell_.data[this.currCellPos_++];
if (this.currCellPos_ >= this.currCell_.data.length) {
this.currCell_ = this.currCell_.next;
this.currCellPos_ = 0;
}
return result;
}
}
/**
@ -86,7 +84,9 @@ ConsArray.prototype.next = function() {
*
* @constructor
*/
ConsArray.Cell = function(data, next) {
this.data = data;
this.next = next;
};
class ConsArrayCell {
constructor(data, next) {
this.data = data;
this.next = next;
}
}

View File

@ -5,10 +5,9 @@
import { LogReader, parseString } from "./logreader.mjs";
import { CodeMap, CodeEntry } from "./codemap.mjs";
export {
ArgumentsProcessor, UnixCppEntriesProvider,
ArgumentsProcessor, UnixCppEntriesProvider,
WindowsCppEntriesProvider, MacCppEntriesProvider,
} from "./tickprocessor.mjs";
import { inherits } from "./tickprocessor.mjs";
export class CppProcessor extends LogReader {
@ -29,7 +28,7 @@ export class CppProcessor extends LogReader {
*/
printError(str) {
print(str);
};
}
processLogFile(fileName) {
this.lastLogFileName_ = fileName;
@ -37,14 +36,14 @@ export class CppProcessor extends LogReader {
while (line = readline()) {
this.processLogLine(line);
}
};
}
processLogFileInTest(fileName) {
// Hack file name to avoid dealing with platform specifics.
this.lastLogFileName_ = 'v8.log';
const contents = readFile(fileName);
this.processLogChunk(contents);
};
}
processSharedLibrary(name, startAddr, endAddr, aslrSlide) {
const self = this;
@ -53,7 +52,7 @@ export class CppProcessor extends LogReader {
const entry = new CodeEntry(fEnd - fStart, fName, 'CPP');
self.codeMap_.addStaticCode(fStart, entry);
});
};
}
dumpCppSymbols() {
const staticEntries = this.codeMap_.getAllStaticEntriesWithAddresses();

View File

@ -28,9 +28,13 @@
/**
* @fileoverview Log Reader is used to process log file produced by V8.
*/
import { CsvParser } from "./csvparser.mjs";
// Parses dummy variable for readability;
export const parseString = 'parse-string';
export const parseVarArgs = 'parse-var-args';
/**
* Base class for processing log files.
*
@ -41,206 +45,200 @@
* markers.
* @constructor
*/
export function LogReader(dispatchTable, timedRange, pairwiseTimedRange) {
/**
* @type {Array.<Object>}
*/
this.dispatchTable_ = dispatchTable;
export class LogReader {
constructor (dispatchTable, timedRange, pairwiseTimedRange) {
/**
* @type {Array.<Object>}
*/
this.dispatchTable_ = dispatchTable;
/**
* @type {boolean}
*/
this.timedRange_ = timedRange;
/**
* @type {boolean}
*/
this.timedRange_ = timedRange;
/**
* @type {boolean}
*/
this.pairwiseTimedRange_ = pairwiseTimedRange;
if (pairwiseTimedRange) {
this.timedRange_ = true;
/**
* @type {boolean}
*/
this.pairwiseTimedRange_ = pairwiseTimedRange;
if (pairwiseTimedRange) {
this.timedRange_ = true;
}
/**
* Current line.
* @type {number}
*/
this.lineNum_ = 0;
/**
* CSV lines parser.
* @type {CsvParser}
*/
this.csvParser_ = new CsvParser();
/**
* Keeps track of whether we've seen a "current-time" tick yet.
* @type {boolean}
*/
this.hasSeenTimerMarker_ = false;
/**
* List of log lines seen since last "current-time" tick.
* @type {Array.<String>}
*/
this.logLinesSinceLastTimerMarker_ = [];
}
/**
* Current line.
* @type {number}
* Used for printing error messages.
*
* @param {string} str Error message.
*/
this.lineNum_ = 0;
/**
* CSV lines parser.
* @type {CsvParser}
*/
this.csvParser_ = new CsvParser();
/**
* Keeps track of whether we've seen a "current-time" tick yet.
* @type {boolean}
*/
this.hasSeenTimerMarker_ = false;
/**
* List of log lines seen since last "current-time" tick.
* @type {Array.<String>}
*/
this.logLinesSinceLastTimerMarker_ = [];
};
/**
* Used for printing error messages.
*
* @param {string} str Error message.
*/
LogReader.prototype.printError = function(str) {
// Do nothing.
};
/**
* Processes a portion of V8 profiler event log.
*
* @param {string} chunk A portion of log.
*/
LogReader.prototype.processLogChunk = function(chunk) {
this.processLog_(chunk.split('\n'));
};
/**
* Processes a line of V8 profiler event log.
*
* @param {string} line A line of log.
*/
LogReader.prototype.processLogLine = function(line) {
if (!this.timedRange_) {
this.processLogLine_(line);
return;
printError(str) {
// Do nothing.
}
if (line.startsWith("current-time")) {
if (this.hasSeenTimerMarker_) {
this.processLog_(this.logLinesSinceLastTimerMarker_);
this.logLinesSinceLastTimerMarker_ = [];
// In pairwise mode, a "current-time" line ends the timed range.
if (this.pairwiseTimedRange_) {
this.hasSeenTimerMarker_ = false;
/**
* Processes a portion of V8 profiler event log.
*
* @param {string} chunk A portion of log.
*/
processLogChunk(chunk) {
this.processLog_(chunk.split('\n'));
}
/**
* Processes a line of V8 profiler event log.
*
* @param {string} line A line of log.
*/
processLogLine(line) {
if (!this.timedRange_) {
this.processLogLine_(line);
return;
}
if (line.startsWith("current-time")) {
if (this.hasSeenTimerMarker_) {
this.processLog_(this.logLinesSinceLastTimerMarker_);
this.logLinesSinceLastTimerMarker_ = [];
// In pairwise mode, a "current-time" line ends the timed range.
if (this.pairwiseTimedRange_) {
this.hasSeenTimerMarker_ = false;
}
} else {
this.hasSeenTimerMarker_ = true;
}
} else {
this.hasSeenTimerMarker_ = true;
}
} else {
if (this.hasSeenTimerMarker_) {
this.logLinesSinceLastTimerMarker_.push(line);
} else if (!line.startsWith("tick")) {
this.processLogLine_(line);
}
}
};
/**
* Processes stack record.
*
* @param {number} pc Program counter.
* @param {number} func JS Function.
* @param {Array.<string>} stack String representation of a stack.
* @return {Array.<number>} Processed stack.
*/
LogReader.prototype.processStack = function(pc, func, stack) {
const fullStack = func ? [pc, func] : [pc];
let prevFrame = pc;
for (let i = 0, n = stack.length; i < n; ++i) {
const frame = stack[i];
const firstChar = frame.charAt(0);
if (firstChar == '+' || firstChar == '-') {
// An offset from the previous frame.
prevFrame += parseInt(frame, 16);
fullStack.push(prevFrame);
// Filter out possible 'overflow' string.
} else if (firstChar != 'o') {
fullStack.push(parseInt(frame, 16));
} else {
this.printError(`dropping: ${frame}`);
}
}
return fullStack;
};
/**
* Returns whether a particular dispatch must be skipped.
*
* @param {!Object} dispatch Dispatch record.
* @return {boolean} True if dispatch must be skipped.
*/
LogReader.prototype.skipDispatch = dispatch => false;
// Parses dummy variable for readability;
export const parseString = 'parse-string';
export const parseVarArgs = 'parse-var-args';
/**
* Does a dispatch of a log record.
*
* @param {Array.<string>} fields Log record.
* @private
*/
LogReader.prototype.dispatchLogRow_ = function(fields) {
// Obtain the dispatch.
const command = fields[0];
const dispatch = this.dispatchTable_[command];
if (dispatch === undefined) return;
if (dispatch === null || this.skipDispatch(dispatch)) {
return;
}
// Parse fields.
const parsedFields = [];
for (let i = 0; i < dispatch.parsers.length; ++i) {
const parser = dispatch.parsers[i];
if (parser === parseString) {
parsedFields.push(fields[1 + i]);
} else if (typeof parser == 'function') {
parsedFields.push(parser(fields[1 + i]));
} else if (parser === parseVarArgs) {
// var-args
parsedFields.push(fields.slice(1 + i));
break;
} else {
throw new Error(`Invalid log field parser: ${parser}`);
if (this.hasSeenTimerMarker_) {
this.logLinesSinceLastTimerMarker_.push(line);
} else if (!line.startsWith("tick")) {
this.processLogLine_(line);
}
}
}
// Run the processor.
dispatch.processor.apply(this, parsedFields);
};
/**
* Processes stack record.
*
* @param {number} pc Program counter.
* @param {number} func JS Function.
* @param {Array.<string>} stack String representation of a stack.
* @return {Array.<number>} Processed stack.
*/
processStack(pc, func, stack) {
const fullStack = func ? [pc, func] : [pc];
let prevFrame = pc;
for (let i = 0, n = stack.length; i < n; ++i) {
const frame = stack[i];
const firstChar = frame.charAt(0);
if (firstChar == '+' || firstChar == '-') {
// An offset from the previous frame.
prevFrame += parseInt(frame, 16);
fullStack.push(prevFrame);
// Filter out possible 'overflow' string.
} else if (firstChar != 'o') {
fullStack.push(parseInt(frame, 16));
} else {
this.printError(`dropping: ${frame}`);
}
}
return fullStack;
}
/**
* Returns whether a particular dispatch must be skipped.
*
* @param {!Object} dispatch Dispatch record.
* @return {boolean} True if dispatch must be skipped.
*/
skipDispatch(dispatch) {
return false;
}
/**
* Processes log lines.
*
* @param {Array.<string>} lines Log lines.
* @private
*/
LogReader.prototype.processLog_ = function(lines) {
for (let i = 0, n = lines.length; i < n; ++i) {
this.processLogLine_(lines[i]);
/**
* Does a dispatch of a log record.
*
* @param {Array.<string>} fields Log record.
* @private
*/
dispatchLogRow_(fields) {
// Obtain the dispatch.
const command = fields[0];
const dispatch = this.dispatchTable_[command];
if (dispatch === undefined) return;
if (dispatch === null || this.skipDispatch(dispatch)) {
return;
}
// Parse fields.
const parsedFields = [];
for (let i = 0; i < dispatch.parsers.length; ++i) {
const parser = dispatch.parsers[i];
if (parser === parseString) {
parsedFields.push(fields[1 + i]);
} else if (typeof parser == 'function') {
parsedFields.push(parser(fields[1 + i]));
} else if (parser === parseVarArgs) {
// var-args
parsedFields.push(fields.slice(1 + i));
break;
} else {
throw new Error(`Invalid log field parser: ${parser}`);
}
}
// Run the processor.
dispatch.processor.apply(this, parsedFields);
}
/**
* Processes log lines.
*
* @param {Array.<string>} lines Log lines.
* @private
*/
processLog_(lines) {
for (let i = 0, n = lines.length; i < n; ++i) {
this.processLogLine_(lines[i]);
}
}
/**
* Processes a single log line.
*
* @param {String} a log line
* @private
*/
processLogLine_(line) {
if (line.length > 0) {
try {
const fields = this.csvParser_.parseLine(line);
this.dispatchLogRow_(fields);
} catch (e) {
this.printError(`line ${this.lineNum_ + 1}: ${e.message || e}\n${e.stack}`);
}
}
this.lineNum_++;
}
}
/**
* Processes a single log line.
*
* @param {String} a log line
* @private
*/
LogReader.prototype.processLogLine_ = function(line) {
if (line.length > 0) {
try {
const fields = this.csvParser_.parseLine(line);
this.dispatchLogRow_(fields);
} catch (e) {
this.printError(`line ${this.lineNum_ + 1}: ${e.message || e}\n${e.stack}`);
}
}
this.lineNum_++;
};

View File

@ -34,274 +34,250 @@
*
* @constructor
*/
export function SplayTree() {
};
export class SplayTree {
/**
* Pointer to the root node of the tree.
*
* @type {SplayTreeNode}
* @private
*/
root_ = null;
/**
* Pointer to the root node of the tree.
*
* @type {SplayTree.Node}
* @private
*/
SplayTree.prototype.root_ = null;
/**
* @return {boolean} Whether the tree is empty.
*/
SplayTree.prototype.isEmpty = function() {
return !this.root_;
};
/**
* Inserts a node into the tree with the specified key and value if
* the tree does not already contain a node with the specified key. If
* the value is inserted, it becomes the root of the tree.
*
* @param {number} key Key to insert into the tree.
* @param {*} value Value to insert into the tree.
*/
SplayTree.prototype.insert = function(key, value) {
if (this.isEmpty()) {
this.root_ = new SplayTree.Node(key, value);
return;
/**
* @return {boolean} Whether the tree is empty.
*/
isEmpty() {
return !this.root_;
}
// Splay on the key to move the last node on the search path for
// the key to the root of the tree.
this.splay_(key);
if (this.root_.key == key) {
return;
}
const node = new SplayTree.Node(key, value);
if (key > this.root_.key) {
node.left = this.root_;
node.right = this.root_.right;
this.root_.right = null;
} else {
node.right = this.root_;
node.left = this.root_.left;
this.root_.left = null;
}
this.root_ = node;
};
/**
* Removes a node with the specified key from the tree if the tree
* contains a node with this key. The removed node is returned. If the
* key is not found, an exception is thrown.
*
* @param {number} key Key to find and remove from the tree.
* @return {SplayTree.Node} The removed node.
*/
SplayTree.prototype.remove = function(key) {
if (this.isEmpty()) {
throw Error(`Key not found: ${key}`);
}
this.splay_(key);
if (this.root_.key != key) {
throw Error(`Key not found: ${key}`);
}
const removed = this.root_;
if (!this.root_.left) {
this.root_ = this.root_.right;
} else {
const { right } = this.root_;
this.root_ = this.root_.left;
// Splay to make sure that the new root has an empty right child.
/**
* Inserts a node into the tree with the specified key and value if
* the tree does not already contain a node with the specified key. If
* the value is inserted, it becomes the root of the tree.
*
* @param {number} key Key to insert into the tree.
* @param {*} value Value to insert into the tree.
*/
insert(key, value) {
if (this.isEmpty()) {
this.root_ = new SplayTreeNode(key, value);
return;
}
// Splay on the key to move the last node on the search path for
// the key to the root of the tree.
this.splay_(key);
// Insert the original right child as the right child of the new
// root.
this.root_.right = right;
if (this.root_.key == key) return;
const node = new SplayTreeNode(key, value);
if (key > this.root_.key) {
node.left = this.root_;
node.right = this.root_.right;
this.root_.right = null;
} else {
node.right = this.root_;
node.left = this.root_.left;
this.root_.left = null;
}
this.root_ = node;
}
return removed;
};
/**
* Returns the node having the specified key or null if the tree doesn't contain
* a node with the specified key.
*
* @param {number} key Key to find in the tree.
* @return {SplayTree.Node} Node having the specified key.
*/
SplayTree.prototype.find = function(key) {
if (this.isEmpty()) {
return null;
/**
* Removes a node with the specified key from the tree if the tree
* contains a node with this key. The removed node is returned. If the
* key is not found, an exception is thrown.
*
* @param {number} key Key to find and remove from the tree.
* @return {SplayTreeNode} The removed node.
*/
remove(key) {
if (this.isEmpty()) {
throw Error(`Key not found: ${key}`);
}
this.splay_(key);
if (this.root_.key != key) {
throw Error(`Key not found: ${key}`);
}
const removed = this.root_;
if (!this.root_.left) {
this.root_ = this.root_.right;
} else {
const { right } = this.root_;
this.root_ = this.root_.left;
// Splay to make sure that the new root has an empty right child.
this.splay_(key);
// Insert the original right child as the right child of the new
// root.
this.root_.right = right;
}
return removed;
}
this.splay_(key);
return this.root_.key == key ? this.root_ : null;
};
/**
* @return {SplayTree.Node} Node having the minimum key value.
*/
SplayTree.prototype.findMin = function() {
if (this.isEmpty()) {
return null;
/**
* Returns the node having the specified key or null if the tree doesn't contain
* a node with the specified key.
*
* @param {number} key Key to find in the tree.
* @return {SplayTreeNode} Node having the specified key.
*/
find(key) {
if (this.isEmpty()) return null;
this.splay_(key);
return this.root_.key == key ? this.root_ : null;
}
let current = this.root_;
while (current.left) {
current = current.left;
/**
* @return {SplayTreeNode} Node having the minimum key value.
*/
findMin() {
if (this.isEmpty()) return null;
let current = this.root_;
while (current.left) {
current = current.left;
}
return current;
}
return current;
};
/**
* @return {SplayTree.Node} Node having the maximum key value.
*/
SplayTree.prototype.findMax = function(opt_startNode) {
if (this.isEmpty()) {
return null;
/**
* @return {SplayTreeNode} Node having the maximum key value.
*/
findMax(opt_startNode) {
if (this.isEmpty()) return null;
let current = opt_startNode || this.root_;
while (current.right) {
current = current.right;
}
return current;
}
let current = opt_startNode || this.root_;
while (current.right) {
current = current.right;
/**
* @return {SplayTreeNode} Node having the maximum key value that
* is less or equal to the specified key value.
*/
findGreatestLessThan(key) {
if (this.isEmpty()) return null;
// Splay on the key to move the node with the given key or the last
// node on the search path to the top of the tree.
this.splay_(key);
// Now the result is either the root node or the greatest node in
// the left subtree.
if (this.root_.key <= key) {
return this.root_;
} else if (this.root_.left) {
return this.findMax(this.root_.left);
} else {
return null;
}
}
return current;
};
/**
* @return {SplayTree.Node} Node having the maximum key value that
* is less or equal to the specified key value.
*/
SplayTree.prototype.findGreatestLessThan = function(key) {
if (this.isEmpty()) {
return null;
/**
* @return {Array<*>} An array containing all the values of tree's nodes paired
* with keys.
*/
exportKeysAndValues() {
const result = [];
this.traverse_(function(node) { result.push([node.key, node.value]); });
return result;
}
// Splay on the key to move the node with the given key or the last
// node on the search path to the top of the tree.
this.splay_(key);
// Now the result is either the root node or the greatest node in
// the left subtree.
if (this.root_.key <= key) {
return this.root_;
} else if (this.root_.left) {
return this.findMax(this.root_.left);
} else {
return null;
/**
* @return {Array<*>} An array containing all the values of tree's nodes.
*/
exportValues() {
const result = [];
this.traverse_(function(node) { result.push(node.value); });
return result;
}
};
/**
* @return {Array<*>} An array containing all the values of tree's nodes paired
* with keys.
*/
SplayTree.prototype.exportKeysAndValues = function() {
const result = [];
this.traverse_(function(node) { result.push([node.key, node.value]); });
return result;
};
/**
* @return {Array<*>} An array containing all the values of tree's nodes.
*/
SplayTree.prototype.exportValues = function() {
const result = [];
this.traverse_(function(node) { result.push(node.value); });
return result;
};
/**
* Perform the splay operation for the given key. Moves the node with
* the given key to the top of the tree. If no node has the given
* key, the last node on the search path is moved to the top of the
* tree. This is the simplified top-down splaying algorithm from:
* "Self-adjusting Binary Search Trees" by Sleator and Tarjan
*
* @param {number} key Key to splay the tree on.
* @private
*/
SplayTree.prototype.splay_ = function(key) {
if (this.isEmpty()) {
return;
}
// Create a dummy node. The use of the dummy node is a bit
// counter-intuitive: The right child of the dummy node will hold
// the L tree of the algorithm. The left child of the dummy node
// will hold the R tree of the algorithm. Using a dummy node, left
// and right will always be nodes and we avoid special cases.
let dummy, left, right;
dummy = left = right = new SplayTree.Node(null, null);
let current = this.root_;
while (true) {
if (key < current.key) {
if (!current.left) {
break;
}
if (key < current.left.key) {
// Rotate right.
const tmp = current.left;
current.left = tmp.right;
tmp.right = current;
current = tmp;
/**
* Perform the splay operation for the given key. Moves the node with
* the given key to the top of the tree. If no node has the given
* key, the last node on the search path is moved to the top of the
* tree. This is the simplified top-down splaying algorithm from:
* "Self-adjusting Binary Search Trees" by Sleator and Tarjan
*
* @param {number} key Key to splay the tree on.
* @private
*/
splay_(key) {
if (this.isEmpty()) return;
// Create a dummy node. The use of the dummy node is a bit
// counter-intuitive: The right child of the dummy node will hold
// the L tree of the algorithm. The left child of the dummy node
// will hold the R tree of the algorithm. Using a dummy node, left
// and right will always be nodes and we avoid special cases.
let dummy, left, right;
dummy = left = right = new SplayTreeNode(null, null);
let current = this.root_;
while (true) {
if (key < current.key) {
if (!current.left) {
break;
}
}
// Link right.
right.left = current;
right = current;
current = current.left;
} else if (key > current.key) {
if (!current.right) {
break;
}
if (key > current.right.key) {
// Rotate left.
const tmp = current.right;
current.right = tmp.left;
tmp.left = current;
current = tmp;
if (key < current.left.key) {
// Rotate right.
const tmp = current.left;
current.left = tmp.right;
tmp.right = current;
current = tmp;
if (!current.left) {
break;
}
}
// Link right.
right.left = current;
right = current;
current = current.left;
} else if (key > current.key) {
if (!current.right) {
break;
}
if (key > current.right.key) {
// Rotate left.
const tmp = current.right;
current.right = tmp.left;
tmp.left = current;
current = tmp;
if (!current.right) {
break;
}
}
// Link left.
left.right = current;
left = current;
current = current.right;
} else {
break;
}
// Link left.
left.right = current;
left = current;
current = current.right;
} else {
break;
}
// Assemble.
left.right = current.left;
right.left = current.right;
current.left = dummy.right;
current.right = dummy.left;
this.root_ = current;
}
/**
* Performs a preorder traversal of the tree.
*
* @param {function(SplayTreeNode)} f Visitor function.
* @private
*/
traverse_(f) {
const nodesToVisit = [this.root_];
while (nodesToVisit.length > 0) {
const node = nodesToVisit.shift();
if (node == null) {
continue;
}
f(node);
nodesToVisit.push(node.left);
nodesToVisit.push(node.right);
}
}
// Assemble.
left.right = current.left;
right.left = current.right;
current.left = dummy.right;
current.right = dummy.left;
this.root_ = current;
};
/**
* Performs a preorder traversal of the tree.
*
* @param {function(SplayTree.Node)} f Visitor function.
* @private
*/
SplayTree.prototype.traverse_ = function(f) {
const nodesToVisit = [this.root_];
while (nodesToVisit.length > 0) {
const node = nodesToVisit.shift();
if (node == null) {
continue;
}
f(node);
nodesToVisit.push(node.left);
nodesToVisit.push(node.right);
}
};
}
/**
* Constructs a Splay tree node.
@ -309,19 +285,17 @@ SplayTree.prototype.traverse_ = function(f) {
* @param {number} key Key.
* @param {*} value Value.
*/
SplayTree.Node = function(key, value) {
this.key = key;
this.value = value;
};
/**
* @type {SplayTree.Node}
*/
SplayTree.Node.prototype.left = null;
/**
* @type {SplayTree.Node}
*/
SplayTree.Node.prototype.right = null;
class SplayTreeNode {
constructor(key, value) {
this.key = key;
this.value = value;
/**
* @type {SplayTreeNode}
*/
this.left = null;
/**
* @type {SplayTreeNode}
*/
this.right = null;
}
};

View File

@ -31,11 +31,6 @@ import { Profile, JsonProfile } from "./profile.mjs";
import { ViewBuilder } from "./profile_view.mjs";
export function inherits(childCtor, parentCtor) {
childCtor.prototype.__proto__ = parentCtor.prototype;
};
class V8Profile extends Profile {
static IC_RE =
/^(LoadGlobalIC: )|(Handler: )|(?:CallIC|LoadIC|StoreIC)|(?:Builtin: (?:Keyed)?(?:Load|Store)IC_)/;
@ -75,7 +70,8 @@ export function readFile(fileName) {
}
export function TickProcessor(
export class TickProcessor extends LogReader {
constructor(
cppEntriesProvider,
separateIc,
separateBytecodes,
@ -92,8 +88,10 @@ export function TickProcessor(
onlySummary,
runtimeTimerFilter,
preprocessJson) {
this.preprocessJson = preprocessJson;
LogReader.call(this, {
super({},
timedRange,
pairwiseTimedRange);
this.dispatchTable_ = {
'shared-library': { parsers: [parseString, parseInt, parseInt, parseInt],
processor: this.processSharedLibrary },
'code-creation': {
@ -142,10 +140,10 @@ export function TickProcessor(
// Obsolete row types.
'code-allocate': null,
'begin-code-region': null,
'end-code-region': null },
timedRange,
pairwiseTimedRange);
'end-code-region': null
};
this.preprocessJson = preprocessJson;
this.cppEntriesProvider_ = cppEntriesProvider;
this.callGraphSize_ = callGraphSize;
this.ignoreUnknown_ = ignoreUnknown;
@ -201,11 +199,10 @@ export function TickProcessor(
this.generation_ = 1;
this.currentProducerProfile_ = null;
this.onlySummary_ = onlySummary;
};
inherits(TickProcessor, LogReader);
}
TickProcessor.VmStates = {
static VmStates = {
JS: 0,
GC: 1,
PARSER: 2,
@ -217,7 +214,7 @@ TickProcessor.VmStates = {
};
TickProcessor.CodeTypes = {
static CodeTypes = {
CPP: 0,
SHARED_LIB: 1
};
@ -225,56 +222,56 @@ TickProcessor.CodeTypes = {
// codeTypes_ map because there can be zillions of them.
TickProcessor.CALL_PROFILE_CUTOFF_PCT = 1.0;
static CALL_PROFILE_CUTOFF_PCT = 1.0;
TickProcessor.CALL_GRAPH_SIZE = 5;
static CALL_GRAPH_SIZE = 5;
/**
* @override
*/
TickProcessor.prototype.printError = function(str) {
printError(str) {
printErr(str);
};
}
TickProcessor.prototype.setCodeType = function(name, type) {
setCodeType(name, type) {
this.codeTypes_[name] = TickProcessor.CodeTypes[type];
};
}
TickProcessor.prototype.isSharedLibrary = function(name) {
isSharedLibrary(name) {
return this.codeTypes_[name] == TickProcessor.CodeTypes.SHARED_LIB;
};
}
TickProcessor.prototype.isCppCode = function(name) {
isCppCode(name) {
return this.codeTypes_[name] == TickProcessor.CodeTypes.CPP;
};
}
TickProcessor.prototype.isJsCode = function(name) {
isJsCode(name) {
return name !== "UNKNOWN" && !(name in this.codeTypes_);
};
}
TickProcessor.prototype.processLogFile = function(fileName) {
processLogFile(fileName) {
this.lastLogFileName_ = fileName;
let line;
while (line = readline()) {
this.processLogLine(line);
}
};
}
TickProcessor.prototype.processLogFileInTest = function(fileName) {
processLogFileInTest(fileName) {
// Hack file name to avoid dealing with platform specifics.
this.lastLogFileName_ = 'v8.log';
const contents = readFile(fileName);
this.processLogChunk(contents);
};
}
TickProcessor.prototype.processSharedLibrary = function(
processSharedLibrary(
name, startAddr, endAddr, aslrSlide) {
const entry = this.profile_.addLibrary(name, startAddr, endAddr, aslrSlide);
this.setCodeType(entry.getName(), 'SHARED_LIB');
@ -285,10 +282,10 @@ TickProcessor.prototype.processSharedLibrary = function(
self.profile_.addStaticCode(fName, fStart, fEnd);
self.setCodeType(fName, 'CPP');
});
};
}
TickProcessor.prototype.processCodeCreation = function(
processCodeCreation(
type, kind, timestamp, start, size, name, maybe_func) {
if (maybe_func.length) {
const funcAddr = parseInt(maybe_func[0]);
@ -297,55 +294,55 @@ TickProcessor.prototype.processCodeCreation = function(
} else {
this.profile_.addCode(type, name, timestamp, start, size);
}
};
}
TickProcessor.prototype.processCodeDeopt = function(
processCodeDeopt(
timestamp, size, code, inliningId, scriptOffset, bailoutType,
sourcePositionText, deoptReasonText) {
this.profile_.deoptCode(timestamp, code, inliningId, scriptOffset,
bailoutType, sourcePositionText, deoptReasonText);
};
}
TickProcessor.prototype.processCodeMove = function(from, to) {
processCodeMove(from, to) {
this.profile_.moveCode(from, to);
};
}
TickProcessor.prototype.processCodeDelete = function(start) {
processCodeDelete(start) {
this.profile_.deleteCode(start);
};
}
TickProcessor.prototype.processCodeSourceInfo = function(
processCodeSourceInfo(
start, script, startPos, endPos, sourcePositions, inliningPositions,
inlinedFunctions) {
this.profile_.addSourcePositions(start, script, startPos,
endPos, sourcePositions, inliningPositions, inlinedFunctions);
};
}
TickProcessor.prototype.processScriptSource = function(script, url, source) {
processScriptSource(script, url, source) {
this.profile_.addScriptSource(script, url, source);
};
}
TickProcessor.prototype.processFunctionMove = function(from, to) {
processFunctionMove(from, to) {
this.profile_.moveFunc(from, to);
};
}
TickProcessor.prototype.includeTick = function(vmState) {
includeTick(vmState) {
if (this.stateFilter_ !== null) {
return this.stateFilter_ == vmState;
} else if (this.runtimeTimerFilter_ !== null) {
return this.currentRuntimeTimer == this.runtimeTimerFilter_;
}
return true;
};
}
TickProcessor.prototype.processRuntimeTimerEvent = function(name) {
processRuntimeTimerEvent(name) {
this.currentRuntimeTimer = name;
}
TickProcessor.prototype.processTick = function(pc,
processTick(pc,
ns_since_start,
is_external_callback,
tos_or_external_callback,
@ -381,21 +378,21 @@ TickProcessor.prototype.processTick = function(pc,
this.profile_.recordTick(
ns_since_start, vmState,
this.processStack(pc, tos_or_external_callback, stack));
};
}
TickProcessor.prototype.advanceDistortion = function() {
advanceDistortion() {
this.distortion += this.distortion_per_entry;
}
TickProcessor.prototype.processHeapSampleBegin = function(space, state, ticks) {
processHeapSampleBegin(space, state, ticks) {
if (space != 'Heap') return;
this.currentProducerProfile_ = new CallTree();
};
}
TickProcessor.prototype.processHeapSampleEnd = function(space, state) {
processHeapSampleEnd(space, state) {
if (space != 'Heap' || !this.currentProducerProfile_) return;
print(`Generation ${this.generation_}:`);
@ -410,10 +407,10 @@ TickProcessor.prototype.processHeapSampleEnd = function(space, state) {
this.currentProducerProfile_ = null;
this.generation_++;
};
}
TickProcessor.prototype.printStatistics = function() {
printStatistics() {
if (this.preprocessJson) {
this.profile_.writeJson();
return;
@ -492,29 +489,16 @@ TickProcessor.prototype.printStatistics = function() {
(rec2.internalFuncName < rec1.internalFuncName ? -1 : 1) );
this.printHeavyProfile(heavyView.head.children);
}
};
}
function padLeft(s, len) {
s = s.toString();
if (s.length < len) {
const padLength = len - s.length;
if (!(padLength in padLeft)) {
padLeft[padLength] = new Array(padLength + 1).join(' ');
}
s = padLeft[padLength] + s;
}
return s;
};
TickProcessor.prototype.printHeader = function(headerTitle) {
printHeader(headerTitle) {
print(`\n [${headerTitle}]:`);
print(' ticks total nonlib name');
};
}
TickProcessor.prototype.printLine = function(
printLine(
entry, ticks, totalTicks, nonLibTicks) {
const pct = ticks * 100 / totalTicks;
const nonLibPct = nonLibTicks != null
@ -526,7 +510,7 @@ TickProcessor.prototype.printLine = function(
entry);
}
TickProcessor.prototype.printHeavyProfHeader = function() {
printHeavyProfHeader() {
print('\n [Bottom up (heavy) profile]:');
print(' Note: percentage shows a share of a particular caller in the ' +
'total\n' +
@ -535,10 +519,10 @@ TickProcessor.prototype.printHeavyProfHeader = function() {
TickProcessor.CALL_PROFILE_CUTOFF_PCT.toFixed(1) +
'% are not shown.\n');
print(' ticks parent name');
};
}
TickProcessor.prototype.processProfile = function(
processProfile(
profile, filterP, func) {
for (let i = 0, n = profile.length; i < n; ++i) {
const rec = profile[i];
@ -549,7 +533,7 @@ TickProcessor.prototype.processProfile = function(
}
};
TickProcessor.prototype.getLineAndColumn = function(name) {
getLineAndColumn(name) {
const re = /:([0-9]+):([0-9]+)$/;
const array = re.exec(name);
if (!array) {
@ -558,12 +542,12 @@ TickProcessor.prototype.getLineAndColumn = function(name) {
return {line: array[1], column: array[2]};
}
TickProcessor.prototype.hasSourceMap = function() {
hasSourceMap() {
return this.sourceMap != null;
};
}
TickProcessor.prototype.formatFunctionName = function(funcName) {
formatFunctionName(funcName) {
if (!this.hasSourceMap()) {
return funcName;
}
@ -580,9 +564,9 @@ TickProcessor.prototype.formatFunctionName = function(funcName) {
const sourceColumn = entry[4] + 1;
return sourceFile + ':' + sourceLine + ':' + sourceColumn + ' -> ' + funcName;
};
}
TickProcessor.prototype.printEntries = function(
printEntries(
profile, totalTicks, nonLibTicks, filterP, callback, printAllTicks) {
const that = this;
this.processProfile(profile, filterP, function (rec) {
@ -593,10 +577,9 @@ TickProcessor.prototype.printEntries = function(
that.printLine(funcName, rec.selfTime, totalTicks, nonLibTicks);
}
});
};
}
TickProcessor.prototype.printHeavyProfile = function(profile, opt_indent) {
printHeavyProfile(profile, opt_indent) {
const self = this;
const indent = opt_indent || 0;
const indentStr = padLeft('', indent);
@ -616,14 +599,27 @@ TickProcessor.prototype.printHeavyProfile = function(profile, opt_indent) {
print('');
}
});
}
}
function padLeft(s, len) {
s = s.toString();
if (s.length < len) {
const padLength = len - s.length;
if (!(padLength in padLeft)) {
padLeft[padLength] = new Array(padLength + 1).join(' ');
}
s = padLeft[padLength] + s;
}
return s;
};
function CppEntriesProvider() {
};
class CppEntriesProvider {
CppEntriesProvider.prototype.parseVmSymbols = function(
parseVmSymbols(
libName, libStart, libEnd, libASLRSlide, processorFunc) {
this.loadSymbols(libName);
@ -686,17 +682,20 @@ CppEntriesProvider.prototype.parseVmSymbols = function(
addEntry(funcInfo);
}
addEntry({name: '', start: libEnd});
};
}
CppEntriesProvider.prototype.loadSymbols = function(libName) {
};
loadSymbols(libName) {
}
parseNextLine() { return false }
}
CppEntriesProvider.prototype.parseNextLine = () => false;
export function UnixCppEntriesProvider(nmExec, objdumpExec, targetRootFS, apkEmbeddedLibrary) {
export class UnixCppEntriesProvider extends CppEntriesProvider {
constructor(nmExec, objdumpExec, targetRootFS, apkEmbeddedLibrary) {
super();
this.symbols = [];
// File offset of a symbol minus the virtual address of a symbol found in
// the symbol table.
@ -707,11 +706,10 @@ export function UnixCppEntriesProvider(nmExec, objdumpExec, targetRootFS, apkEmb
this.targetRootFS = targetRootFS;
this.apkEmbeddedLibrary = apkEmbeddedLibrary;
this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ([0-9a-fA-F]{8,16} )?[tTwW] (.*)$/;
};
inherits(UnixCppEntriesProvider, CppEntriesProvider);
}
UnixCppEntriesProvider.prototype.loadSymbols = function(libName) {
loadSymbols(libName) {
this.parsePos = 0;
if (this.apkEmbeddedLibrary && libName.endsWith('.apk')) {
libName = this.apkEmbeddedLibrary;
@ -737,10 +735,10 @@ UnixCppEntriesProvider.prototype.loadSymbols = function(libName) {
// If the library cannot be found on this system let's not panic.
this.symbols = ['', ''];
}
};
}
UnixCppEntriesProvider.prototype.parseNextLine = function() {
parseNextLine() {
if (this.symbols.length == 0) {
return false;
}
@ -762,18 +760,18 @@ UnixCppEntriesProvider.prototype.parseNextLine = function() {
}
}
return funcInfo;
};
}
}
export function MacCppEntriesProvider(nmExec, objdumpExec, targetRootFS, apkEmbeddedLibrary) {
UnixCppEntriesProvider.call(this, nmExec, objdumpExec, targetRootFS, apkEmbeddedLibrary);
export class MacCppEntriesProvider extends UnixCppEntriesProvider {
constructor(nmExec, objdumpExec, targetRootFS, apkEmbeddedLibrary) {
super(nmExec, objdumpExec, targetRootFS, apkEmbeddedLibrary);
// Note an empty group. It is required, as UnixCppEntriesProvider expects 3 groups.
this.FUNC_RE = /^([0-9a-fA-F]{8,16})() (.*)$/;
};
inherits(MacCppEntriesProvider, UnixCppEntriesProvider);
}
MacCppEntriesProvider.prototype.loadSymbols = function(libName) {
loadSymbols(libName) {
this.parsePos = 0;
libName = this.targetRootFS + libName;
@ -785,34 +783,36 @@ MacCppEntriesProvider.prototype.loadSymbols = function(libName) {
// If the library cannot be found on this system let's not panic.
this.symbols = '';
}
};
}
}
export function WindowsCppEntriesProvider(_ignored_nmExec, _ignored_objdumpExec, targetRootFS,
export class WindowsCppEntriesProvider extends CppEntriesProvider {
constructor(_ignored_nmExec, _ignored_objdumpExec, targetRootFS,
_ignored_apkEmbeddedLibrary) {
super();
this.targetRootFS = targetRootFS;
this.symbols = '';
this.parsePos = 0;
};
inherits(WindowsCppEntriesProvider, CppEntriesProvider);
WindowsCppEntriesProvider.FILENAME_RE = /^(.*)\.([^.]+)$/;
static FILENAME_RE = /^(.*)\.([^.]+)$/;
WindowsCppEntriesProvider.FUNC_RE =
static FUNC_RE =
/^\s+0001:[0-9a-fA-F]{8}\s+([_\?@$0-9a-zA-Z]+)\s+([0-9a-fA-F]{8}).*$/;
WindowsCppEntriesProvider.IMAGE_BASE_RE =
static IMAGE_BASE_RE =
/^\s+0000:00000000\s+___ImageBase\s+([0-9a-fA-F]{8}).*$/;
// This is almost a constant on Windows.
WindowsCppEntriesProvider.EXE_IMAGE_BASE = 0x00400000;
static EXE_IMAGE_BASE = 0x00400000;
WindowsCppEntriesProvider.prototype.loadSymbols = function(libName) {
loadSymbols(libName) {
libName = this.targetRootFS + libName;
const fileNameFields = libName.match(WindowsCppEntriesProvider.FILENAME_RE);
if (!fileNameFields) return;
@ -827,7 +827,7 @@ WindowsCppEntriesProvider.prototype.loadSymbols = function(libName) {
};
WindowsCppEntriesProvider.prototype.parseNextLine = function() {
parseNextLine() {
const lineEndPos = this.symbols.indexOf('\r\n', this.parsePos);
if (lineEndPos == -1) {
return false;
@ -862,14 +862,15 @@ WindowsCppEntriesProvider.prototype.parseNextLine = function() {
*
* ?LookupInDescriptor@JSObject@internal@v8@@...arguments info...
*/
WindowsCppEntriesProvider.prototype.unmangleName = function(name) {
unmangleName(name) {
// Empty or non-mangled name.
if (name.length < 1 || name.charAt(0) != '?') return name;
const nameEndPos = name.indexOf('@@');
const components = name.substring(1, nameEndPos).split('@');
components.reverse();
return components.join('::');
};
}
}
export class ArgumentsProcessor extends BaseArgumentsProcessor {