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