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