[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:
parent
73875e9585
commit
d5d45c611a
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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_++;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user