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