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