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