Index: tools/tickprocessor.js |
diff --git a/tools/tickprocessor.js b/tools/tickprocessor.js |
index f1a11ccc948808d92eed172a4072b31d824957a6..66b0e68ed1fd1a59f104048a2b061c6d4b4c7094 100644 |
--- a/tools/tickprocessor.js |
+++ b/tools/tickprocessor.js |
@@ -30,7 +30,6 @@ function inherits(childCtor, parentCtor) { |
childCtor.prototype.__proto__ = parentCtor.prototype; |
}; |
- |
Jakob Kummerow
2013/08/23 13:08:35
nit: leave this line in please.
Daniel Kurka
2013/08/23 13:31:43
Done.
|
function V8Profile(separateIc) { |
Profile.call(this); |
if (!separateIc) { |
@@ -153,7 +152,8 @@ function TickProcessor( |
stateFilter, |
snapshotLogProcessor, |
distortion, |
- range) { |
+ range, |
+ sourceMap) { |
LogReader.call(this, { |
'shared-library': { parsers: [null, parseInt, parseInt], |
processor: this.processSharedLibrary }, |
@@ -196,6 +196,7 @@ function TickProcessor( |
this.ignoreUnknown_ = ignoreUnknown; |
this.stateFilter_ = stateFilter; |
this.snapshotLogProcessor_ = snapshotLogProcessor; |
+ this.sourceMap = sourceMap; |
this.deserializedEntriesNames_ = []; |
var ticks = this.ticks_ = |
{ total: 0, unaccounted: 0, excluded: 0, gc: 0 }; |
@@ -544,17 +545,52 @@ TickProcessor.prototype.processProfile = function( |
} |
}; |
+TickProcessor.prototype.getLineAndColumn = function(name) { |
+ var re = /:([0-9]+):([0-9]+)$/; |
+ var array = re.exec(name); |
+ if (!array) { |
+ return null; |
Jakob Kummerow
2013/08/23 13:08:35
nit: indentation
Daniel Kurka
2013/08/23 13:31:43
Done.
|
+ } |
+ return {line: array[1], column: array[2]}; |
+} |
+ |
+TickProcessor.prototype.hasSourceMap = function() { |
+ return this.sourceMap != null; |
+}; |
+ |
+ |
+TickProcessor.prototype.formatFunctionName = function(funcName) { |
+ if (!this.hasSourceMap()) { |
+ return funcName; |
+ } |
+ var lc = this.getLineAndColumn(funcName); |
+ if (lc == null) { |
+ return funcName; |
+ } |
+ // in source maps lines and columns are zero based |
+ var lineNumber = lc.line - 1; |
+ var column = lc.column - 1; |
+ var entry = this.sourceMap.findEntry(lineNumber, column); |
+ var sourceFile = entry[2]; |
+ var sourceLine = entry[3] + 1; |
+ var sourceColumn = entry[4] + 1; |
+ |
+ return sourceFile + ':' + sourceLine + ':' + sourceColumn + ' -> ' + funcName; |
+}; |
TickProcessor.prototype.printEntries = function( |
profile, nonLibTicks, filterP) { |
+ var that = this; |
this.processProfile(profile, filterP, function (rec) { |
if (rec.selfTime == 0) return; |
var nonLibPct = nonLibTicks != null ? |
rec.selfTime * 100.0 / nonLibTicks : 0.0; |
+ var funcName = that.formatFunctionName(rec.internalFuncName); |
+ |
print(' ' + padLeft(rec.selfTime, 5) + ' ' + |
padLeft(rec.selfPercent.toFixed(1), 5) + '% ' + |
padLeft(nonLibPct.toFixed(1), 5) + '% ' + |
- rec.internalFuncName); |
+ funcName); |
}); |
}; |
@@ -566,9 +602,10 @@ TickProcessor.prototype.printHeavyProfile = function(profile, opt_indent) { |
this.processProfile(profile, function() { return true; }, function (rec) { |
// Cut off too infrequent callers. |
if (rec.parentTotalPercent < TickProcessor.CALL_PROFILE_CUTOFF_PCT) return; |
+ var funcName = self.formatFunctionName(rec.internalFuncName); |
print(' ' + padLeft(rec.totalTime, 5) + ' ' + |
padLeft(rec.parentTotalPercent.toFixed(1), 5) + '% ' + |
- indentStr + rec.internalFuncName); |
+ indentStr + funcName); |
// Limit backtrace depth. |
if (indent < 2 * self.callGraphSize_) { |
self.printHeavyProfile(rec.children, indent + 2); |
@@ -825,7 +862,9 @@ function ArgumentsProcessor(args) { |
'--range': ['range', 'auto,auto', |
'Specify the range limit as [start],[end]'], |
'--distortion': ['distortion', 0, |
- 'Specify the logging overhead in picoseconds'] |
+ 'Specify the logging overhead in picoseconds'], |
+ '--sourceMap': ['sourceMap', null, |
Jakob Kummerow
2013/08/23 13:08:35
For consistency with other arg names, let's call t
Daniel Kurka
2013/08/23 13:31:43
Done.
|
+ 'Specify the source map that should be used for output'] |
Jakob Kummerow
2013/08/23 13:08:35
nit: indentation
Daniel Kurka
2013/08/23 13:31:43
Done.
|
}; |
this.argsDispatch_['--js'] = this.argsDispatch_['-j']; |
this.argsDispatch_['--gc'] = this.argsDispatch_['-g']; |
@@ -911,3 +950,241 @@ ArgumentsProcessor.prototype.printUsageAndExit = function() { |
quit(2); |
}; |
+ |
+/** |
+ * Implements Source Map V3 model. See http://code.google.com/p/closure-compiler/wiki/SourceMaps |
Jakob Kummerow
2013/08/23 13:08:35
This section looks a lot like http://src.chromium.
Daniel Kurka
2013/08/23 13:31:43
Actually we only need a modified load method
The
|
+ * for format description. |
+ * @constructor |
+ * @param {string} sourceMappingURL |
+ * @param {SourceMapV3} payload |
+ */ |
+SourceMap = function(sourceMappingURL, payload) |
+{ |
+ if (!SourceMap.prototype._base64Map) { |
+ const base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
+ SourceMap.prototype._base64Map = {}; |
+ for (var i = 0; i < base64Digits.length; ++i) |
+ SourceMap.prototype._base64Map[base64Digits.charAt(i)] = i; |
+ } |
+ |
+ this._sourceMappingURL = sourceMappingURL; |
+ this._reverseMappingsBySourceURL = {}; |
+ this._mappings = []; |
+ this._sources = {}; |
+ this._sourceContentByURL = {}; |
+ this._parseMappingPayload(payload); |
+} |
+ |
+/** |
+ * @param {string} sourceMapURL |
+ */ |
+SourceMap.load = function(sourceMapURL) |
+{ |
+ var content = readFile(sourceMapURL); |
+ var sourceMapObject = /** @type {SourceMapV3} */ (JSON.parse(content)); |
+ return new SourceMap(sourceMapURL, sourceMapObject); |
+} |
+ |
+SourceMap.prototype = { |
+ /** |
+ * @return {Array.<string>} |
+ */ |
+ sources: function() |
+ { |
+ return Object.keys(this._sources); |
+ }, |
+ |
+ /** |
+ * @param {SourceMapV3} mappingPayload |
+ */ |
+ _parseMappingPayload: function(mappingPayload) |
+ { |
+ if (mappingPayload.sections) |
+ this._parseSections(mappingPayload.sections); |
+ else |
+ this._parseMap(mappingPayload, 0, 0); |
+ }, |
+ |
+ /** |
+ * @param {Array.<SourceMapV3.Section>} sections |
+ */ |
+ _parseSections: function(sections) |
+ { |
+ for (var i = 0; i < sections.length; ++i) { |
+ var section = sections[i]; |
+ this._parseMap(section.map, section.offset.line, section.offset.column); |
+ } |
+ }, |
+ |
+ /** |
+ * @param {number} lineNumber in compiled resource |
+ * @param {number} columnNumber in compiled resource |
+ * @return {?Array} |
+ */ |
+ findEntry: function(lineNumber, columnNumber) |
+ { |
+ var first = 0; |
+ var count = this._mappings.length; |
+ while (count > 1) { |
+ var step = count >> 1; |
+ var middle = first + step; |
+ var mapping = this._mappings[middle]; |
+ if (lineNumber < mapping[0] || (lineNumber === mapping[0] && columnNumber < mapping[1])) |
+ count = step; |
+ else { |
+ first = middle; |
+ count -= step; |
+ } |
+ } |
+ var entry = this._mappings[first]; |
+ if (!first && entry && (lineNumber < entry[0] || (lineNumber === entry[0] && columnNumber < entry[1]))) |
+ return null; |
+ return entry; |
+ }, |
+ |
+ /** |
+ * @override |
+ */ |
+ _parseMap: function(map, lineNumber, columnNumber) |
+ { |
+ var sourceIndex = 0; |
+ var sourceLineNumber = 0; |
+ var sourceColumnNumber = 0; |
+ var nameIndex = 0; |
+ |
+ var sources = []; |
+ var originalToCanonicalURLMap = {}; |
+ for (var i = 0; i < map.sources.length; ++i) { |
+ var originalSourceURL = map.sources[i]; |
+ var sourceRoot = map.sourceRoot || ""; |
+ if (sourceRoot && !sourceRoot.endsWith("/")) |
+ sourceRoot += "/"; |
+ var href = sourceRoot + originalSourceURL; |
+ var url = href; |
+ originalToCanonicalURLMap[originalSourceURL] = url; |
+ sources.push(url); |
+ this._sources[url] = true; |
+ |
+ if (map.sourcesContent && map.sourcesContent[i]) |
+ this._sourceContentByURL[url] = map.sourcesContent[i]; |
+ } |
+ |
+ var stringCharIterator = new SourceMap.StringCharIterator(map.mappings); |
+ var sourceURL = sources[sourceIndex]; |
+ |
+ while (true) { |
+ if (stringCharIterator.peek() === ",") |
+ stringCharIterator.next(); |
+ else { |
+ while (stringCharIterator.peek() === ";") { |
+ lineNumber += 1; |
+ columnNumber = 0; |
+ stringCharIterator.next(); |
+ } |
+ if (!stringCharIterator.hasNext()) |
+ break; |
+ } |
+ |
+ columnNumber += this._decodeVLQ(stringCharIterator); |
+ if (this._isSeparator(stringCharIterator.peek())) { |
+ this._mappings.push([lineNumber, columnNumber]); |
+ continue; |
+ } |
+ |
+ var sourceIndexDelta = this._decodeVLQ(stringCharIterator); |
+ if (sourceIndexDelta) { |
+ sourceIndex += sourceIndexDelta; |
+ sourceURL = sources[sourceIndex]; |
+ } |
+ sourceLineNumber += this._decodeVLQ(stringCharIterator); |
+ sourceColumnNumber += this._decodeVLQ(stringCharIterator); |
+ if (!this._isSeparator(stringCharIterator.peek())) |
+ nameIndex += this._decodeVLQ(stringCharIterator); |
+ |
+ this._mappings.push([lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber]); |
+ } |
+ |
+ for (var i = 0; i < this._mappings.length; ++i) { |
+ var mapping = this._mappings[i]; |
+ var url = mapping[2]; |
+ if (!url) |
+ continue; |
+ if (!this._reverseMappingsBySourceURL[url]) |
+ this._reverseMappingsBySourceURL[url] = []; |
+ var reverseMappings = this._reverseMappingsBySourceURL[url]; |
+ var sourceLine = mapping[3]; |
+ if (!reverseMappings[sourceLine]) |
+ reverseMappings[sourceLine] = [mapping[0], mapping[1]]; |
+ } |
+ }, |
+ |
+ /** |
+ * @param {string} char |
+ * @return {boolean} |
+ */ |
+ _isSeparator: function(char) |
+ { |
+ return char === "," || char === ";"; |
+ }, |
+ |
+ /** |
+ * @param {SourceMap.StringCharIterator} stringCharIterator |
+ * @return {number} |
+ */ |
+ _decodeVLQ: function(stringCharIterator) |
+ { |
+ // Read unsigned value. |
+ var result = 0; |
+ var shift = 0; |
+ do { |
+ var digit = this._base64Map[stringCharIterator.next()]; |
+ result += (digit & this._VLQ_BASE_MASK) << shift; |
+ shift += this._VLQ_BASE_SHIFT; |
+ } while (digit & this._VLQ_CONTINUATION_MASK); |
+ |
+ // Fix the sign. |
+ var negative = result & 1; |
+ result >>= 1; |
+ return negative ? -result : result; |
+ }, |
+ |
+ _VLQ_BASE_SHIFT: 5, |
+ _VLQ_BASE_MASK: (1 << 5) - 1, |
+ _VLQ_CONTINUATION_MASK: 1 << 5 |
+} |
+ |
+/** |
+ * @constructor |
+ * @param {string} string |
+ */ |
+SourceMap.StringCharIterator = function(string) |
+{ |
+ this._string = string; |
+ this._position = 0; |
+} |
+ |
+SourceMap.StringCharIterator.prototype = { |
+ /** |
+ * @return {string} |
+ */ |
+ next: function() |
+ { |
+ return this._string.charAt(this._position++); |
+ }, |
+ |
+ /** |
+ * @return {string} |
+ */ |
+ peek: function() |
+ { |
+ return this._string.charAt(this._position); |
+ }, |
+ |
+ /** |
+ * @return {boolean} |
+ */ |
+ hasNext: function() |
+ { |
+ return this._position < this._string.length; |
+ } |
+} |