• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
29function inherits(childCtor, parentCtor) {
30  childCtor.prototype.__proto__ = parentCtor.prototype;
31};
32
33
34function V8Profile(separateIc) {
35  Profile.call(this);
36  if (!separateIc) {
37    this.skipThisFunction = function(name) { return V8Profile.IC_RE.test(name); };
38  }
39};
40inherits(V8Profile, Profile);
41
42
43V8Profile.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 */
50function 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 */
63function 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
73function 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}
106inherits(SnapshotLogProcessor, LogReader);
107
108
109SnapshotLogProcessor.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
121SnapshotLogProcessor.prototype.processCodeMove = function(from, to) {
122  this.profile_.moveCode(from, to);
123};
124
125
126SnapshotLogProcessor.prototype.processCodeDelete = function(start) {
127  this.profile_.deleteCode(start);
128};
129
130
131SnapshotLogProcessor.prototype.processSnapshotPosition = function(addr, pos) {
132  this.serializedEntries_[pos] = this.profile_.findEntry(addr);
133};
134
135
136SnapshotLogProcessor.prototype.processLogFile = function(fileName) {
137  var contents = readFile(fileName);
138  this.processLogChunk(contents);
139};
140
141
142SnapshotLogProcessor.prototype.getSerializedEntryName = function(pos) {
143  var entry = this.serializedEntries_[pos];
144  return entry ? entry.getRawName() : null;
145};
146
147
148function 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};
226inherits(TickProcessor, LogReader);
227
228
229TickProcessor.VmStates = {
230  JS: 0,
231  GC: 1,
232  COMPILER: 2,
233  OTHER: 3,
234  EXTERNAL: 4
235};
236
237
238TickProcessor.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
246TickProcessor.CALL_PROFILE_CUTOFF_PCT = 2.0;
247
248
249/**
250 * @override
251 */
252TickProcessor.prototype.printError = function(str) {
253  print(str);
254};
255
256
257TickProcessor.prototype.setCodeType = function(name, type) {
258  this.codeTypes_[name] = TickProcessor.CodeTypes[type];
259};
260
261
262TickProcessor.prototype.isSharedLibrary = function(name) {
263  return this.codeTypes_[name] == TickProcessor.CodeTypes.SHARED_LIB;
264};
265
266
267TickProcessor.prototype.isCppCode = function(name) {
268  return this.codeTypes_[name] == TickProcessor.CodeTypes.CPP;
269};
270
271
272TickProcessor.prototype.isJsCode = function(name) {
273  return !(name in this.codeTypes_);
274};
275
276
277TickProcessor.prototype.processLogFile = function(fileName) {
278  this.lastLogFileName_ = fileName;
279  var line;
280  while (line = readline()) {
281    this.processLogLine(line);
282  }
283};
284
285
286TickProcessor.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
294TickProcessor.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
308TickProcessor.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
321TickProcessor.prototype.processCodeMove = function(from, to) {
322  this.profile_.moveCode(from, to);
323};
324
325
326TickProcessor.prototype.processCodeDelete = function(start) {
327  this.profile_.deleteCode(start);
328};
329
330
331TickProcessor.prototype.processFunctionMove = function(from, to) {
332  this.profile_.moveFunc(from, to);
333};
334
335
336TickProcessor.prototype.processSnapshotPosition = function(addr, pos) {
337  if (this.snapshotLogProcessor_) {
338    this.deserializedEntriesNames_[addr] =
339      this.snapshotLogProcessor_.getSerializedEntryName(pos);
340  }
341};
342
343
344TickProcessor.prototype.includeTick = function(vmState) {
345  return this.stateFilter_ == null || this.stateFilter_ == vmState;
346};
347
348TickProcessor.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
380TickProcessor.prototype.processHeapSampleBegin = function(space, state, ticks) {
381  if (space != 'Heap') return;
382  this.currentProducerProfile_ = new CallTree();
383};
384
385
386TickProcessor.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
404TickProcessor.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
415TickProcessor.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
480function 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
493TickProcessor.prototype.printHeader = function(headerTitle) {
494  print('\n [' + headerTitle + ']:');
495  print('   ticks  total  nonlib   name');
496};
497
498
499TickProcessor.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
511TickProcessor.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
517TickProcessor.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
529TickProcessor.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
543TickProcessor.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
565function CppEntriesProvider() {
566};
567
568
569CppEntriesProvider.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
611CppEntriesProvider.prototype.loadSymbols = function(libName) {
612};
613
614
615CppEntriesProvider.prototype.parseNextLine = function() {
616  return false;
617};
618
619
620function 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};
626inherits(UnixCppEntriesProvider, CppEntriesProvider);
627
628
629UnixCppEntriesProvider.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
643UnixCppEntriesProvider.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
668function 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};
673inherits(MacCppEntriesProvider, UnixCppEntriesProvider);
674
675
676MacCppEntriesProvider.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
687function WindowsCppEntriesProvider() {
688  this.symbols = '';
689  this.parsePos = 0;
690};
691inherits(WindowsCppEntriesProvider, CppEntriesProvider);
692
693
694WindowsCppEntriesProvider.FILENAME_RE = /^(.*)\.([^.]+)$/;
695
696
697WindowsCppEntriesProvider.FUNC_RE =
698    /^\s+0001:[0-9a-fA-F]{8}\s+([_\?@$0-9a-zA-Z]+)\s+([0-9a-fA-F]{8}).*$/;
699
700
701WindowsCppEntriesProvider.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.
706WindowsCppEntriesProvider.EXE_IMAGE_BASE = 0x00400000;
707
708
709WindowsCppEntriesProvider.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
723WindowsCppEntriesProvider.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 */
758WindowsCppEntriesProvider.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
768function 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
806ArgumentsProcessor.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
817ArgumentsProcessor.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
845ArgumentsProcessor.prototype.result = function() {
846  return this.result_;
847};
848
849
850ArgumentsProcessor.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