blob: db2f3c9b9048242a305778789ba6a4d3b6644e63 [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
Steve Block1e0659c2011-05-24 12:43:12 +010029function inherits(childCtor, parentCtor) {
30 childCtor.prototype.__proto__ = parentCtor.prototype;
31};
32
33
34function V8Profile(separateIc) {
35 Profile.call(this);
Steve Blocka7e24c12009-10-30 11:49:00 +000036 if (!separateIc) {
Steve Block1e0659c2011-05-24 12:43:12 +010037 this.skipThisFunction = function(name) { return V8Profile.IC_RE.test(name); };
Steve Blocka7e24c12009-10-30 11:49:00 +000038 }
39};
Steve Block1e0659c2011-05-24 12:43:12 +010040inherits(V8Profile, Profile);
Steve Blocka7e24c12009-10-30 11:49:00 +000041
42
Steve Block1e0659c2011-05-24 12:43:12 +010043V8Profile.IC_RE =
Steve Blocka7e24c12009-10-30 11:49:00 +000044 /^(?:CallIC|LoadIC|StoreIC)|(?:Builtin: (?:Keyed)?(?:Call|Load|Store)IC_)/;
45
46
47/**
48 * A thin wrapper around shell's 'read' function showing a file name on error.
49 */
50function readFile(fileName) {
51 try {
52 return read(fileName);
53 } catch (e) {
54 print(fileName + ': ' + (e.message || e));
55 throw e;
56 }
57}
58
59
Leon Clarkee46be812010-01-19 14:06:41 +000060function SnapshotLogProcessor() {
Steve Block1e0659c2011-05-24 12:43:12 +010061 LogReader.call(this, {
Leon Clarkee46be812010-01-19 14:06:41 +000062 'code-creation': {
Ben Murdochb0fe1622011-05-05 13:52:32 +010063 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 },
Andrei Popescu31002712010-02-23 13:46:05 +000069 'function-creation': null,
70 'function-move': null,
71 'function-delete': null,
Ben Murdochb0fe1622011-05-05 13:52:32 +010072 'snapshot-pos': { parsers: [parseInt, parseInt],
73 processor: this.processSnapshotPosition }});
Leon Clarkee46be812010-01-19 14:06:41 +000074
Steve Block1e0659c2011-05-24 12:43:12 +010075 V8Profile.prototype.handleUnknownCode = function(operation, addr) {
76 var op = Profile.Operation;
Leon Clarkee46be812010-01-19 14:06:41 +000077 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
Steve Block1e0659c2011-05-24 12:43:12 +010089 this.profile_ = new V8Profile();
Leon Clarkee46be812010-01-19 14:06:41 +000090 this.serializedEntries_ = [];
91}
Steve Block1e0659c2011-05-24 12:43:12 +010092inherits(SnapshotLogProcessor, LogReader);
Leon Clarkee46be812010-01-19 14:06:41 +000093
94
95SnapshotLogProcessor.prototype.processCodeCreation = function(
96 type, start, size, name) {
Ben Murdochb0fe1622011-05-05 13:52:32 +010097 var entry = this.profile_.addCode(type, name, start, size);
Leon Clarkee46be812010-01-19 14:06:41 +000098};
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;
Steve Blocka7e24c12009-10-30 11:49:00 +0000125};
126
127
128function TickProcessor(
Leon Clarkee46be812010-01-19 14:06:41 +0000129 cppEntriesProvider, separateIc, ignoreUnknown, stateFilter, snapshotLogProcessor) {
Steve Block1e0659c2011-05-24 12:43:12 +0100130 LogReader.call(this, {
Steve Blocka7e24c12009-10-30 11:49:00 +0000131 'shared-library': { parsers: [null, parseInt, parseInt],
132 processor: this.processSharedLibrary },
133 'code-creation': {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100134 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 },
Steve Block3ce2e202009-11-05 08:53:23 +0000150 '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'],
Ben Murdochb0fe1622011-05-05 13:52:32 +0100155 processor: this.processJSProducer },
Steve Block3ce2e202009-11-05 08:53:23 +0000156 // Ignored events.
Steve Blocka7e24c12009-10-30 11:49:00 +0000157 'profiler': null,
Steve Block3ce2e202009-11-05 08:53:23 +0000158 'heap-sample-stats': null,
159 'heap-sample-item': null,
160 'heap-js-cons-item': null,
161 'heap-js-ret-item': null,
Steve Blocka7e24c12009-10-30 11:49:00 +0000162 // 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;
Leon Clarkee46be812010-01-19 14:06:41 +0000170 this.snapshotLogProcessor_ = snapshotLogProcessor;
171 this.deserializedEntriesNames_ = [];
Steve Blocka7e24c12009-10-30 11:49:00 +0000172 var ticks = this.ticks_ =
173 { total: 0, unaccounted: 0, excluded: 0, gc: 0 };
174
Steve Block1e0659c2011-05-24 12:43:12 +0100175 V8Profile.prototype.handleUnknownCode = function(
Steve Blocka7e24c12009-10-30 11:49:00 +0000176 operation, addr, opt_stackPos) {
Steve Block1e0659c2011-05-24 12:43:12 +0100177 var op = Profile.Operation;
Steve Blocka7e24c12009-10-30 11:49:00 +0000178 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
Steve Block1e0659c2011-05-24 12:43:12 +0100196 this.profile_ = new V8Profile(separateIc);
Steve Blocka7e24c12009-10-30 11:49:00 +0000197 this.codeTypes_ = {};
198 // Count each tick as a time unit.
Steve Block1e0659c2011-05-24 12:43:12 +0100199 this.viewBuilder_ = new ViewBuilder(1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000200 this.lastLogFileName_ = null;
Steve Block3ce2e202009-11-05 08:53:23 +0000201
202 this.generation_ = 1;
203 this.currentProducerProfile_ = null;
Steve Blocka7e24c12009-10-30 11:49:00 +0000204};
Steve Block1e0659c2011-05-24 12:43:12 +0100205inherits(TickProcessor, LogReader);
Steve Blocka7e24c12009-10-30 11:49:00 +0000206
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;
Andrei Popescu31002712010-02-23 13:46:05 +0000258 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';
Steve Blocka7e24c12009-10-30 11:49:00 +0000268 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) {
Leon Clarkee46be812010-01-19 14:06:41 +0000289 name = this.deserializedEntriesNames_[start] || name;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100290 var entry = this.profile_.addCode(type, name, start, size);
Steve Blocka7e24c12009-10-30 11:49:00 +0000291};
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
Leon Clarked91b9f72010-01-27 17:25:45 +0000304TickProcessor.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
Leon Clarkee46be812010-01-19 14:06:41 +0000320TickProcessor.prototype.processSnapshotPosition = function(addr, pos) {
321 if (this.snapshotLogProcessor_) {
322 this.deserializedEntriesNames_[addr] =
323 this.snapshotLogProcessor_.getSerializedEntryName(pos);
324 }
325};
326
327
Steve Blocka7e24c12009-10-30 11:49:00 +0000328TickProcessor.prototype.includeTick = function(vmState) {
329 return this.stateFilter_ == null || this.stateFilter_ == vmState;
330};
331
332
Leon Clarked91b9f72010-01-27 17:25:45 +0000333TickProcessor.prototype.processTick = function(pc, sp, func, vmState, stack) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000334 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
Leon Clarked91b9f72010-01-27 17:25:45 +0000341 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));
Steve Blocka7e24c12009-10-30 11:49:00 +0000354};
355
356
Steve Block3ce2e202009-11-05 08:53:23 +0000357TickProcessor.prototype.processHeapSampleBegin = function(space, state, ticks) {
358 if (space != 'Heap') return;
Steve Block1e0659c2011-05-24 12:43:12 +0100359 this.currentProducerProfile_ = new CallTree();
Steve Block3ce2e202009-11-05 08:53:23 +0000360};
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 =
Leon Clarked91b9f72010-01-27 17:25:45 +0000386 this.profile_.resolveAndFilterFuncs_(this.processStack(first, 0, stack));
Steve Block3ce2e202009-11-05 08:53:23 +0000387 processedStack.unshift(constructor);
388 this.currentProducerProfile_.addPath(processedStack);
389};
390
391
Steve Blocka7e24c12009-10-30 11:49:00 +0000392TickProcessor.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',
Leon Clarkee46be812010-01-19 14:06:41 +0000771 '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)']
Steve Blocka7e24c12009-10-30 11:49:00 +0000774 };
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',
Leon Clarkee46be812010-01-19 14:06:41 +0000785 snapshotLogFileName: null,
Steve Blocka7e24c12009-10-30 11:49:00 +0000786 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