Home | History | Annotate | Download | only in tools
      1 // Copyright 2012 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 function inherits(childCtor, parentCtor) {
     29   childCtor.prototype.__proto__ = parentCtor.prototype;
     30 };
     31 
     32 
     33 function V8Profile(separateIc) {
     34   Profile.call(this);
     35   if (!separateIc) {
     36     this.skipThisFunction = function(name) { return V8Profile.IC_RE.test(name); };
     37   }
     38 };
     39 inherits(V8Profile, Profile);
     40 
     41 
     42 V8Profile.IC_RE =
     43     /^(LoadGlobalIC: )|(Handler: )|(Stub: )|(Builtin: )|(BytecodeHandler: )|(?:CallIC|LoadIC|StoreIC)|(?:Builtin: (?:Keyed)?(?:Load|Store)IC_)/;
     44 
     45 
     46 /**
     47  * A thin wrapper around shell's 'read' function showing a file name on error.
     48  */
     49 function readFile(fileName) {
     50   try {
     51     return read(fileName);
     52   } catch (e) {
     53     print(fileName + ': ' + (e.message || e));
     54     throw e;
     55   }
     56 }
     57 
     58 
     59 /**
     60  * Parser for dynamic code optimization state.
     61  */
     62 function parseState(s) {
     63   switch (s) {
     64   case "": return Profile.CodeState.COMPILED;
     65   case "~": return Profile.CodeState.OPTIMIZABLE;
     66   case "*": return Profile.CodeState.OPTIMIZED;
     67   }
     68   throw new Error("unknown code state: " + s);
     69 }
     70 
     71 
     72 function TickProcessor(
     73     cppEntriesProvider,
     74     separateIc,
     75     callGraphSize,
     76     ignoreUnknown,
     77     stateFilter,
     78     distortion,
     79     range,
     80     sourceMap,
     81     timedRange,
     82     pairwiseTimedRange,
     83     onlySummary,
     84     runtimeTimerFilter) {
     85   LogReader.call(this, {
     86       'shared-library': { parsers: [null, parseInt, parseInt, parseInt],
     87           processor: this.processSharedLibrary },
     88       'code-creation': {
     89           parsers: [null, parseInt, parseInt, parseInt, null, 'var-args'],
     90           processor: this.processCodeCreation },
     91       'code-move': { parsers: [parseInt, parseInt],
     92           processor: this.processCodeMove },
     93       'code-delete': { parsers: [parseInt],
     94           processor: this.processCodeDelete },
     95       'sfi-move': { parsers: [parseInt, parseInt],
     96           processor: this.processFunctionMove },
     97       'active-runtime-timer': {
     98         parsers: [null],
     99         processor: this.processRuntimeTimerEvent },
    100       'tick': {
    101           parsers: [parseInt, parseInt, parseInt,
    102                     parseInt, parseInt, 'var-args'],
    103           processor: this.processTick },
    104       'heap-sample-begin': { parsers: [null, null, parseInt],
    105           processor: this.processHeapSampleBegin },
    106       'heap-sample-end': { parsers: [null, null],
    107           processor: this.processHeapSampleEnd },
    108       'timer-event-start' : { parsers: [null, null, null],
    109                               processor: this.advanceDistortion },
    110       'timer-event-end' : { parsers: [null, null, null],
    111                             processor: this.advanceDistortion },
    112       // Ignored events.
    113       'profiler': null,
    114       'function-creation': null,
    115       'function-move': null,
    116       'function-delete': null,
    117       'heap-sample-item': null,
    118       'current-time': null,  // Handled specially, not parsed.
    119       // Obsolete row types.
    120       'code-allocate': null,
    121       'begin-code-region': null,
    122       'end-code-region': null },
    123       timedRange,
    124       pairwiseTimedRange);
    125 
    126   this.cppEntriesProvider_ = cppEntriesProvider;
    127   this.callGraphSize_ = callGraphSize;
    128   this.ignoreUnknown_ = ignoreUnknown;
    129   this.stateFilter_ = stateFilter;
    130   this.runtimeTimerFilter_ = runtimeTimerFilter;
    131   this.sourceMap = sourceMap;
    132   this.deserializedEntriesNames_ = [];
    133   var ticks = this.ticks_ =
    134     { total: 0, unaccounted: 0, excluded: 0, gc: 0 };
    135 
    136   distortion = parseInt(distortion);
    137   // Convert picoseconds to nanoseconds.
    138   this.distortion_per_entry = isNaN(distortion) ? 0 : (distortion / 1000);
    139   this.distortion = 0;
    140   var rangelimits = range ? range.split(",") : [];
    141   var range_start = parseInt(rangelimits[0]);
    142   var range_end = parseInt(rangelimits[1]);
    143   // Convert milliseconds to nanoseconds.
    144   this.range_start = isNaN(range_start) ? -Infinity : (range_start * 1000);
    145   this.range_end = isNaN(range_end) ? Infinity : (range_end * 1000)
    146 
    147   V8Profile.prototype.handleUnknownCode = function(
    148       operation, addr, opt_stackPos) {
    149     var op = Profile.Operation;
    150     switch (operation) {
    151       case op.MOVE:
    152         print('Code move event for unknown code: 0x' + addr.toString(16));
    153         break;
    154       case op.DELETE:
    155         print('Code delete event for unknown code: 0x' + addr.toString(16));
    156         break;
    157       case op.TICK:
    158         // Only unknown PCs (the first frame) are reported as unaccounted,
    159         // otherwise tick balance will be corrupted (this behavior is compatible
    160         // with the original tickprocessor.py script.)
    161         if (opt_stackPos == 0) {
    162           ticks.unaccounted++;
    163         }
    164         break;
    165     }
    166   };
    167 
    168   this.profile_ = new V8Profile(separateIc);
    169   this.codeTypes_ = {};
    170   // Count each tick as a time unit.
    171   this.viewBuilder_ = new ViewBuilder(1);
    172   this.lastLogFileName_ = null;
    173 
    174   this.generation_ = 1;
    175   this.currentProducerProfile_ = null;
    176   this.onlySummary_ = onlySummary;
    177 };
    178 inherits(TickProcessor, LogReader);
    179 
    180 
    181 TickProcessor.VmStates = {
    182   JS: 0,
    183   GC: 1,
    184   COMPILER: 2,
    185   OTHER: 3,
    186   EXTERNAL: 4,
    187   IDLE: 5
    188 };
    189 
    190 
    191 TickProcessor.CodeTypes = {
    192   CPP: 0,
    193   SHARED_LIB: 1
    194 };
    195 // Otherwise, this is JS-related code. We are not adding it to
    196 // codeTypes_ map because there can be zillions of them.
    197 
    198 
    199 TickProcessor.CALL_PROFILE_CUTOFF_PCT = 2.0;
    200 
    201 TickProcessor.CALL_GRAPH_SIZE = 5;
    202 
    203 /**
    204  * @override
    205  */
    206 TickProcessor.prototype.printError = function(str) {
    207   print(str);
    208 };
    209 
    210 
    211 TickProcessor.prototype.setCodeType = function(name, type) {
    212   this.codeTypes_[name] = TickProcessor.CodeTypes[type];
    213 };
    214 
    215 
    216 TickProcessor.prototype.isSharedLibrary = function(name) {
    217   return this.codeTypes_[name] == TickProcessor.CodeTypes.SHARED_LIB;
    218 };
    219 
    220 
    221 TickProcessor.prototype.isCppCode = function(name) {
    222   return this.codeTypes_[name] == TickProcessor.CodeTypes.CPP;
    223 };
    224 
    225 
    226 TickProcessor.prototype.isJsCode = function(name) {
    227   return name !== "UNKNOWN" && !(name in this.codeTypes_);
    228 };
    229 
    230 
    231 TickProcessor.prototype.processLogFile = function(fileName) {
    232   this.lastLogFileName_ = fileName;
    233   var line;
    234   while (line = readline()) {
    235     this.processLogLine(line);
    236   }
    237 };
    238 
    239 
    240 TickProcessor.prototype.processLogFileInTest = function(fileName) {
    241    // Hack file name to avoid dealing with platform specifics.
    242   this.lastLogFileName_ = 'v8.log';
    243   var contents = readFile(fileName);
    244   this.processLogChunk(contents);
    245 };
    246 
    247 
    248 TickProcessor.prototype.processSharedLibrary = function(
    249     name, startAddr, endAddr, aslrSlide) {
    250   var entry = this.profile_.addLibrary(name, startAddr, endAddr, aslrSlide);
    251   this.setCodeType(entry.getName(), 'SHARED_LIB');
    252 
    253   var self = this;
    254   var libFuncs = this.cppEntriesProvider_.parseVmSymbols(
    255       name, startAddr, endAddr, aslrSlide, function(fName, fStart, fEnd) {
    256     self.profile_.addStaticCode(fName, fStart, fEnd);
    257     self.setCodeType(fName, 'CPP');
    258   });
    259 };
    260 
    261 
    262 TickProcessor.prototype.processCodeCreation = function(
    263     type, kind, start, size, name, maybe_func) {
    264   name = this.deserializedEntriesNames_[start] || name;
    265   if (maybe_func.length) {
    266     var funcAddr = parseInt(maybe_func[0]);
    267     var state = parseState(maybe_func[1]);
    268     this.profile_.addFuncCode(type, name, start, size, funcAddr, state);
    269   } else {
    270     this.profile_.addCode(type, name, start, size);
    271   }
    272 };
    273 
    274 
    275 TickProcessor.prototype.processCodeMove = function(from, to) {
    276   this.profile_.moveCode(from, to);
    277 };
    278 
    279 
    280 TickProcessor.prototype.processCodeDelete = function(start) {
    281   this.profile_.deleteCode(start);
    282 };
    283 
    284 
    285 TickProcessor.prototype.processFunctionMove = function(from, to) {
    286   this.profile_.moveFunc(from, to);
    287 };
    288 
    289 
    290 TickProcessor.prototype.includeTick = function(vmState) {
    291   if (this.stateFilter_ !== null) {
    292     return this.stateFilter_ == vmState;
    293   } else if (this.runtimeTimerFilter_ !== null) {
    294     return this.currentRuntimeTimer == this.runtimeTimerFilter_;
    295   }
    296   return true;
    297 };
    298 
    299 TickProcessor.prototype.processRuntimeTimerEvent = function(name) {
    300   this.currentRuntimeTimer = name;
    301 }
    302 
    303 TickProcessor.prototype.processTick = function(pc,
    304                                                ns_since_start,
    305                                                is_external_callback,
    306                                                tos_or_external_callback,
    307                                                vmState,
    308                                                stack) {
    309   this.distortion += this.distortion_per_entry;
    310   ns_since_start -= this.distortion;
    311   if (ns_since_start < this.range_start || ns_since_start > this.range_end) {
    312     return;
    313   }
    314   this.ticks_.total++;
    315   if (vmState == TickProcessor.VmStates.GC) this.ticks_.gc++;
    316   if (!this.includeTick(vmState)) {
    317     this.ticks_.excluded++;
    318     return;
    319   }
    320   if (is_external_callback) {
    321     // Don't use PC when in external callback code, as it can point
    322     // inside callback's code, and we will erroneously report
    323     // that a callback calls itself. Instead we use tos_or_external_callback,
    324     // as simply resetting PC will produce unaccounted ticks.
    325     pc = tos_or_external_callback;
    326     tos_or_external_callback = 0;
    327   } else if (tos_or_external_callback) {
    328     // Find out, if top of stack was pointing inside a JS function
    329     // meaning that we have encountered a frameless invocation.
    330     var funcEntry = this.profile_.findEntry(tos_or_external_callback);
    331     if (!funcEntry || !funcEntry.isJSFunction || !funcEntry.isJSFunction()) {
    332       tos_or_external_callback = 0;
    333     }
    334   }
    335 
    336   this.profile_.recordTick(this.processStack(pc, tos_or_external_callback, stack));
    337 };
    338 
    339 
    340 TickProcessor.prototype.advanceDistortion = function() {
    341   this.distortion += this.distortion_per_entry;
    342 }
    343 
    344 
    345 TickProcessor.prototype.processHeapSampleBegin = function(space, state, ticks) {
    346   if (space != 'Heap') return;
    347   this.currentProducerProfile_ = new CallTree();
    348 };
    349 
    350 
    351 TickProcessor.prototype.processHeapSampleEnd = function(space, state) {
    352   if (space != 'Heap' || !this.currentProducerProfile_) return;
    353 
    354   print('Generation ' + this.generation_ + ':');
    355   var tree = this.currentProducerProfile_;
    356   tree.computeTotalWeights();
    357   var producersView = this.viewBuilder_.buildView(tree);
    358   // Sort by total time, desc, then by name, desc.
    359   producersView.sort(function(rec1, rec2) {
    360       return rec2.totalTime - rec1.totalTime ||
    361           (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
    362   this.printHeavyProfile(producersView.head.children);
    363 
    364   this.currentProducerProfile_ = null;
    365   this.generation_++;
    366 };
    367 
    368 
    369 TickProcessor.prototype.printStatistics = function() {
    370   print('Statistical profiling result from ' + this.lastLogFileName_ +
    371         ', (' + this.ticks_.total +
    372         ' ticks, ' + this.ticks_.unaccounted + ' unaccounted, ' +
    373         this.ticks_.excluded + ' excluded).');
    374 
    375   if (this.ticks_.total == 0) return;
    376 
    377   var flatProfile = this.profile_.getFlatProfile();
    378   var flatView = this.viewBuilder_.buildView(flatProfile);
    379   // Sort by self time, desc, then by name, desc.
    380   flatView.sort(function(rec1, rec2) {
    381       return rec2.selfTime - rec1.selfTime ||
    382           (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
    383   var totalTicks = this.ticks_.total;
    384   if (this.ignoreUnknown_) {
    385     totalTicks -= this.ticks_.unaccounted;
    386   }
    387   var printAllTicks = !this.onlySummary_;
    388 
    389   // Count library ticks
    390   var flatViewNodes = flatView.head.children;
    391   var self = this;
    392 
    393   var libraryTicks = 0;
    394   if(printAllTicks) this.printHeader('Shared libraries');
    395   this.printEntries(flatViewNodes, totalTicks, null,
    396       function(name) { return self.isSharedLibrary(name); },
    397       function(rec) { libraryTicks += rec.selfTime; }, printAllTicks);
    398   var nonLibraryTicks = totalTicks - libraryTicks;
    399 
    400   var jsTicks = 0;
    401   if(printAllTicks) this.printHeader('JavaScript');
    402   this.printEntries(flatViewNodes, totalTicks, nonLibraryTicks,
    403       function(name) { return self.isJsCode(name); },
    404       function(rec) { jsTicks += rec.selfTime; }, printAllTicks);
    405 
    406   var cppTicks = 0;
    407   if(printAllTicks) this.printHeader('C++');
    408   this.printEntries(flatViewNodes, totalTicks, nonLibraryTicks,
    409       function(name) { return self.isCppCode(name); },
    410       function(rec) { cppTicks += rec.selfTime; }, printAllTicks);
    411 
    412   this.printHeader('Summary');
    413   this.printLine('JavaScript', jsTicks, totalTicks, nonLibraryTicks);
    414   this.printLine('C++', cppTicks, totalTicks, nonLibraryTicks);
    415   this.printLine('GC', this.ticks_.gc, totalTicks, nonLibraryTicks);
    416   this.printLine('Shared libraries', libraryTicks, totalTicks, null);
    417   if (!this.ignoreUnknown_ && this.ticks_.unaccounted > 0) {
    418     this.printLine('Unaccounted', this.ticks_.unaccounted,
    419                    this.ticks_.total, null);
    420   }
    421 
    422   if(printAllTicks) {
    423     print('\n [C++ entry points]:');
    424     print('   ticks    cpp   total   name');
    425     var c_entry_functions = this.profile_.getCEntryProfile();
    426     var total_c_entry = c_entry_functions[0].ticks;
    427     for (var i = 1; i < c_entry_functions.length; i++) {
    428       c = c_entry_functions[i];
    429       this.printLine(c.name, c.ticks, total_c_entry, totalTicks);
    430     }
    431 
    432     this.printHeavyProfHeader();
    433     var heavyProfile = this.profile_.getBottomUpProfile();
    434     var heavyView = this.viewBuilder_.buildView(heavyProfile);
    435     // To show the same percentages as in the flat profile.
    436     heavyView.head.totalTime = totalTicks;
    437     // Sort by total time, desc, then by name, desc.
    438     heavyView.sort(function(rec1, rec2) {
    439         return rec2.totalTime - rec1.totalTime ||
    440             (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
    441     this.printHeavyProfile(heavyView.head.children);
    442   }
    443 };
    444 
    445 
    446 function padLeft(s, len) {
    447   s = s.toString();
    448   if (s.length < len) {
    449     var padLength = len - s.length;
    450     if (!(padLength in padLeft)) {
    451       padLeft[padLength] = new Array(padLength + 1).join(' ');
    452     }
    453     s = padLeft[padLength] + s;
    454   }
    455   return s;
    456 };
    457 
    458 
    459 TickProcessor.prototype.printHeader = function(headerTitle) {
    460   print('\n [' + headerTitle + ']:');
    461   print('   ticks  total  nonlib   name');
    462 };
    463 
    464 
    465 TickProcessor.prototype.printLine = function(
    466     entry, ticks, totalTicks, nonLibTicks) {
    467   var pct = ticks * 100 / totalTicks;
    468   var nonLibPct = nonLibTicks != null
    469       ? padLeft((ticks * 100 / nonLibTicks).toFixed(1), 5) + '%  '
    470       : '        ';
    471   print('  ' + padLeft(ticks, 5) + '  ' +
    472         padLeft(pct.toFixed(1), 5) + '%  ' +
    473         nonLibPct +
    474         entry);
    475 }
    476 
    477 TickProcessor.prototype.printHeavyProfHeader = function() {
    478   print('\n [Bottom up (heavy) profile]:');
    479   print('  Note: percentage shows a share of a particular caller in the ' +
    480         'total\n' +
    481         '  amount of its parent calls.');
    482   print('  Callers occupying less than ' +
    483         TickProcessor.CALL_PROFILE_CUTOFF_PCT.toFixed(1) +
    484         '% are not shown.\n');
    485   print('   ticks parent  name');
    486 };
    487 
    488 
    489 TickProcessor.prototype.processProfile = function(
    490     profile, filterP, func) {
    491   for (var i = 0, n = profile.length; i < n; ++i) {
    492     var rec = profile[i];
    493     if (!filterP(rec.internalFuncName)) {
    494       continue;
    495     }
    496     func(rec);
    497   }
    498 };
    499 
    500 TickProcessor.prototype.getLineAndColumn = function(name) {
    501   var re = /:([0-9]+):([0-9]+)$/;
    502   var array = re.exec(name);
    503   if (!array) {
    504     return null;
    505   }
    506   return {line: array[1], column: array[2]};
    507 }
    508 
    509 TickProcessor.prototype.hasSourceMap = function() {
    510   return this.sourceMap != null;
    511 };
    512 
    513 
    514 TickProcessor.prototype.formatFunctionName = function(funcName) {
    515   if (!this.hasSourceMap()) {
    516     return funcName;
    517   }
    518   var lc = this.getLineAndColumn(funcName);
    519   if (lc == null) {
    520     return funcName;
    521   }
    522   // in source maps lines and columns are zero based
    523   var lineNumber = lc.line - 1;
    524   var column = lc.column - 1;
    525   var entry = this.sourceMap.findEntry(lineNumber, column);
    526   var sourceFile = entry[2];
    527   var sourceLine = entry[3] + 1;
    528   var sourceColumn = entry[4] + 1;
    529 
    530   return sourceFile + ':' + sourceLine + ':' + sourceColumn + ' -> ' + funcName;
    531 };
    532 
    533 TickProcessor.prototype.printEntries = function(
    534     profile, totalTicks, nonLibTicks, filterP, callback, printAllTicks) {
    535   var that = this;
    536   this.processProfile(profile, filterP, function (rec) {
    537     if (rec.selfTime == 0) return;
    538     callback(rec);
    539     var funcName = that.formatFunctionName(rec.internalFuncName);
    540     if(printAllTicks) {
    541       that.printLine(funcName, rec.selfTime, totalTicks, nonLibTicks);
    542     }
    543   });
    544 };
    545 
    546 
    547 TickProcessor.prototype.printHeavyProfile = function(profile, opt_indent) {
    548   var self = this;
    549   var indent = opt_indent || 0;
    550   var indentStr = padLeft('', indent);
    551   this.processProfile(profile, function() { return true; }, function (rec) {
    552     // Cut off too infrequent callers.
    553     if (rec.parentTotalPercent < TickProcessor.CALL_PROFILE_CUTOFF_PCT) return;
    554     var funcName = self.formatFunctionName(rec.internalFuncName);
    555     print('  ' + padLeft(rec.totalTime, 5) + '  ' +
    556           padLeft(rec.parentTotalPercent.toFixed(1), 5) + '%  ' +
    557           indentStr + funcName);
    558     // Limit backtrace depth.
    559     if (indent < 2 * self.callGraphSize_) {
    560       self.printHeavyProfile(rec.children, indent + 2);
    561     }
    562     // Delimit top-level functions.
    563     if (indent == 0) {
    564       print('');
    565     }
    566   });
    567 };
    568 
    569 
    570 function CppEntriesProvider() {
    571 };
    572 
    573 
    574 CppEntriesProvider.prototype.parseVmSymbols = function(
    575     libName, libStart, libEnd, libASLRSlide, processorFunc) {
    576   this.loadSymbols(libName);
    577 
    578   var prevEntry;
    579 
    580   function addEntry(funcInfo) {
    581     // Several functions can be mapped onto the same address. To avoid
    582     // creating zero-sized entries, skip such duplicates.
    583     // Also double-check that function belongs to the library address space.
    584     if (prevEntry && !prevEntry.end &&
    585         prevEntry.start < funcInfo.start &&
    586         prevEntry.start >= libStart && funcInfo.start <= libEnd) {
    587       processorFunc(prevEntry.name, prevEntry.start, funcInfo.start);
    588     }
    589     if (funcInfo.end &&
    590         (!prevEntry || prevEntry.start != funcInfo.start) &&
    591         funcInfo.start >= libStart && funcInfo.end <= libEnd) {
    592       processorFunc(funcInfo.name, funcInfo.start, funcInfo.end);
    593     }
    594     prevEntry = funcInfo;
    595   }
    596 
    597   while (true) {
    598     var funcInfo = this.parseNextLine();
    599     if (funcInfo === null) {
    600       continue;
    601     } else if (funcInfo === false) {
    602       break;
    603     }
    604     funcInfo.start += libASLRSlide;
    605     if (funcInfo.start < libStart && funcInfo.start < libEnd - libStart) {
    606       funcInfo.start += libStart;
    607     }
    608     if (funcInfo.size) {
    609       funcInfo.end = funcInfo.start + funcInfo.size;
    610     }
    611     addEntry(funcInfo);
    612   }
    613   addEntry({name: '', start: libEnd});
    614 };
    615 
    616 
    617 CppEntriesProvider.prototype.loadSymbols = function(libName) {
    618 };
    619 
    620 
    621 CppEntriesProvider.prototype.parseNextLine = function() {
    622   return false;
    623 };
    624 
    625 
    626 function UnixCppEntriesProvider(nmExec, targetRootFS) {
    627   this.symbols = [];
    628   this.parsePos = 0;
    629   this.nmExec = nmExec;
    630   this.targetRootFS = targetRootFS;
    631   this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ([0-9a-fA-F]{8,16} )?[tTwW] (.*)$/;
    632 };
    633 inherits(UnixCppEntriesProvider, CppEntriesProvider);
    634 
    635 
    636 UnixCppEntriesProvider.prototype.loadSymbols = function(libName) {
    637   this.parsePos = 0;
    638   libName = this.targetRootFS + libName;
    639   try {
    640     this.symbols = [
    641       os.system(this.nmExec, ['-C', '-n', '-S', libName], -1, -1),
    642       os.system(this.nmExec, ['-C', '-n', '-S', '-D', libName], -1, -1)
    643     ];
    644   } catch (e) {
    645     // If the library cannot be found on this system let's not panic.
    646     this.symbols = ['', ''];
    647   }
    648 };
    649 
    650 
    651 UnixCppEntriesProvider.prototype.parseNextLine = function() {
    652   if (this.symbols.length == 0) {
    653     return false;
    654   }
    655   var lineEndPos = this.symbols[0].indexOf('\n', this.parsePos);
    656   if (lineEndPos == -1) {
    657     this.symbols.shift();
    658     this.parsePos = 0;
    659     return this.parseNextLine();
    660   }
    661 
    662   var line = this.symbols[0].substring(this.parsePos, lineEndPos);
    663   this.parsePos = lineEndPos + 1;
    664   var fields = line.match(this.FUNC_RE);
    665   var funcInfo = null;
    666   if (fields) {
    667     funcInfo = { name: fields[3], start: parseInt(fields[1], 16) };
    668     if (fields[2]) {
    669       funcInfo.size = parseInt(fields[2], 16);
    670     }
    671   }
    672   return funcInfo;
    673 };
    674 
    675 
    676 function MacCppEntriesProvider(nmExec, targetRootFS) {
    677   UnixCppEntriesProvider.call(this, nmExec, targetRootFS);
    678   // Note an empty group. It is required, as UnixCppEntriesProvider expects 3 groups.
    679   this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ()[iItT] (.*)$/;
    680 };
    681 inherits(MacCppEntriesProvider, UnixCppEntriesProvider);
    682 
    683 
    684 MacCppEntriesProvider.prototype.loadSymbols = function(libName) {
    685   this.parsePos = 0;
    686   libName = this.targetRootFS + libName;
    687 
    688   // It seems that in OS X `nm` thinks that `-f` is a format option, not a
    689   // "flat" display option flag.
    690   try {
    691     this.symbols = [os.system(this.nmExec, ['-n', libName], -1, -1), ''];
    692   } catch (e) {
    693     // If the library cannot be found on this system let's not panic.
    694     this.symbols = '';
    695   }
    696 };
    697 
    698 
    699 function WindowsCppEntriesProvider(_ignored_nmExec, targetRootFS) {
    700   this.targetRootFS = targetRootFS;
    701   this.symbols = '';
    702   this.parsePos = 0;
    703 };
    704 inherits(WindowsCppEntriesProvider, CppEntriesProvider);
    705 
    706 
    707 WindowsCppEntriesProvider.FILENAME_RE = /^(.*)\.([^.]+)$/;
    708 
    709 
    710 WindowsCppEntriesProvider.FUNC_RE =
    711     /^\s+0001:[0-9a-fA-F]{8}\s+([_\?@$0-9a-zA-Z]+)\s+([0-9a-fA-F]{8}).*$/;
    712 
    713 
    714 WindowsCppEntriesProvider.IMAGE_BASE_RE =
    715     /^\s+0000:00000000\s+___ImageBase\s+([0-9a-fA-F]{8}).*$/;
    716 
    717 
    718 // This is almost a constant on Windows.
    719 WindowsCppEntriesProvider.EXE_IMAGE_BASE = 0x00400000;
    720 
    721 
    722 WindowsCppEntriesProvider.prototype.loadSymbols = function(libName) {
    723   libName = this.targetRootFS + libName;
    724   var fileNameFields = libName.match(WindowsCppEntriesProvider.FILENAME_RE);
    725   if (!fileNameFields) return;
    726   var mapFileName = fileNameFields[1] + '.map';
    727   this.moduleType_ = fileNameFields[2].toLowerCase();
    728   try {
    729     this.symbols = read(mapFileName);
    730   } catch (e) {
    731     // If .map file cannot be found let's not panic.
    732     this.symbols = '';
    733   }
    734 };
    735 
    736 
    737 WindowsCppEntriesProvider.prototype.parseNextLine = function() {
    738   var lineEndPos = this.symbols.indexOf('\r\n', this.parsePos);
    739   if (lineEndPos == -1) {
    740     return false;
    741   }
    742 
    743   var line = this.symbols.substring(this.parsePos, lineEndPos);
    744   this.parsePos = lineEndPos + 2;
    745 
    746   // Image base entry is above all other symbols, so we can just
    747   // terminate parsing.
    748   var imageBaseFields = line.match(WindowsCppEntriesProvider.IMAGE_BASE_RE);
    749   if (imageBaseFields) {
    750     var imageBase = parseInt(imageBaseFields[1], 16);
    751     if ((this.moduleType_ == 'exe') !=
    752         (imageBase == WindowsCppEntriesProvider.EXE_IMAGE_BASE)) {
    753       return false;
    754     }
    755   }
    756 
    757   var fields = line.match(WindowsCppEntriesProvider.FUNC_RE);
    758   return fields ?
    759       { name: this.unmangleName(fields[1]), start: parseInt(fields[2], 16) } :
    760       null;
    761 };
    762 
    763 
    764 /**
    765  * Performs very simple unmangling of C++ names.
    766  *
    767  * Does not handle arguments and template arguments. The mangled names have
    768  * the form:
    769  *
    770  *   ?LookupInDescriptor@JSObject@internal@v8@@...arguments info...
    771  */
    772 WindowsCppEntriesProvider.prototype.unmangleName = function(name) {
    773   // Empty or non-mangled name.
    774   if (name.length < 1 || name.charAt(0) != '?') return name;
    775   var nameEndPos = name.indexOf('@@');
    776   var components = name.substring(1, nameEndPos).split('@');
    777   components.reverse();
    778   return components.join('::');
    779 };
    780 
    781 
    782 function ArgumentsProcessor(args) {
    783   this.args_ = args;
    784   this.result_ = ArgumentsProcessor.DEFAULTS;
    785 
    786   this.argsDispatch_ = {
    787     '-j': ['stateFilter', TickProcessor.VmStates.JS,
    788         'Show only ticks from JS VM state'],
    789     '-g': ['stateFilter', TickProcessor.VmStates.GC,
    790         'Show only ticks from GC VM state'],
    791     '-c': ['stateFilter', TickProcessor.VmStates.COMPILER,
    792         'Show only ticks from COMPILER VM state'],
    793     '-o': ['stateFilter', TickProcessor.VmStates.OTHER,
    794         'Show only ticks from OTHER VM state'],
    795     '-e': ['stateFilter', TickProcessor.VmStates.EXTERNAL,
    796         'Show only ticks from EXTERNAL VM state'],
    797     '--filter-runtime-timer': ['runtimeTimerFilter', null,
    798             'Show only ticks matching the given runtime timer scope'],
    799     '--call-graph-size': ['callGraphSize', TickProcessor.CALL_GRAPH_SIZE,
    800         'Set the call graph size'],
    801     '--ignore-unknown': ['ignoreUnknown', true,
    802         'Exclude ticks of unknown code entries from processing'],
    803     '--separate-ic': ['separateIc', true,
    804         'Separate IC entries'],
    805     '--unix': ['platform', 'unix',
    806         'Specify that we are running on *nix platform'],
    807     '--windows': ['platform', 'windows',
    808         'Specify that we are running on Windows platform'],
    809     '--mac': ['platform', 'mac',
    810         'Specify that we are running on Mac OS X platform'],
    811     '--nm': ['nm', 'nm',
    812         'Specify the \'nm\' executable to use (e.g. --nm=/my_dir/nm)'],
    813     '--target': ['targetRootFS', '',
    814         'Specify the target root directory for cross environment'],
    815     '--range': ['range', 'auto,auto',
    816         'Specify the range limit as [start],[end]'],
    817     '--distortion': ['distortion', 0,
    818         'Specify the logging overhead in picoseconds'],
    819     '--source-map': ['sourceMap', null,
    820         'Specify the source map that should be used for output'],
    821     '--timed-range': ['timedRange', true,
    822         'Ignore ticks before first and after last Date.now() call'],
    823     '--pairwise-timed-range': ['pairwiseTimedRange', true,
    824         'Ignore ticks outside pairs of Date.now() calls'],
    825     '--only-summary': ['onlySummary', true,
    826         'Print only tick summary, exclude other information']
    827   };
    828   this.argsDispatch_['--js'] = this.argsDispatch_['-j'];
    829   this.argsDispatch_['--gc'] = this.argsDispatch_['-g'];
    830   this.argsDispatch_['--compiler'] = this.argsDispatch_['-c'];
    831   this.argsDispatch_['--other'] = this.argsDispatch_['-o'];
    832   this.argsDispatch_['--external'] = this.argsDispatch_['-e'];
    833   this.argsDispatch_['--ptr'] = this.argsDispatch_['--pairwise-timed-range'];
    834 };
    835 
    836 
    837 ArgumentsProcessor.DEFAULTS = {
    838   logFileName: 'v8.log',
    839   platform: 'unix',
    840   stateFilter: null,
    841   callGraphSize: 5,
    842   ignoreUnknown: false,
    843   separateIc: false,
    844   targetRootFS: '',
    845   nm: 'nm',
    846   range: 'auto,auto',
    847   distortion: 0,
    848   timedRange: false,
    849   pairwiseTimedRange: false,
    850   onlySummary: false,
    851   runtimeTimerFilter: null,
    852 };
    853 
    854 
    855 ArgumentsProcessor.prototype.parse = function() {
    856   while (this.args_.length) {
    857     var arg = this.args_.shift();
    858     if (arg.charAt(0) != '-') {
    859       this.result_.logFileName = arg;
    860       continue;
    861     }
    862     var userValue = null;
    863     var eqPos = arg.indexOf('=');
    864     if (eqPos != -1) {
    865       userValue = arg.substr(eqPos + 1);
    866       arg = arg.substr(0, eqPos);
    867     }
    868     if (arg in this.argsDispatch_) {
    869       var dispatch = this.argsDispatch_[arg];
    870       this.result_[dispatch[0]] = userValue == null ? dispatch[1] : userValue;
    871     } else {
    872       return false;
    873     }
    874   }
    875   return true;
    876 };
    877 
    878 
    879 ArgumentsProcessor.prototype.result = function() {
    880   return this.result_;
    881 };
    882 
    883 
    884 ArgumentsProcessor.prototype.printUsageAndExit = function() {
    885 
    886   function padRight(s, len) {
    887     s = s.toString();
    888     if (s.length < len) {
    889       s = s + (new Array(len - s.length + 1).join(' '));
    890     }
    891     return s;
    892   }
    893 
    894   print('Cmdline args: [options] [log-file-name]\n' +
    895         'Default log file name is "' +
    896         ArgumentsProcessor.DEFAULTS.logFileName + '".\n');
    897   print('Options:');
    898   for (var arg in this.argsDispatch_) {
    899     var synonyms = [arg];
    900     var dispatch = this.argsDispatch_[arg];
    901     for (var synArg in this.argsDispatch_) {
    902       if (arg !== synArg && dispatch === this.argsDispatch_[synArg]) {
    903         synonyms.push(synArg);
    904         delete this.argsDispatch_[synArg];
    905       }
    906     }
    907     print('  ' + padRight(synonyms.join(', '), 20) + " " + dispatch[2]);
    908   }
    909   quit(2);
    910 };
    911