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