Author: [email protected]
Date: Mon Apr 27 06:50:42 2009
New Revision: 1802
Added:
branches/bleeding_edge/test/mjsunit/tools/consarray.js
branches/bleeding_edge/tools/consarray.js
branches/bleeding_edge/tools/linux-tick-processor (contents, props
changed)
branches/bleeding_edge/tools/tickprocessor.js
branches/bleeding_edge/tools/windows-tick-processor.bat (contents,
props changed)
Modified:
branches/bleeding_edge/test/mjsunit/tools/codemap.js
branches/bleeding_edge/test/mjsunit/tools/profile.js
branches/bleeding_edge/test/mjsunit/tools/profileview.js
branches/bleeding_edge/tools/codemap.js
branches/bleeding_edge/tools/profile.js
branches/bleeding_edge/tools/profileview.js
branches/bleeding_edge/tools/tickprocessor.py
Log:
TickProcessor script reimplemented in JavaScript.
This is an effort to reuse profiler data processing code both in
TickProcessor and Dev Tools Profiler. The old Python implementation
will be removed.
The new TickProcessor works almost identical to the previous one.
However, it has some differences:
1. Not very useful "Call profile" section is replaced with a new
WebKit-like "Bottom up (heavy) profile" which shows the most
expensive functions together with their callers. I used it
personally in order to find and remove bottlenecks in the
tickprocessor script itself, and found it quite helpful.
2. Code entries with duplicate names (they occur for RegExes, stubs
and sometimes for anonymous Function objects) are now distinguished
by adding an occurence number inside curly brackets.
3. (Address -> code entry) mapping is more precise in boundary cases.
4. Windows version no more requires specifying .map file location.
5. Works faster.
Review URL: http://codereview.chromium.org/99054
Modified: branches/bleeding_edge/test/mjsunit/tools/codemap.js
==============================================================================
--- branches/bleeding_edge/test/mjsunit/tools/codemap.js (original)
+++ branches/bleeding_edge/test/mjsunit/tools/codemap.js Mon Apr 27
06:50:42 2009
@@ -114,3 +114,13 @@
assertNoEntry(codeMap, 0x1700);
assertEntry(codeMap, 'code1', 0x1800);
})();
+
+
+(function testDynamicNamesDuplicates() {
+ var codeMap = new devtools.profiler.CodeMap();
+ // Code entries with same names but different addresses.
+ codeMap.addCode(0x1500, newCodeEntry(0x200, 'code'));
+ codeMap.addCode(0x1700, newCodeEntry(0x100, 'code'));
+ assertEntry(codeMap, 'code', 0x1500);
+ assertEntry(codeMap, 'code {1}', 0x1700);
+})();
Added: branches/bleeding_edge/test/mjsunit/tools/consarray.js
==============================================================================
--- (empty file)
+++ branches/bleeding_edge/test/mjsunit/tools/consarray.js Mon Apr 27
06:50:42 2009
@@ -0,0 +1,60 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Load ConsArray implementation from <project root>/tools.
+// Files: tools/consarray.js
+
+
+var arr1 = new ConsArray();
+assertTrue(arr1.atEnd());
+
+arr1.concat([]);
+assertTrue(arr1.atEnd());
+
+arr1.concat([1]);
+assertFalse(arr1.atEnd());
+assertEquals(1, arr1.next());
+assertTrue(arr1.atEnd());
+
+arr1.concat([2, 3, 4]);
+arr1.concat([5]);
+arr1.concat([]);
+arr1.concat([6, 7]);
+
+assertFalse(arr1.atEnd());
+assertEquals(2, arr1.next());
+assertFalse(arr1.atEnd());
+assertEquals(3, arr1.next());
+assertFalse(arr1.atEnd());
+assertEquals(4, arr1.next());
+assertFalse(arr1.atEnd());
+assertEquals(5, arr1.next());
+assertFalse(arr1.atEnd());
+assertEquals(6, arr1.next());
+assertFalse(arr1.atEnd());
+assertEquals(7, arr1.next());
+assertTrue(arr1.atEnd());
Modified: branches/bleeding_edge/test/mjsunit/tools/profile.js
==============================================================================
--- branches/bleeding_edge/test/mjsunit/tools/profile.js (original)
+++ branches/bleeding_edge/test/mjsunit/tools/profile.js Mon Apr 27
06:50:42 2009
@@ -26,7 +26,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Load source code files from <project root>/tools.
-// Files: tools/splaytree.js tools/codemap.js tools/profile.js
+// Files: tools/splaytree.js tools/codemap.js tools/consarray.js
tools/profile.js
function stackToString(stack) {
Modified: branches/bleeding_edge/test/mjsunit/tools/profileview.js
==============================================================================
--- branches/bleeding_edge/test/mjsunit/tools/profileview.js (original)
+++ branches/bleeding_edge/test/mjsunit/tools/profileview.js Mon Apr 27
06:50:42 2009
@@ -26,7 +26,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Load source code files from <project root>/tools.
-// Files: tools/profile.js tools/profileview.js
+// Files: tools/consarray.js tools/profile.js tools/profileview.js
function createNode(name, time, opt_parent) {
Modified: branches/bleeding_edge/tools/codemap.js
==============================================================================
--- branches/bleeding_edge/tools/codemap.js (original)
+++ branches/bleeding_edge/tools/codemap.js Mon Apr 27 06:50:42 2009
@@ -47,6 +47,8 @@
*/
this.deleted_ = [];
+ this.dynamicsNameGen_ = new devtools.profiler.CodeMap.NameGenerator();
+
/**
* Static code entries. Used for libraries code.
*/
@@ -79,6 +81,8 @@
* @param {devtools.profiler.CodeMap.CodeEntry} codeEntry Code entry
object.
*/
devtools.profiler.CodeMap.prototype.addCode = function(start, codeEntry) {
+ var entryName = this.dynamicsNameGen_.getName(codeEntry.name);
+ codeEntry.name = entryName;
this.dynamics_.insert(start, codeEntry);
};
@@ -204,7 +208,22 @@
return this.name;
};
-
+
devtools.profiler.CodeMap.CodeEntry.prototype.toString = function() {
return this.name + ': ' + this.size.toString(16);
+};
+
+
+devtools.profiler.CodeMap.NameGenerator = function() {
+ this.knownNames_ = [];
+};
+
+
+devtools.profiler.CodeMap.NameGenerator.prototype.getName = function(name)
{
+ if (!(name in this.knownNames_)) {
+ this.knownNames_[name] = 0;
+ return name;
+ }
+ var count = ++this.knownNames_[name];
+ return name + ' {' + count + '}';
};
Added: branches/bleeding_edge/tools/consarray.js
==============================================================================
--- (empty file)
+++ branches/bleeding_edge/tools/consarray.js Mon Apr 27 06:50:42 2009
@@ -0,0 +1,93 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+/**
+ * Constructs a ConsArray object. It is used mainly for tree traversal.
+ * In this use case we have lots of arrays that we need to iterate
+ * sequentally. The internal Array implementation is horribly slow
+ * when concatenating on large (10K items) arrays due to memory copying.
+ * That's why we avoid copying memory and insead build a linked list
+ * of arrays to iterate through.
+ *
+ * @constructor
+ */
+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() {
+ var result = this.currCell_.data[this.currCellPos_++];
+ if (this.currCellPos_ >= this.currCell_.data.length) {
+ this.currCell_ = this.currCell_.next;
+ this.currCellPos_ = 0;
+ }
+ return result;
+};
+
+
+/**
+ * A cell object used for constructing a list in ConsArray.
+ *
+ * @constructor
+ */
+ConsArray.Cell = function(data, next) {
+ this.data = data;
+ this.next = next;
+};
+
Added: branches/bleeding_edge/tools/linux-tick-processor
==============================================================================
--- (empty file)
+++ branches/bleeding_edge/tools/linux-tick-processor Mon Apr 27 06:50:42
2009
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+tools_dir=$(dirname "$0")
+
+if [ "$1" != "--no-build" ]
+then
+ scons -C $tools_dir/.. d8
+else
+ shift
+fi
+
+# nm spits out 'no symbols found' messages on stderr
+$tools_dir/../d8 $tools_dir/splaytree.js $tools_dir/codemap.js \
+ $tools_dir/csvparser.js $tools_dir/consarray.js \
+ $tools_dir/profile.js $tools_dir/profileview.js \
+ $tools_dir/tickprocessor.js -- $@ 2>/dev/null
Modified: branches/bleeding_edge/tools/profile.js
==============================================================================
--- branches/bleeding_edge/tools/profile.js (original)
+++ branches/bleeding_edge/tools/profile.js Mon Apr 27 06:50:42 2009
@@ -395,14 +395,16 @@
* @param {devtools.profiler.CallTree.Node} opt_start Starting node.
*/
devtools.profiler.CallTree.prototype.traverse = function(f, opt_start) {
- var pairsToProcess = [{node: opt_start || this.root_, param: null}];
- while (pairsToProcess.length > 0) {
- var pair = pairsToProcess.shift();
+ var pairsToProcess = new ConsArray();
+ pairsToProcess.concat([{node: opt_start || this.root_, param: null}]);
+ while (!pairsToProcess.atEnd()) {
+ var pair = pairsToProcess.next();
var node = pair.node;
var newParam = f(node, pair.param);
- node.forEachChild(
- function (child) { pairsToProcess.push({node: child, param:
newParam}); }
- );
+ var morePairsToProcess = [];
+ node.forEachChild(function (child) {
+ morePairsToProcess.push({node: child, param: newParam}); });
+ pairsToProcess.concat(morePairsToProcess);
}
};
Modified: branches/bleeding_edge/tools/profileview.js
==============================================================================
--- branches/bleeding_edge/tools/profileview.js (original)
+++ branches/bleeding_edge/tools/profileview.js Mon Apr 27 06:50:42 2009
@@ -100,11 +100,12 @@
* @param {function(devtools.profiler.ProfileView.Node)} f Visitor
function.
*/
devtools.profiler.ProfileView.prototype.traverse = function(f) {
- var nodesToTraverse = [this.head];
- while (nodesToTraverse.length > 0) {
- var node = nodesToTraverse.shift();
+ var nodesToTraverse = new ConsArray();
+ nodesToTraverse.concat([this.head]);
+ while (!nodesToTraverse.atEnd()) {
+ var node = nodesToTraverse.next();
f(node);
- nodesToTraverse = nodesToTraverse.concat(node.children);
+ nodesToTraverse.concat(node.children);
}
};
Added: branches/bleeding_edge/tools/tickprocessor.js
==============================================================================
--- (empty file)
+++ branches/bleeding_edge/tools/tickprocessor.js Mon Apr 27 06:50:42 2009
@@ -0,0 +1,620 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+function Profile(separateIc) {
+ devtools.profiler.Profile.call(this);
+ if (!separateIc) {
+ this.skipThisFunction = function(name) { return
Profile.IC_RE.test(name); };
+ }
+};
+Profile.prototype = devtools.profiler.Profile.prototype;
+
+
+Profile.IC_RE =
+ /^(?:CallIC|LoadIC|StoreIC)|(?:Builtin: (?:Keyed)?(?:Call|Load|
Store)IC_)/;
+
+
+/**
+ * A thin wrapper around shell's 'read' function showing a file name on
error.
+ */
+function readFile(fileName) {
+ try {
+ return read(fileName);
+ } catch (e) {
+ print(fileName + ': ' + (e.message || e));
+ throw e;
+ }
+}
+
+
+function TickProcessor(
+ cppEntriesProvider, separateIc, ignoreUnknown, stateFilter) {
+ this.cppEntriesProvider_ = cppEntriesProvider;
+ this.ignoreUnknown_ = ignoreUnknown;
+ this.stateFilter_ = stateFilter;
+ var ticks = this.ticks_ =
+ { total: 0, unaccounted: 0, excluded: 0, gc: 0 };
+
+ Profile.prototype.handleUnknownCode = function(
+ operation, addr, opt_stackPos) {
+ var op = devtools.profiler.Profile.Operation;
+ switch (operation) {
+ case op.MOVE:
+ print('Code move event for unknown code: 0x' + addr.toString(16));
+ break;
+ case op.DELETE:
+ print('Code delete event for unknown code: 0x' +
addr.toString(16));
+ break;
+ case op.TICK:
+ // Only unknown PCs (the first frame) are reported as unaccounted,
+ // otherwise tick balance will be corrupted (this behavior is
compatible
+ // with the original tickprocessor.py script.)
+ if (opt_stackPos == 0) {
+ ticks.unaccounted++;
+ }
+ break;
+ }
+ };
+
+ this.profile_ = new Profile(separateIc);
+ this.codeTypes_ = {};
+ // Count each tick as a time unit.
+ this.viewBuilder_ = new devtools.profiler.ViewBuilder(1);
+ this.lastLogFileName_ = null;
+};
+
+
+TickProcessor.VmStates = {
+ JS: 0,
+ GC: 1,
+ COMPILER: 2,
+ OTHER: 3,
+ EXTERNAL: 4
+};
+
+
+TickProcessor.CodeTypes = {
+ JS: 0,
+ CPP: 1,
+ SHARED_LIB: 2
+};
+
+
+TickProcessor.RecordsDispatch = {
+ 'shared-library': { parsers: [null, parseInt, parseInt],
+ processor: 'processSharedLibrary' },
+ 'code-creation': { parsers: [null, parseInt, parseInt, null],
+ processor: 'processCodeCreation' },
+ 'code-move': { parsers: [parseInt, parseInt],
+ processor: 'processCodeMove' },
+ 'code-delete': { parsers: [parseInt], processor: 'processCodeDelete' },
+ 'tick': { parsers: [parseInt, parseInt, parseInt, 'var-args'],
+ processor: 'processTick' },
+ 'profiler': null,
+ // Obsolete row types.
+ 'code-allocate': null,
+ 'begin-code-region': null,
+ 'end-code-region': null
+};
+
+
+TickProcessor.CALL_PROFILE_CUTOFF_PCT = 2.0;
+
+
+TickProcessor.prototype.setCodeType = function(name, type) {
+ this.codeTypes_[name] = TickProcessor.CodeTypes[type];
+};
+
+
+TickProcessor.prototype.isSharedLibrary = function(name) {
+ return this.codeTypes_[name] == TickProcessor.CodeTypes.SHARED_LIB;
+};
+
+
+TickProcessor.prototype.isCppCode = function(name) {
+ return this.codeTypes_[name] == TickProcessor.CodeTypes.CPP;
+};
+
+
+TickProcessor.prototype.isJsCode = function(name) {
+ return this.codeTypes_[name] == TickProcessor.CodeTypes.JS;
+};
+
+
+TickProcessor.prototype.processLogFile = function(fileName) {
+ this.lastLogFileName_ = fileName;
+ var contents = readFile(fileName);
+ this.processLog(contents.split('\n'));
+};
+
+
+TickProcessor.prototype.processLog = function(lines) {
+ var csvParser = new devtools.profiler.CsvParser();
+ try {
+ for (var i = 0, n = lines.length; i < n; ++i) {
+ var line = lines[i];
+ if (!line) {
+ continue;
+ }
+ var fields = csvParser.parseLine(line);
+ this.dispatchLogRow(fields);
+ }
+ } catch (e) {
+ print('line ' + (i + 1) + ': ' + (e.message || e));
+ throw e;
+ }
+};
+
+
+TickProcessor.prototype.dispatchLogRow = function(fields) {
+ // Obtain the dispatch.
+ var command = fields[0];
+ if (!(command in TickProcessor.RecordsDispatch)) {
+ throw new Error('unknown command: ' + command);
+ }
+ var dispatch = TickProcessor.RecordsDispatch[command];
+
+ if (dispatch === null) {
+ return;
+ }
+
+ // Parse fields.
+ var parsedFields = [];
+ for (var i = 0; i < dispatch.parsers.length; ++i) {
+ var parser = dispatch.parsers[i];
+ if (parser === null) {
+ parsedFields.push(fields[1 + i]);
+ } else if (typeof parser == 'function') {
+ parsedFields.push(parser(fields[1 + i]));
+ } else {
+ // var-args
+ parsedFields.push(fields.slice(1 + i));
+ break;
+ }
+ }
+
+ // Run the processor.
+ this[dispatch.processor].apply(this, parsedFields);
+};
+
+
+TickProcessor.prototype.processSharedLibrary = function(
+ name, startAddr, endAddr) {
+ var entry = this.profile_.addStaticCode(name, startAddr, endAddr);
+ this.setCodeType(entry.getName(), 'SHARED_LIB');
+
+ var self = this;
+ var libFuncs = this.cppEntriesProvider_.parseVmSymbols(
+ name, startAddr, endAddr, function(fName, fStart, fEnd) {
+ self.profile_.addStaticCode(fName, fStart, fEnd);
+ self.setCodeType(fName, 'CPP');
+ });
+};
+
+
+TickProcessor.prototype.processCodeCreation = function(
+ type, start, size, name) {
+ var entry = this.profile_.addCode(type, name, start, size);
+ this.setCodeType(entry.getName(), 'JS');
+};
+
+
+TickProcessor.prototype.processCodeMove = function(from, to) {
+ this.profile_.moveCode(from, to);
+};
+
+
+TickProcessor.prototype.processCodeDelete = function(start) {
+ this.profile_.deleteCode(start);
+};
+
+
+TickProcessor.prototype.includeTick = function(vmState) {
+ return this.stateFilter_ == null || this.stateFilter_ == vmState;
+};
+
+
+TickProcessor.prototype.processTick = function(pc, sp, vmState, stack) {
+ this.ticks_.total++;
+ if (vmState == TickProcessor.VmStates.GC) this.ticks_.gc++;
+ if (!this.includeTick(vmState)) {
+ this.ticks_.excluded++;
+ return;
+ }
+
+ var fullStack = [pc];
+ for (var i = 0, n = stack.length; i < n; ++i) {
+ var frame = stack[i];
+ // Leave only numbers starting with 0x. Filter possible 'overflow'
string.
+ if (frame.charAt(0) == '0') {
+ fullStack.push(parseInt(frame, 16));
+ }
+ }
+ this.profile_.recordTick(fullStack);
+};
+
+
+TickProcessor.prototype.printStatistics = function() {
+ print('Statistical profiling result from ' + this.lastLogFileName_ +
+ ', (' + this.ticks_.total +
+ ' ticks, ' + this.ticks_.unaccounted + ' unaccounted, ' +
+ this.ticks_.excluded + ' excluded).');
+
+ if (this.ticks_.total == 0) return;
+
+ // Print the unknown ticks percentage if they are not ignored.
+ if (!this.ignoreUnknown_ && this.ticks_.unaccounted > 0) {
+ this.printHeader('Unknown');
+ this.printCounter(this.ticks_.unaccounted, this.ticks_.total);
+ }
+
+ var flatProfile = this.profile_.getFlatProfile();
+ var flatView = this.viewBuilder_.buildView(flatProfile);
+ // Sort by self time, desc, then by name, desc.
+ flatView.sort(function(rec1, rec2) {
+ return rec2.selfTime - rec1.selfTime ||
+ (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
+ var totalTicks = this.ticks_.total;
+ if (this.ignoreUnknown_) {
+ totalTicks -= this.ticks_.unaccounted;
+ }
+ // Our total time contains all the ticks encountered,
+ // while profile only knows about the filtered ticks.
+ flatView.head.totalTime = totalTicks;
+
+ // Count library ticks
+ var flatViewNodes = flatView.head.children;
+ var self = this;
+ var libraryTicks = 0;
+ this.processProfile(flatViewNodes,
+ function(name) { return self.isSharedLibrary(name); },
+ function(rec) { libraryTicks += rec.selfTime; });
+ var nonLibraryTicks = totalTicks - libraryTicks;
+
+ this.printHeader('Shared libraries');
+ this.printEntries(flatViewNodes, null,
+ function(name) { return self.isSharedLibrary(name); });
+
+ this.printHeader('JavaScript');
+ this.printEntries(flatViewNodes, nonLibraryTicks,
+ function(name) { return self.isJsCode(name); });
+
+ this.printHeader('C++');
+ this.printEntries(flatViewNodes, nonLibraryTicks,
+ function(name) { return self.isCppCode(name); });
+
+ this.printHeader('GC');
+ this.printCounter(this.ticks_.gc, totalTicks);
+
+ this.printHeavyProfHeader();
+ var heavyProfile = this.profile_.getBottomUpProfile();
+ var heavyView = this.viewBuilder_.buildView(heavyProfile);
+ // To show the same percentages as in the flat profile.
+ heavyView.head.totalTime = totalTicks;
+ // Sort by total time, desc, then by name, desc.
+ heavyView.sort(function(rec1, rec2) {
+ return rec2.totalTime - rec1.totalTime ||
+ (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
+ this.printHeavyProfile(heavyView.head.children);
+};
+
+
+function padLeft(s, len) {
+ s = s.toString();
+ if (s.length < len) {
+ s = (new Array(len - s.length + 1).join(' ')) + s;
+ }
+ return s;
+};
+
+
+TickProcessor.prototype.printHeader = function(headerTitle) {
+ print('\n [' + headerTitle + ']:');
+ print(' ticks total nonlib name');
+};
+
+
+TickProcessor.prototype.printHeavyProfHeader = function() {
+ print('\n [Bottom up (heavy) profile]:');
+ print(' Note: percentage shows a share of a particular caller in the ' +
+ 'total\n' +
+ ' amount of its parent calls.');
+ print(' Callers occupying less than ' +
+ TickProcessor.CALL_PROFILE_CUTOFF_PCT.toFixed(1) +
+ '% are not shown.\n');
+ print(' ticks parent name');
+};
+
+
+TickProcessor.prototype.printCounter = function(ticksCount,
totalTicksCount) {
+ var pct = ticksCount * 100.0 / totalTicksCount;
+ print(' ' + padLeft(ticksCount, 5) + ' ' + padLeft(pct.toFixed(1), 5)
+ '%');
+};
+
+
+TickProcessor.prototype.processProfile = function(
+ profile, filterP, func) {
+ for (var i = 0, n = profile.length; i < n; ++i) {
+ var rec = profile[i];
+ // An empty record corresponds to a tree root.
+ if (!rec.internalFuncName || !filterP(rec.internalFuncName)) {
+ continue;
+ }
+ func(rec);
+ }
+};
+
+
+TickProcessor.prototype.printEntries = function(
+ profile, nonLibTicks, filterP) {
+ this.processProfile(profile, filterP, function (rec) {
+ if (rec.selfTime == 0) return;
+ var nonLibPct = nonLibTicks != null ?
+ rec.selfTime * 100.0 / nonLibTicks : 0.0;
+ print(' ' + padLeft(rec.selfTime, 5) + ' ' +
+ padLeft(rec.selfPercent.toFixed(1), 5) + '% ' +
+ padLeft(nonLibPct.toFixed(1), 5) + '% ' +
+ rec.internalFuncName);
+ });
+};
+
+
+TickProcessor.prototype.printHeavyProfile = function(profile, opt_indent) {
+ var self = this;
+ var indent = opt_indent || 0;
+ var indentStr = padLeft('', indent);
+ this.processProfile(profile, function() { return true; }, function (rec)
{
+ // Cut off too infrequent callers.
+ if (rec.parentTotalPercent < TickProcessor.CALL_PROFILE_CUTOFF_PCT)
return;
+ print(' ' + padLeft(rec.totalTime, 5) + ' ' +
+ padLeft(rec.parentTotalPercent.toFixed(1), 5) + '% ' +
+ indentStr + rec.internalFuncName);
+ // Limit backtrace depth.
+ if (indent < 10) {
+ self.printHeavyProfile(rec.children, indent + 2);
+ }
+ // Delimit top-level functions.
+ if (indent == 0) {
+ print('');
+ }
+ });
+};
+
+
+function CppEntriesProvider() {
+};
+
+
+CppEntriesProvider.prototype.parseVmSymbols = function(
+ libName, libStart, libEnd, processorFunc) {
+ var syms = this.loadSymbols(libName);
+ if (syms.length == 0) return;
+
+ var prevEntry;
+
+ function addPrevEntry(end) {
+ // Several functions can be mapped onto the same address. To avoid
+ // creating zero-sized entries, skip such duplicates.
+ if (prevEntry && prevEntry.start != end) {
+ processorFunc(prevEntry.name, prevEntry.start, end);
+ }
+ }
+
+ for (var i = 0, n = syms.length; i < n; ++i) {
+ var line = syms[i];
+ var funcInfo = this.parseLine(line);
+ if (!funcInfo) {
+ continue;
+ }
+ if (funcInfo.start < libStart && funcInfo.start < libEnd - libStart) {
+ funcInfo.start += libStart;
+ }
+ addPrevEntry(funcInfo.start);
+ prevEntry = funcInfo;
+ }
+ addPrevEntry(libEnd);
+};
+
+
+CppEntriesProvider.prototype.loadSymbols = function(libName) {
+ return [];
+};
+
+
+CppEntriesProvider.prototype.parseLine = function(line) {
+ return { name: '', start: 0 };
+};
+
+
+function inherits(childCtor, parentCtor) {
+ function tempCtor() {};
+ tempCtor.prototype = parentCtor.prototype;
+ childCtor.prototype = new tempCtor();
+};
+
+
+function UnixCppEntriesProvider() {
+};
+inherits(UnixCppEntriesProvider, CppEntriesProvider);
+
+
+UnixCppEntriesProvider.FUNC_RE = /^([0-9a-fA-F]{8}) . (.*)$/;
+
+
+UnixCppEntriesProvider.prototype.loadSymbols = function(libName) {
+ var normalSyms = os.system('nm', ['-C', '-n', libName], -1, -1);
+ var dynaSyms = os.system('nm', ['-C', '-n', '-D', libName], -1, -1);
+ var syms = (normalSyms + dynaSyms).split('\n');
+ return syms;
+};
+
+
+UnixCppEntriesProvider.prototype.parseLine = function(line) {
+ var fields = line.match(UnixCppEntriesProvider.FUNC_RE);
+ return fields ? { name: fields[2], start: parseInt(fields[1], 16) } :
null;
+};
+
+
+function WindowsCppEntriesProvider() {
+};
+inherits(WindowsCppEntriesProvider, CppEntriesProvider);
+
+
+WindowsCppEntriesProvider.FILENAME_RE = /^(.*)\.exe$/;
+
+
+WindowsCppEntriesProvider.FUNC_RE =
+ /^ 0001:[0-9a-fA-F]{8}\s+([...@$0-9a-za-z]+)\s+([0-9a-fA-F]{8}).*$/;
+
+
+WindowsCppEntriesProvider.prototype.loadSymbols = function(libName) {
+ var fileNameFields =
libName.match(WindowsCppEntriesProvider.FILENAME_RE);
+ // Only try to load symbols for the .exe file.
+ if (!fileNameFields) return [];
+ var mapFileName = fileNameFields[1] + '.map';
+ return readFile(mapFileName).split('\r\n');
+};
+
+
+WindowsCppEntriesProvider.prototype.parseLine = function(line) {
+ var fields = line.match(WindowsCppEntriesProvider.FUNC_RE);
+ return fields ?
+ { name: this.unmangleName(fields[1]), start: parseInt(fields[2], 16)
} :
+ null;
+};
+
+
+/**
+ * Performs very simple unmangling of C++ names.
+ *
+ * Does not handle arguments and template arguments. The mangled names have
+ * the form:
+ *
+ * ?lookupindescrip...@jsobject@inter...@v8@@...arguments info...
+ */
+WindowsCppEntriesProvider.prototype.unmangleName = function(name) {
+ // Empty or non-mangled name.
+ if (name.length < 1 || name.charAt(0) != '?') return name;
+ var nameEndPos = name.indexOf('@@');
+ var components = name.substring(1, nameEndPos).split('@');
+ components.reverse();
+ return components.join('::');
+};
+
+
+function padRight(s, len) {
+ s = s.toString();
+ if (s.length < len) {
+ s = s + (new Array(len - s.length + 1).join(' '));
+ }
+ return s;
+};
+
+
+function processArguments(args) {
+ var result = {
+ logFileName: 'v8.log',
+ platform: 'unix',
+ stateFilter: null,
+ ignoreUnknown: false,
+ separateIc: false
+ };
+ var argsDispatch = {
+ '-j': ['stateFilter', TickProcessor.VmStates.JS,
+ 'Show only ticks from JS VM state'],
+ '-g': ['stateFilter', TickProcessor.VmStates.GC,
+ 'Show only ticks from GC VM state'],
+ '-c': ['stateFilter', TickProcessor.VmStates.COMPILER,
+ 'Show only ticks from COMPILER VM state'],
+ '-o': ['stateFilter', TickProcessor.VmStates.OTHER,
+ 'Show only ticks from OTHER VM state'],
+ '-e': ['stateFilter', TickProcessor.VmStates.EXTERNAL,
+ 'Show only ticks from EXTERNAL VM state'],
+ '--ignore-unknown': ['ignoreUnknown', true,
+ 'Exclude ticks of unknown code entries from processing'],
+ '--separate-ic': ['separateIc', true,
+ 'Separate IC entries'],
+ '--unix': ['platform', 'unix',
+ 'Specify that we are running on *nix platform'],
+ '--windows': ['platform', 'windows',
+ 'Specify that we are running on Windows platform']
+ };
+ argsDispatch['--js'] = argsDispatch['-j'];
+ argsDispatch['--gc'] = argsDispatch['-g'];
+ argsDispatch['--compiler'] = argsDispatch['-c'];
+ argsDispatch['--other'] = argsDispatch['-o'];
+ argsDispatch['--external'] = argsDispatch['-e'];
+
+ function printUsageAndExit() {
+ print('Cmdline args: [options] [log-file-name]\n' +
+ 'Default log file name is "v8.log".\n');
+ print('Options:');
+ for (var arg in argsDispatch) {
+ var synonims = [arg];
+ var dispatch = argsDispatch[arg];
+ for (var synArg in argsDispatch) {
+ if (arg !== synArg && dispatch === argsDispatch[synArg]) {
+ synonims.push(synArg);
+ delete argsDispatch[synArg];
+ }
+ }
+ print(' ' + padRight(synonims.join(', '), 20) + dispatch[2]);
+ }
+ quit(2);
+ }
+
+ while (args.length) {
+ var arg = args[0];
+ if (arg.charAt(0) != '-') {
+ break;
+ }
+ args.shift();
+ if (arg in argsDispatch) {
+ var dispatch = argsDispatch[arg];
+ result[dispatch[0]] = dispatch[1];
+ } else {
+ printUsageAndExit();
+ }
+ }
+
+ if (args.length >= 1) {
+ result.logFileName = args.shift();
+ }
+ return result;
+};
+
+
+var params = processArguments(arguments);
+var tickProcessor = new TickProcessor(
+ params.platform == 'unix' ? new UnixCppEntriesProvider() :
+ new WindowsCppEntriesProvider(),
+ params.separateIc,
+ params.ignoreUnknown,
+ params.stateFilter);
+tickProcessor.processLogFile(params.logFileName);
+tickProcessor.printStatistics();
Modified: branches/bleeding_edge/tools/tickprocessor.py
==============================================================================
--- branches/bleeding_edge/tools/tickprocessor.py (original)
+++ branches/bleeding_edge/tools/tickprocessor.py Mon Apr 27 06:50:42 2009
@@ -411,7 +411,7 @@
number_of_accounted_ticks -= self.unaccounted_number_of_ticks
number_of_non_library_ticks = number_of_accounted_ticks -
self.number_of_library_ticks
- entries.sort(key=lambda e:e.tick_count, reverse=True)
+ entries.sort(key=lambda e: (e.tick_count, e.ToString()), reverse=True)
for entry in entries:
if entry.tick_count > 0 and condition(entry):
total_percentage = entry.tick_count * 100.0 /
number_of_accounted_ticks
Added: branches/bleeding_edge/tools/windows-tick-processor.bat
==============================================================================
--- (empty file)
+++ branches/bleeding_edge/tools/windows-tick-processor.bat Mon Apr 27
06:50:42 2009
@@ -0,0 +1,5 @@
+...@echo off
+
+SET tools_dir=%~dp0
+
+%tools_dir%..\d8 %tools_dir%splaytree.js %tools_dir%codemap.js
%tools_dir%csvparser.js %tools_dir%consarray.js %tools_dir%profile.js
%tools_dir%profileview.js %tools_dir%tickprocessor.js
--
--windows %*
--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---