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