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