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