| Index: tools/tickprocessor.js
|
| diff --git a/tools/tickprocessor.js b/tools/tickprocessor.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9d19719713fafc3acf62e999b36e61b7405a46ac
|
| --- /dev/null
|
| +++ b/tools/tickprocessor.js
|
| @@ -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:
|
| + *
|
| + * ?LookupInDescriptor@JSObject@internal@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();
|
|
|