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