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