blob: 7a05ef1e1f29bb021232084f783f6fd2f57f7681 [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
348
Steve Block44f0eee2011-05-26 01:26:41 +0100349TickProcessor.prototype.processTick = function(pc,
350 sp,
351 is_external_callback,
352 tos_or_external_callback,
353 vmState,
354 stack) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000355 this.ticks_.total++;
356 if (vmState == TickProcessor.VmStates.GC) this.ticks_.gc++;
357 if (!this.includeTick(vmState)) {
358 this.ticks_.excluded++;
359 return;
360 }
Steve Block44f0eee2011-05-26 01:26:41 +0100361 if (is_external_callback) {
362 // Don't use PC when in external callback code, as it can point
363 // inside callback's code, and we will erroneously report
364 // that a callback calls itself.
365 pc = 0;
366 } else if (tos_or_external_callback) {
367 // Find out, if top of stack was pointing inside a JS function
368 // meaning that we have encountered a frameless invocation.
369 var funcEntry = this.profile_.findEntry(tos_or_external_callback);
Leon Clarked91b9f72010-01-27 17:25:45 +0000370 if (!funcEntry || !funcEntry.isJSFunction || !funcEntry.isJSFunction()) {
Steve Block44f0eee2011-05-26 01:26:41 +0100371 tos_or_external_callback = 0;
Leon Clarked91b9f72010-01-27 17:25:45 +0000372 }
373 }
374
Steve Block44f0eee2011-05-26 01:26:41 +0100375 this.profile_.recordTick(this.processStack(pc, tos_or_external_callback, stack));
Steve Blocka7e24c12009-10-30 11:49:00 +0000376};
377
378
Steve Block3ce2e202009-11-05 08:53:23 +0000379TickProcessor.prototype.processHeapSampleBegin = function(space, state, ticks) {
380 if (space != 'Heap') return;
Steve Block1e0659c2011-05-24 12:43:12 +0100381 this.currentProducerProfile_ = new CallTree();
Steve Block3ce2e202009-11-05 08:53:23 +0000382};
383
384
385TickProcessor.prototype.processHeapSampleEnd = function(space, state) {
386 if (space != 'Heap' || !this.currentProducerProfile_) return;
387
388 print('Generation ' + this.generation_ + ':');
389 var tree = this.currentProducerProfile_;
390 tree.computeTotalWeights();
391 var producersView = this.viewBuilder_.buildView(tree);
392 // Sort by total time, desc, then by name, desc.
393 producersView.sort(function(rec1, rec2) {
394 return rec2.totalTime - rec1.totalTime ||
395 (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
396 this.printHeavyProfile(producersView.head.children);
397
398 this.currentProducerProfile_ = null;
399 this.generation_++;
400};
401
402
403TickProcessor.prototype.processJSProducer = function(constructor, stack) {
404 if (!this.currentProducerProfile_) return;
405 if (stack.length == 0) return;
406 var first = stack.shift();
407 var processedStack =
Leon Clarked91b9f72010-01-27 17:25:45 +0000408 this.profile_.resolveAndFilterFuncs_(this.processStack(first, 0, stack));
Steve Block3ce2e202009-11-05 08:53:23 +0000409 processedStack.unshift(constructor);
410 this.currentProducerProfile_.addPath(processedStack);
411};
412
413
Steve Blocka7e24c12009-10-30 11:49:00 +0000414TickProcessor.prototype.printStatistics = function() {
415 print('Statistical profiling result from ' + this.lastLogFileName_ +
416 ', (' + this.ticks_.total +
417 ' ticks, ' + this.ticks_.unaccounted + ' unaccounted, ' +
418 this.ticks_.excluded + ' excluded).');
419
420 if (this.ticks_.total == 0) return;
421
422 // Print the unknown ticks percentage if they are not ignored.
423 if (!this.ignoreUnknown_ && this.ticks_.unaccounted > 0) {
424 this.printHeader('Unknown');
425 this.printCounter(this.ticks_.unaccounted, this.ticks_.total);
426 }
427
428 var flatProfile = this.profile_.getFlatProfile();
429 var flatView = this.viewBuilder_.buildView(flatProfile);
430 // Sort by self time, desc, then by name, desc.
431 flatView.sort(function(rec1, rec2) {
432 return rec2.selfTime - rec1.selfTime ||
433 (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
434 var totalTicks = this.ticks_.total;
435 if (this.ignoreUnknown_) {
436 totalTicks -= this.ticks_.unaccounted;
437 }
438 // Our total time contains all the ticks encountered,
439 // while profile only knows about the filtered ticks.
440 flatView.head.totalTime = totalTicks;
441
442 // Count library ticks
443 var flatViewNodes = flatView.head.children;
444 var self = this;
445 var libraryTicks = 0;
446 this.processProfile(flatViewNodes,
447 function(name) { return self.isSharedLibrary(name); },
448 function(rec) { libraryTicks += rec.selfTime; });
449 var nonLibraryTicks = totalTicks - libraryTicks;
450
451 this.printHeader('Shared libraries');
452 this.printEntries(flatViewNodes, null,
453 function(name) { return self.isSharedLibrary(name); });
454
455 this.printHeader('JavaScript');
456 this.printEntries(flatViewNodes, nonLibraryTicks,
457 function(name) { return self.isJsCode(name); });
458
459 this.printHeader('C++');
460 this.printEntries(flatViewNodes, nonLibraryTicks,
461 function(name) { return self.isCppCode(name); });
462
463 this.printHeader('GC');
464 this.printCounter(this.ticks_.gc, totalTicks);
465
466 this.printHeavyProfHeader();
467 var heavyProfile = this.profile_.getBottomUpProfile();
468 var heavyView = this.viewBuilder_.buildView(heavyProfile);
469 // To show the same percentages as in the flat profile.
470 heavyView.head.totalTime = totalTicks;
471 // Sort by total time, desc, then by name, desc.
472 heavyView.sort(function(rec1, rec2) {
473 return rec2.totalTime - rec1.totalTime ||
474 (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
475 this.printHeavyProfile(heavyView.head.children);
476};
477
478
479function padLeft(s, len) {
480 s = s.toString();
481 if (s.length < len) {
482 var padLength = len - s.length;
483 if (!(padLength in padLeft)) {
484 padLeft[padLength] = new Array(padLength + 1).join(' ');
485 }
486 s = padLeft[padLength] + s;
487 }
488 return s;
489};
490
491
492TickProcessor.prototype.printHeader = function(headerTitle) {
493 print('\n [' + headerTitle + ']:');
494 print(' ticks total nonlib name');
495};
496
497
498TickProcessor.prototype.printHeavyProfHeader = function() {
499 print('\n [Bottom up (heavy) profile]:');
500 print(' Note: percentage shows a share of a particular caller in the ' +
501 'total\n' +
502 ' amount of its parent calls.');
503 print(' Callers occupying less than ' +
504 TickProcessor.CALL_PROFILE_CUTOFF_PCT.toFixed(1) +
505 '% are not shown.\n');
506 print(' ticks parent name');
507};
508
509
510TickProcessor.prototype.printCounter = function(ticksCount, totalTicksCount) {
511 var pct = ticksCount * 100.0 / totalTicksCount;
512 print(' ' + padLeft(ticksCount, 5) + ' ' + padLeft(pct.toFixed(1), 5) + '%');
513};
514
515
516TickProcessor.prototype.processProfile = function(
517 profile, filterP, func) {
518 for (var i = 0, n = profile.length; i < n; ++i) {
519 var rec = profile[i];
520 if (!filterP(rec.internalFuncName)) {
521 continue;
522 }
523 func(rec);
524 }
525};
526
527
528TickProcessor.prototype.printEntries = function(
529 profile, nonLibTicks, filterP) {
530 this.processProfile(profile, filterP, function (rec) {
531 if (rec.selfTime == 0) return;
532 var nonLibPct = nonLibTicks != null ?
533 rec.selfTime * 100.0 / nonLibTicks : 0.0;
534 print(' ' + padLeft(rec.selfTime, 5) + ' ' +
535 padLeft(rec.selfPercent.toFixed(1), 5) + '% ' +
536 padLeft(nonLibPct.toFixed(1), 5) + '% ' +
537 rec.internalFuncName);
538 });
539};
540
541
542TickProcessor.prototype.printHeavyProfile = function(profile, opt_indent) {
543 var self = this;
544 var indent = opt_indent || 0;
545 var indentStr = padLeft('', indent);
546 this.processProfile(profile, function() { return true; }, function (rec) {
547 // Cut off too infrequent callers.
548 if (rec.parentTotalPercent < TickProcessor.CALL_PROFILE_CUTOFF_PCT) return;
549 print(' ' + padLeft(rec.totalTime, 5) + ' ' +
550 padLeft(rec.parentTotalPercent.toFixed(1), 5) + '% ' +
551 indentStr + rec.internalFuncName);
552 // Limit backtrace depth.
553 if (indent < 10) {
554 self.printHeavyProfile(rec.children, indent + 2);
555 }
556 // Delimit top-level functions.
557 if (indent == 0) {
558 print('');
559 }
560 });
561};
562
563
564function CppEntriesProvider() {
565};
566
567
568CppEntriesProvider.prototype.parseVmSymbols = function(
569 libName, libStart, libEnd, processorFunc) {
570 this.loadSymbols(libName);
571
572 var prevEntry;
573
574 function addEntry(funcInfo) {
575 // Several functions can be mapped onto the same address. To avoid
576 // creating zero-sized entries, skip such duplicates.
577 // Also double-check that function belongs to the library address space.
578 if (prevEntry && !prevEntry.end &&
579 prevEntry.start < funcInfo.start &&
580 prevEntry.start >= libStart && funcInfo.start <= libEnd) {
581 processorFunc(prevEntry.name, prevEntry.start, funcInfo.start);
582 }
583 if (funcInfo.end &&
584 (!prevEntry || prevEntry.start != funcInfo.start) &&
585 funcInfo.start >= libStart && funcInfo.end <= libEnd) {
586 processorFunc(funcInfo.name, funcInfo.start, funcInfo.end);
587 }
588 prevEntry = funcInfo;
589 }
590
591 while (true) {
592 var funcInfo = this.parseNextLine();
593 if (funcInfo === null) {
594 continue;
595 } else if (funcInfo === false) {
596 break;
597 }
598 if (funcInfo.start < libStart && funcInfo.start < libEnd - libStart) {
599 funcInfo.start += libStart;
600 }
601 if (funcInfo.size) {
602 funcInfo.end = funcInfo.start + funcInfo.size;
603 }
604 addEntry(funcInfo);
605 }
606 addEntry({name: '', start: libEnd});
607};
608
609
610CppEntriesProvider.prototype.loadSymbols = function(libName) {
611};
612
613
614CppEntriesProvider.prototype.parseNextLine = function() {
615 return false;
616};
617
618
619function UnixCppEntriesProvider(nmExec) {
620 this.symbols = [];
621 this.parsePos = 0;
622 this.nmExec = nmExec;
623 this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ([0-9a-fA-F]{8,16} )?[tTwW] (.*)$/;
624};
625inherits(UnixCppEntriesProvider, CppEntriesProvider);
626
627
628UnixCppEntriesProvider.prototype.loadSymbols = function(libName) {
629 this.parsePos = 0;
630 try {
631 this.symbols = [
632 os.system(this.nmExec, ['-C', '-n', '-S', libName], -1, -1),
633 os.system(this.nmExec, ['-C', '-n', '-S', '-D', libName], -1, -1)
634 ];
635 } catch (e) {
636 // If the library cannot be found on this system let's not panic.
637 this.symbols = ['', ''];
638 }
639};
640
641
642UnixCppEntriesProvider.prototype.parseNextLine = function() {
643 if (this.symbols.length == 0) {
644 return false;
645 }
646 var lineEndPos = this.symbols[0].indexOf('\n', this.parsePos);
647 if (lineEndPos == -1) {
648 this.symbols.shift();
649 this.parsePos = 0;
650 return this.parseNextLine();
651 }
652
653 var line = this.symbols[0].substring(this.parsePos, lineEndPos);
654 this.parsePos = lineEndPos + 1;
655 var fields = line.match(this.FUNC_RE);
656 var funcInfo = null;
657 if (fields) {
658 funcInfo = { name: fields[3], start: parseInt(fields[1], 16) };
659 if (fields[2]) {
660 funcInfo.size = parseInt(fields[2], 16);
661 }
662 }
663 return funcInfo;
664};
665
666
667function MacCppEntriesProvider(nmExec) {
668 UnixCppEntriesProvider.call(this, nmExec);
669 // Note an empty group. It is required, as UnixCppEntriesProvider expects 3 groups.
670 this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ()[iItT] (.*)$/;
671};
672inherits(MacCppEntriesProvider, UnixCppEntriesProvider);
673
674
675MacCppEntriesProvider.prototype.loadSymbols = function(libName) {
676 this.parsePos = 0;
677 try {
678 this.symbols = [os.system(this.nmExec, ['-n', '-f', libName], -1, -1), ''];
679 } catch (e) {
680 // If the library cannot be found on this system let's not panic.
681 this.symbols = '';
682 }
683};
684
685
686function WindowsCppEntriesProvider() {
687 this.symbols = '';
688 this.parsePos = 0;
689};
690inherits(WindowsCppEntriesProvider, CppEntriesProvider);
691
692
693WindowsCppEntriesProvider.FILENAME_RE = /^(.*)\.([^.]+)$/;
694
695
696WindowsCppEntriesProvider.FUNC_RE =
697 /^\s+0001:[0-9a-fA-F]{8}\s+([_\?@$0-9a-zA-Z]+)\s+([0-9a-fA-F]{8}).*$/;
698
699
700WindowsCppEntriesProvider.IMAGE_BASE_RE =
701 /^\s+0000:00000000\s+___ImageBase\s+([0-9a-fA-F]{8}).*$/;
702
703
704// This is almost a constant on Windows.
705WindowsCppEntriesProvider.EXE_IMAGE_BASE = 0x00400000;
706
707
708WindowsCppEntriesProvider.prototype.loadSymbols = function(libName) {
709 var fileNameFields = libName.match(WindowsCppEntriesProvider.FILENAME_RE);
710 if (!fileNameFields) return;
711 var mapFileName = fileNameFields[1] + '.map';
712 this.moduleType_ = fileNameFields[2].toLowerCase();
713 try {
714 this.symbols = read(mapFileName);
715 } catch (e) {
716 // If .map file cannot be found let's not panic.
717 this.symbols = '';
718 }
719};
720
721
722WindowsCppEntriesProvider.prototype.parseNextLine = function() {
723 var lineEndPos = this.symbols.indexOf('\r\n', this.parsePos);
724 if (lineEndPos == -1) {
725 return false;
726 }
727
728 var line = this.symbols.substring(this.parsePos, lineEndPos);
729 this.parsePos = lineEndPos + 2;
730
731 // Image base entry is above all other symbols, so we can just
732 // terminate parsing.
733 var imageBaseFields = line.match(WindowsCppEntriesProvider.IMAGE_BASE_RE);
734 if (imageBaseFields) {
735 var imageBase = parseInt(imageBaseFields[1], 16);
736 if ((this.moduleType_ == 'exe') !=
737 (imageBase == WindowsCppEntriesProvider.EXE_IMAGE_BASE)) {
738 return false;
739 }
740 }
741
742 var fields = line.match(WindowsCppEntriesProvider.FUNC_RE);
743 return fields ?
744 { name: this.unmangleName(fields[1]), start: parseInt(fields[2], 16) } :
745 null;
746};
747
748
749/**
750 * Performs very simple unmangling of C++ names.
751 *
752 * Does not handle arguments and template arguments. The mangled names have
753 * the form:
754 *
755 * ?LookupInDescriptor@JSObject@internal@v8@@...arguments info...
756 */
757WindowsCppEntriesProvider.prototype.unmangleName = function(name) {
758 // Empty or non-mangled name.
759 if (name.length < 1 || name.charAt(0) != '?') return name;
760 var nameEndPos = name.indexOf('@@');
761 var components = name.substring(1, nameEndPos).split('@');
762 components.reverse();
763 return components.join('::');
764};
765
766
767function ArgumentsProcessor(args) {
768 this.args_ = args;
769 this.result_ = ArgumentsProcessor.DEFAULTS;
770
771 this.argsDispatch_ = {
772 '-j': ['stateFilter', TickProcessor.VmStates.JS,
773 'Show only ticks from JS VM state'],
774 '-g': ['stateFilter', TickProcessor.VmStates.GC,
775 'Show only ticks from GC VM state'],
776 '-c': ['stateFilter', TickProcessor.VmStates.COMPILER,
777 'Show only ticks from COMPILER VM state'],
778 '-o': ['stateFilter', TickProcessor.VmStates.OTHER,
779 'Show only ticks from OTHER VM state'],
780 '-e': ['stateFilter', TickProcessor.VmStates.EXTERNAL,
781 'Show only ticks from EXTERNAL VM state'],
782 '--ignore-unknown': ['ignoreUnknown', true,
783 'Exclude ticks of unknown code entries from processing'],
784 '--separate-ic': ['separateIc', true,
785 'Separate IC entries'],
786 '--unix': ['platform', 'unix',
787 'Specify that we are running on *nix platform'],
788 '--windows': ['platform', 'windows',
789 'Specify that we are running on Windows platform'],
790 '--mac': ['platform', 'mac',
791 'Specify that we are running on Mac OS X platform'],
792 '--nm': ['nm', 'nm',
Leon Clarkee46be812010-01-19 14:06:41 +0000793 'Specify the \'nm\' executable to use (e.g. --nm=/my_dir/nm)'],
794 '--snapshot-log': ['snapshotLogFileName', 'snapshot.log',
795 'Specify snapshot log file to use (e.g. --snapshot-log=snapshot.log)']
Steve Blocka7e24c12009-10-30 11:49:00 +0000796 };
797 this.argsDispatch_['--js'] = this.argsDispatch_['-j'];
798 this.argsDispatch_['--gc'] = this.argsDispatch_['-g'];
799 this.argsDispatch_['--compiler'] = this.argsDispatch_['-c'];
800 this.argsDispatch_['--other'] = this.argsDispatch_['-o'];
801 this.argsDispatch_['--external'] = this.argsDispatch_['-e'];
802};
803
804
805ArgumentsProcessor.DEFAULTS = {
806 logFileName: 'v8.log',
Leon Clarkee46be812010-01-19 14:06:41 +0000807 snapshotLogFileName: null,
Steve Blocka7e24c12009-10-30 11:49:00 +0000808 platform: 'unix',
809 stateFilter: null,
810 ignoreUnknown: false,
811 separateIc: false,
812 nm: 'nm'
813};
814
815
816ArgumentsProcessor.prototype.parse = function() {
817 while (this.args_.length) {
818 var arg = this.args_[0];
819 if (arg.charAt(0) != '-') {
820 break;
821 }
822 this.args_.shift();
823 var userValue = null;
824 var eqPos = arg.indexOf('=');
825 if (eqPos != -1) {
826 userValue = arg.substr(eqPos + 1);
827 arg = arg.substr(0, eqPos);
828 }
829 if (arg in this.argsDispatch_) {
830 var dispatch = this.argsDispatch_[arg];
831 this.result_[dispatch[0]] = userValue == null ? dispatch[1] : userValue;
832 } else {
833 return false;
834 }
835 }
836
837 if (this.args_.length >= 1) {
838 this.result_.logFileName = this.args_.shift();
839 }
840 return true;
841};
842
843
844ArgumentsProcessor.prototype.result = function() {
845 return this.result_;
846};
847
848
849ArgumentsProcessor.prototype.printUsageAndExit = function() {
850
851 function padRight(s, len) {
852 s = s.toString();
853 if (s.length < len) {
854 s = s + (new Array(len - s.length + 1).join(' '));
855 }
856 return s;
857 }
858
859 print('Cmdline args: [options] [log-file-name]\n' +
860 'Default log file name is "' +
861 ArgumentsProcessor.DEFAULTS.logFileName + '".\n');
862 print('Options:');
863 for (var arg in this.argsDispatch_) {
864 var synonims = [arg];
865 var dispatch = this.argsDispatch_[arg];
866 for (var synArg in this.argsDispatch_) {
867 if (arg !== synArg && dispatch === this.argsDispatch_[synArg]) {
868 synonims.push(synArg);
869 delete this.argsDispatch_[synArg];
870 }
871 }
872 print(' ' + padRight(synonims.join(', '), 20) + dispatch[2]);
873 }
874 quit(2);
875};
876