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