Commit 91a60a4f authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[tools] Harden and speed up map-processor

- avoid endless recursion with corrupted traces
- speed up page by async bar repainting
- minor tweaks to avoid unnecessary work
- move helper functions to make command line version parse log files

Change-Id: If8ce9cc4093030d648fbc7bbb60e53412e9f7a79
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2115434
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran  <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66942}
parent a1bd7227
...@@ -46,14 +46,14 @@ class CsvParser { ...@@ -46,14 +46,14 @@ class CsvParser {
while (nextPos !== -1) { while (nextPos !== -1) {
let escapeIdentifier = string.charAt(nextPos + 1); let escapeIdentifier = string.charAt(nextPos + 1);
pos = nextPos + 2; pos = nextPos + 2;
if (escapeIdentifier == 'n') { if (escapeIdentifier === 'n') {
result += '\n'; result += '\n';
nextPos = pos; nextPos = pos;
} else if (escapeIdentifier == '\\') { } else if (escapeIdentifier === '\\') {
result += '\\'; result += '\\';
nextPos = pos; nextPos = pos;
} else { } else {
if (escapeIdentifier == 'x') { if (escapeIdentifier === 'x') {
// \x00 ascii range escapes consume 2 chars. // \x00 ascii range escapes consume 2 chars.
nextPos = pos + 2; nextPos = pos + 2;
} else { } else {
...@@ -71,7 +71,7 @@ class CsvParser { ...@@ -71,7 +71,7 @@ class CsvParser {
// If there are no more escape sequences consume the rest of the string. // If there are no more escape sequences consume the rest of the string.
if (nextPos === -1) { if (nextPos === -1) {
result += string.substr(pos); result += string.substr(pos);
} else if (pos != nextPos) { } else if (pos !== nextPos) {
result += string.substring(pos, nextPos); result += string.substring(pos, nextPos);
} }
} }
......
...@@ -190,7 +190,6 @@ LogReader.prototype.dispatchLogRow_ = function(fields) { ...@@ -190,7 +190,6 @@ LogReader.prototype.dispatchLogRow_ = function(fields) {
var command = fields[0]; var command = fields[0];
var dispatch = this.dispatchTable_[command]; var dispatch = this.dispatchTable_[command];
if (dispatch === undefined) return; if (dispatch === undefined) return;
if (dispatch === null || this.skipDispatch(dispatch)) { if (dispatch === null || this.skipDispatch(dispatch)) {
return; return;
} }
...@@ -241,7 +240,7 @@ LogReader.prototype.processLogLine_ = function(line) { ...@@ -241,7 +240,7 @@ LogReader.prototype.processLogLine_ = function(line) {
var fields = this.csvParser_.parseLine(line); var fields = this.csvParser_.parseLine(line);
this.dispatchLogRow_(fields); this.dispatchLogRow_(fields);
} catch (e) { } catch (e) {
this.printError('line ' + (this.lineNum_ + 1) + ': ' + (e.message || e)); this.printError('line ' + (this.lineNum_ + 1) + ': ' + (e.message || e) + '\n' + e.stack);
} }
} }
this.lineNum_++; this.lineNum_++;
......
...@@ -452,19 +452,6 @@ function tr() { ...@@ -452,19 +452,6 @@ function tr() {
return document.createElement("tr"); return document.createElement("tr");
} }
function define(prototype, name, fn) {
Object.defineProperty(prototype, name, {value:fn, enumerable:false});
}
define(Array.prototype, "max", function(fn) {
if (this.length == 0) return undefined;
if (fn == undefined) fn = (each) => each;
let max = fn(this[0]);
for (let i = 1; i < this.length; i++) {
max = Math.max(max, fn(this[i]));
}
return max;
})
define(Array.prototype, "histogram", function(mapFn) { define(Array.prototype, "histogram", function(mapFn) {
let histogram = []; let histogram = [];
for (let i = 0; i < this.length; i++) { for (let i = 0; i < this.length; i++) {
...@@ -483,9 +470,6 @@ define(Array.prototype, "histogram", function(mapFn) { ...@@ -483,9 +470,6 @@ define(Array.prototype, "histogram", function(mapFn) {
return histogram; return histogram;
}); });
define(Array.prototype, "first", function() { return this[0] });
define(Array.prototype, "last", function() { return this[this.length - 1] });
// ========================================================================= // =========================================================================
// EventHandlers // EventHandlers
function handleBodyLoad() { function handleBodyLoad() {
...@@ -698,6 +682,7 @@ class View { ...@@ -698,6 +682,7 @@ class View {
timeNode.style.left = ((time-start) * timeToPixel) + "px"; timeNode.style.left = ((time-start) * timeToPixel) + "px";
chunksNode.appendChild(timeNode); chunksNode.appendChild(timeNode);
}; };
let backgroundTodo = [];
for (let i = 0; i < chunks.length; i++) { for (let i = 0; i < chunks.length; i++) {
let chunk = chunks[i]; let chunk = chunks[i];
let height = (chunk.size() / max * kChunkHeight); let height = (chunk.size() / max * kChunkHeight);
...@@ -711,10 +696,13 @@ class View { ...@@ -711,10 +696,13 @@ class View {
node.addEventListener("mousemove", e => this.handleChunkMouseMove(e)); node.addEventListener("mousemove", e => this.handleChunkMouseMove(e));
node.addEventListener("click", e => this.handleChunkClick(e)); node.addEventListener("click", e => this.handleChunkClick(e));
node.addEventListener("dblclick", e => this.handleChunkDoubleClick(e)); node.addEventListener("dblclick", e => this.handleChunkDoubleClick(e));
this.setTimelineChunkBackground(chunk, node); backgroundTodo.push([chunk, node])
chunksNode.appendChild(node); chunksNode.appendChild(node);
chunk.markers.forEach(marker => addTimestamp(marker.time, marker.name)); chunk.markers.forEach(marker => addTimestamp(marker.time, marker.name));
} }
this.asyncSetTimelineChunkBackground(backgroundTodo)
// Put a time marker roughly every 20 chunks. // Put a time marker roughly every 20 chunks.
let expected = duration / chunks.length * 20; let expected = duration / chunks.length * 20;
let interval = (10 ** Math.floor(Math.log10(expected))); let interval = (10 ** Math.floor(Math.log10(expected)));
...@@ -753,6 +741,22 @@ class View { ...@@ -753,6 +741,22 @@ class View {
this.transitionView.showMaps(chunk.getUniqueTransitions()); this.transitionView.showMaps(chunk.getUniqueTransitions());
} }
asyncSetTimelineChunkBackground(backgroundTodo) {
const kIncrement = 100;
let start = 0;
let delay = 1;
while (start < backgroundTodo.length) {
let end = Math.min(start+kIncrement, backgroundTodo.length);
setTimeout((from, to) => {
for (let i = from; i < to; i++) {
let [chunk, node] = backgroundTodo[i];
this.setTimelineChunkBackground(chunk, node);
}
}, delay++, start, end);
start = end;
}
}
setTimelineChunkBackground(chunk, node) { setTimelineChunkBackground(chunk, node) {
// Render the types of transitions as bar charts // Render the types of transitions as bar charts
const kHeight = chunk.height; const kHeight = chunk.height;
...@@ -779,7 +783,7 @@ class View { ...@@ -779,7 +783,7 @@ class View {
}); });
} }
let imageData = this.backgroundCanvas.toDataURL("image/png"); let imageData = this.backgroundCanvas.toDataURL("image/webp", 0.2);
node.style.backgroundImage = "url(" + imageData + ")"; node.style.backgroundImage = "url(" + imageData + ")";
} }
...@@ -818,7 +822,7 @@ class View { ...@@ -818,7 +822,7 @@ class View {
ctx.stroke(); ctx.stroke();
ctx.closePath(); ctx.closePath();
ctx.fill(); ctx.fill();
let imageData = canvas.toDataURL("image/png"); let imageData = canvas.toDataURL("image/webp", 0.2);
$("timelineOverview").style.backgroundImage = "url(" + imageData + ")"; $("timelineOverview").style.backgroundImage = "url(" + imageData + ")";
} }
...@@ -1122,11 +1126,16 @@ class StatsView { ...@@ -1122,11 +1126,16 @@ class StatsView {
if (color !== null) { if (color !== null) {
row.appendChild(td(div(['colorbox', color]))); row.appendChild(td(div(['colorbox', color])));
} else { } else {
row.appendChild(td("")); row.appendChild(td(""));
}
row.onclick = (e) => {
// lazily compute the stats
let node = e.target.parentNode;
if (node.maps == undefined) {
node.maps = this.timeline.filterUniqueTransitions(filter);
}
this.transitionView.showMaps(node.maps);
} }
row.maps = this.timeline.filterUniqueTransitions(filter);
row.onclick =
(e) => this.transitionView.showMaps(e.target.parentNode.maps);
row.appendChild(td(name)); row.appendChild(td(name));
let count = this.timeline.count(filter); let count = this.timeline.count(filter);
row.appendChild(td(count)); row.appendChild(td(count));
......
...@@ -3,10 +3,28 @@ ...@@ -3,10 +3,28 @@
// found in the LICENSE file. // found in the LICENSE file.
// =========================================================================== // ===========================================================================
function define(prototype, name, fn) {
Object.defineProperty(prototype, name, {value:fn, enumerable:false});
}
define(Array.prototype, "max", function(fn) {
if (this.length === 0) return undefined;
if (fn === undefined) fn = (each) => each;
let max = fn(this[0]);
for (let i = 1; i < this.length; i++) {
max = Math.max(max, fn(this[i]));
}
return max;
})
define(Array.prototype, "first", function() { return this[0] });
define(Array.prototype, "last", function() { return this[this.length - 1] });
// ===========================================================================
class MapProcessor extends LogReader { class MapProcessor extends LogReader {
constructor() { constructor() {
super(); super();
this.dispatchTable_ = { this.dispatchTable_ = {
__proto__:null,
'code-creation': { 'code-creation': {
parsers: [parseString, parseInt, parseInt, parseInt, parseInt, parsers: [parseString, parseInt, parseInt, parseInt, parseInt,
parseString, parseVarArgs], parseString, parseVarArgs],
...@@ -41,6 +59,7 @@ class MapProcessor extends LogReader { ...@@ -41,6 +59,7 @@ class MapProcessor extends LogReader {
}; };
this.profile_ = new Profile(); this.profile_ = new Profile();
this.timeline_ = new Timeline(); this.timeline_ = new Timeline();
this.formatPCRegexp_ = /(.*):[0-9]+:[0-9]+$/;
} }
printError(str) { printError(str) {
...@@ -73,9 +92,15 @@ class MapProcessor extends LogReader { ...@@ -73,9 +92,15 @@ class MapProcessor extends LogReader {
processLogFile(fileName) { processLogFile(fileName) {
this.collectEntries = true this.collectEntries = true
this.lastLogFileName_ = fileName; this.lastLogFileName_ = fileName;
let i = 1;
let line; let line;
while (line = readline()) { try {
this.processLogLine(line); while (line = readline()) {
this.processLogLine(line);
i++;
}
} catch(e) {
console.error("Error occurred during parsing line " + i + ", trying to continue: " + e);
} }
return this.finalize(); return this.finalize();
} }
...@@ -131,13 +156,12 @@ class MapProcessor extends LogReader { ...@@ -131,13 +156,12 @@ class MapProcessor extends LogReader {
formatPC(pc, line, column) { formatPC(pc, line, column) {
let entry = this.profile_.findEntry(pc); let entry = this.profile_.findEntry(pc);
if (!entry) return "<unknown>" if (!entry) return "<unknown>"
if (entry.type == "Builtin") { if (entry.type === "Builtin") {
return entry.name; return entry.name;
} }
let name = entry.func.getName(); let name = entry.func.getName();
let re = /(.*):[0-9]+:[0-9]+$/; let array = this.formatPCRegexp_.exec(name);
let array = re.exec(name); if (array === null) {
if (!array) {
entry = name; entry = name;
} else { } else {
entry = entry.getState() + array[1]; entry = entry.getState() + array[1];
...@@ -146,12 +170,12 @@ class MapProcessor extends LogReader { ...@@ -146,12 +170,12 @@ class MapProcessor extends LogReader {
} }
processMap(type, time, from, to, pc, line, column, reason, name) { processMap(type, time, from, to, pc, line, column, reason, name) {
time = parseInt(time); let time_ = parseInt(time);
if (type == "Deprecate") return this.deprecateMap(type, time, from); if (type === "Deprecate") return this.deprecateMap(type, time_, from);
from = this.getExistingMap(from, time); let from_ = this.getExistingMap(from, time_);
to = this.getExistingMap(to, time); let to_ = this.getExistingMap(to, time_);
let edge = new Edge(type, name, reason, time, from, to); let edge = new Edge(type, name, reason, time, from_, to_);
to.filePosition = this.formatPC(pc, line, column); to_.filePosition = this.formatPC(pc, line, column);
edge.finishSetup(); edge.finishSetup();
} }
...@@ -170,7 +194,7 @@ class MapProcessor extends LogReader { ...@@ -170,7 +194,7 @@ class MapProcessor extends LogReader {
//TODO(cbruni): fix initial map logging. //TODO(cbruni): fix initial map logging.
let map = this.getExistingMap(id, time); let map = this.getExistingMap(id, time);
if (!map.description) { if (!map.description) {
map.description = string; //map.description = string;
} }
} }
...@@ -212,19 +236,30 @@ class V8Map { ...@@ -212,19 +236,30 @@ class V8Map {
this.filePosition = ""; this.filePosition = "";
} }
finalizeRootMap(id) {
let stack = [this];
while (stack.length > 0) {
let current = stack.pop();
if (current.leftId !== 0) {
console.error("Skipping potential parent loop between maps:", current)
continue;
}
current.finalize(id)
id += 1;
current.children.forEach(edge => stack.push(edge.to))
// TODO implement rightId
}
return id;
}
finalize(id) { finalize(id) {
// Initialize preorder tree traversal Ids for fast subtree inclusion checks // Initialize preorder tree traversal Ids for fast subtree inclusion checks
if (id <= 0) throw "invalid id"; if (id <= 0) throw "invalid id";
let currentId = id; let currentId = id;
this.leftId = currentId this.leftId = currentId
this.children.forEach(edge => {
let map = edge.to;
currentId = map.finalize(currentId + 1);
});
this.rightId = currentId + 1;
return currentId + 1;
} }
parent() { parent() {
if (this.edge === void 0) return void 0; if (this.edge === void 0) return void 0;
return this.edge.from; return this.edge.from;
...@@ -239,7 +274,7 @@ class V8Map { ...@@ -239,7 +274,7 @@ class V8Map {
} }
isRoot() { isRoot() {
return this.edge == void 0 || this.edge.from == void 0; return this.edge === void 0 || this.edge.from === void 0;
} }
contains(map) { contains(map) {
...@@ -300,16 +335,16 @@ class V8Map { ...@@ -300,16 +335,16 @@ class V8Map {
} }
static get(id) { static get(id) {
if (!this.cache) return undefined;
return this.cache.get(id); return this.cache.get(id);
} }
static set(id, map) { static set(id, map) {
if (!this.cache) this.cache = new Map();
this.cache.set(id, map); this.cache.set(id, map);
} }
} }
V8Map.cache = new Map();
// =========================================================================== // ===========================================================================
class Edge { class Edge {
...@@ -323,21 +358,21 @@ class Edge { ...@@ -323,21 +358,21 @@ class Edge {
} }
finishSetup() { finishSetup() {
if (this.from) this.from.addEdge(this); let from = this.from
if (this.to) { if (from) from.addEdge(this);
this.to.edge = this; let to = this.to;
if (this.to === this.from) throw "From and to must be distinct."; if (to === undefined) return;
if (this.from) { to.edge = this;
if (this.to.time < this.from.time) { if (from === undefined ) return;
console.error("invalid time order"); if (to === from) throw "From and to must be distinct.";
} if (to.time < from.time) {
let newDepth = this.from.depth + 1; console.error("invalid time order");
if (this.to.depth > 0 && this.to.depth != newDepth) {
console.error("Depth has already been initialized");
}
this.to.depth = newDepth;
}
} }
let newDepth = from.depth + 1;
if (to.depth > 0 && to.depth != newDepth) {
console.error("Depth has already been initialized");
}
to.depth = newDepth;
} }
chunkIndex(chunks) { chunkIndex(chunks) {
...@@ -471,16 +506,16 @@ class Timeline { ...@@ -471,16 +506,16 @@ class Timeline {
finalize() { finalize() {
let id = 0; let id = 0;
this.forEach(map => { this.forEach(map => {
if (map.isRoot()) id = map.finalize(id + 1); if (map.isRoot()) id = map.finalizeRootMap(id + 1);
if (map.edge && map.edge.name) { if (map.edge && map.edge.name) {
let edge = map.edge; let edge = map.edge;
let list = this.transitions.get(edge.name); let list = this.transitions.get(edge.name);
if (list === undefined) { if (list === undefined) {
this.transitions.set(edge.name, [edge]); this.transitions.set(edge.name, [edge]);
} else { } else {
list.push(edge); list.push(edge);
}
} }
}
}); });
this.markers.sort((a, b) => b.time - a.time); this.markers.sort((a, b) => b.time - a.time);
} }
...@@ -490,7 +525,7 @@ class Timeline { ...@@ -490,7 +525,7 @@ class Timeline {
} }
isEmpty() { isEmpty() {
return this.size() == 0 return this.size() === 0
} }
size() { size() {
...@@ -573,7 +608,7 @@ class Timeline { ...@@ -573,7 +608,7 @@ class Timeline {
count(filter) { count(filter) {
return this.values.reduce((sum, each) => { return this.values.reduce((sum, each) => {
return sum + (filter(each) ? 1 : 0); return sum + (filter(each) === true ? 1 : 0);
}, 0); }, 0);
} }
...@@ -584,10 +619,10 @@ class Timeline { ...@@ -584,10 +619,10 @@ class Timeline {
filterUniqueTransitions(filter) { filterUniqueTransitions(filter) {
// Returns a list of Maps whose parent is not in the list. // Returns a list of Maps whose parent is not in the list.
return this.values.filter(map => { return this.values.filter(map => {
if (!filter(map)) return false; if (filter(map) === false) return false;
let parent = map.parent(); let parent = map.parent();
if (!parent) return true; if (parent === undefined) return true;
return !filter(parent); return filter(parent) === false;
}); });
} }
...@@ -617,7 +652,7 @@ class Chunk { ...@@ -617,7 +652,7 @@ class Chunk {
} }
isEmpty() { isEmpty() {
return this.items.length == 0; return this.items.length === 0;
} }
last() { last() {
...@@ -662,7 +697,7 @@ class Chunk { ...@@ -662,7 +697,7 @@ class Chunk {
findChunk(chunks, delta) { findChunk(chunks, delta) {
let i = this.index + delta; let i = this.index + delta;
let chunk = chunks[i]; let chunk = chunks[i];
while (chunk && chunk.size() == 0) { while (chunk && chunk.size() === 0) {
i += delta; i += delta;
chunk = chunks[i] chunk = chunks[i]
} }
......
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