blob: 600d2eeb7b79dc3bcee56f183e0ec3c2347e83f6 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001// Copyright 2012 the V8 project authors. All rights reserved.
Steve Blocka7e24c12009-10-30 11:49:00 +00002// 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 Murdochb8a8cc12014-11-26 15:28:44 +000076 parsers: [null, parseInt, 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 Murdochb8a8cc12014-11-26 15:28:44 +0000110 type, kind, start, size, name, maybe_func) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100111 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 Murdoch3ef787d2012-04-12 10:51:47 +0100149 cppEntriesProvider,
150 separateIc,
151 callGraphSize,
152 ignoreUnknown,
153 stateFilter,
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000154 snapshotLogProcessor,
155 distortion,
156 range,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000157 sourceMap,
158 timedRange,
159 pairwiseTimedRange,
160 onlySummary) {
Steve Block1e0659c2011-05-24 12:43:12 +0100161 LogReader.call(this, {
Steve Blocka7e24c12009-10-30 11:49:00 +0000162 'shared-library': { parsers: [null, parseInt, parseInt],
163 processor: this.processSharedLibrary },
164 'code-creation': {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000165 parsers: [null, parseInt, parseInt, parseInt, null, 'var-args'],
Ben Murdochb0fe1622011-05-05 13:52:32 +0100166 processor: this.processCodeCreation },
167 'code-move': { parsers: [parseInt, parseInt],
168 processor: this.processCodeMove },
169 'code-delete': { parsers: [parseInt],
170 processor: this.processCodeDelete },
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100171 'sfi-move': { parsers: [parseInt, parseInt],
Ben Murdochb0fe1622011-05-05 13:52:32 +0100172 processor: this.processFunctionMove },
Ben Murdochb0fe1622011-05-05 13:52:32 +0100173 'snapshot-pos': { parsers: [parseInt, parseInt],
174 processor: this.processSnapshotPosition },
Steve Block44f0eee2011-05-26 01:26:41 +0100175 'tick': {
176 parsers: [parseInt, parseInt, parseInt,
177 parseInt, parseInt, 'var-args'],
Ben Murdochb0fe1622011-05-05 13:52:32 +0100178 processor: this.processTick },
Steve Block3ce2e202009-11-05 08:53:23 +0000179 'heap-sample-begin': { parsers: [null, null, parseInt],
180 processor: this.processHeapSampleBegin },
181 'heap-sample-end': { parsers: [null, null],
182 processor: this.processHeapSampleEnd },
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000183 'timer-event-start' : { parsers: [null, null, null],
184 processor: this.advanceDistortion },
185 'timer-event-end' : { parsers: [null, null, null],
186 processor: this.advanceDistortion },
Steve Block3ce2e202009-11-05 08:53:23 +0000187 // Ignored events.
Steve Blocka7e24c12009-10-30 11:49:00 +0000188 'profiler': null,
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100189 'function-creation': null,
190 'function-move': null,
191 'function-delete': null,
Steve Block3ce2e202009-11-05 08:53:23 +0000192 'heap-sample-item': null,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000193 'current-time': null, // Handled specially, not parsed.
Steve Blocka7e24c12009-10-30 11:49:00 +0000194 // Obsolete row types.
195 'code-allocate': null,
196 'begin-code-region': null,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000197 'end-code-region': null },
198 timedRange,
199 pairwiseTimedRange);
Steve Blocka7e24c12009-10-30 11:49:00 +0000200
201 this.cppEntriesProvider_ = cppEntriesProvider;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100202 this.callGraphSize_ = callGraphSize;
Steve Blocka7e24c12009-10-30 11:49:00 +0000203 this.ignoreUnknown_ = ignoreUnknown;
204 this.stateFilter_ = stateFilter;
Leon Clarkee46be812010-01-19 14:06:41 +0000205 this.snapshotLogProcessor_ = snapshotLogProcessor;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000206 this.sourceMap = sourceMap;
Leon Clarkee46be812010-01-19 14:06:41 +0000207 this.deserializedEntriesNames_ = [];
Steve Blocka7e24c12009-10-30 11:49:00 +0000208 var ticks = this.ticks_ =
209 { total: 0, unaccounted: 0, excluded: 0, gc: 0 };
210
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000211 distortion = parseInt(distortion);
212 // Convert picoseconds to nanoseconds.
213 this.distortion_per_entry = isNaN(distortion) ? 0 : (distortion / 1000);
214 this.distortion = 0;
215 var rangelimits = range ? range.split(",") : [];
216 var range_start = parseInt(rangelimits[0]);
217 var range_end = parseInt(rangelimits[1]);
218 // Convert milliseconds to nanoseconds.
219 this.range_start = isNaN(range_start) ? -Infinity : (range_start * 1000);
220 this.range_end = isNaN(range_end) ? Infinity : (range_end * 1000)
221
Steve Block1e0659c2011-05-24 12:43:12 +0100222 V8Profile.prototype.handleUnknownCode = function(
Steve Blocka7e24c12009-10-30 11:49:00 +0000223 operation, addr, opt_stackPos) {
Steve Block1e0659c2011-05-24 12:43:12 +0100224 var op = Profile.Operation;
Steve Blocka7e24c12009-10-30 11:49:00 +0000225 switch (operation) {
226 case op.MOVE:
227 print('Code move event for unknown code: 0x' + addr.toString(16));
228 break;
229 case op.DELETE:
230 print('Code delete event for unknown code: 0x' + addr.toString(16));
231 break;
232 case op.TICK:
233 // Only unknown PCs (the first frame) are reported as unaccounted,
234 // otherwise tick balance will be corrupted (this behavior is compatible
235 // with the original tickprocessor.py script.)
236 if (opt_stackPos == 0) {
237 ticks.unaccounted++;
238 }
239 break;
240 }
241 };
242
Steve Block1e0659c2011-05-24 12:43:12 +0100243 this.profile_ = new V8Profile(separateIc);
Steve Blocka7e24c12009-10-30 11:49:00 +0000244 this.codeTypes_ = {};
245 // Count each tick as a time unit.
Steve Block1e0659c2011-05-24 12:43:12 +0100246 this.viewBuilder_ = new ViewBuilder(1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000247 this.lastLogFileName_ = null;
Steve Block3ce2e202009-11-05 08:53:23 +0000248
249 this.generation_ = 1;
250 this.currentProducerProfile_ = null;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000251 this.onlySummary_ = onlySummary;
Steve Blocka7e24c12009-10-30 11:49:00 +0000252};
Steve Block1e0659c2011-05-24 12:43:12 +0100253inherits(TickProcessor, LogReader);
Steve Blocka7e24c12009-10-30 11:49:00 +0000254
255
256TickProcessor.VmStates = {
257 JS: 0,
258 GC: 1,
259 COMPILER: 2,
260 OTHER: 3,
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000261 EXTERNAL: 4,
262 IDLE: 5
Steve Blocka7e24c12009-10-30 11:49:00 +0000263};
264
265
266TickProcessor.CodeTypes = {
267 CPP: 0,
268 SHARED_LIB: 1
269};
270// Otherwise, this is JS-related code. We are not adding it to
271// codeTypes_ map because there can be zillions of them.
272
273
274TickProcessor.CALL_PROFILE_CUTOFF_PCT = 2.0;
275
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100276TickProcessor.CALL_GRAPH_SIZE = 5;
Steve Blocka7e24c12009-10-30 11:49:00 +0000277
278/**
279 * @override
280 */
281TickProcessor.prototype.printError = function(str) {
282 print(str);
283};
284
285
286TickProcessor.prototype.setCodeType = function(name, type) {
287 this.codeTypes_[name] = TickProcessor.CodeTypes[type];
288};
289
290
291TickProcessor.prototype.isSharedLibrary = function(name) {
292 return this.codeTypes_[name] == TickProcessor.CodeTypes.SHARED_LIB;
293};
294
295
296TickProcessor.prototype.isCppCode = function(name) {
297 return this.codeTypes_[name] == TickProcessor.CodeTypes.CPP;
298};
299
300
301TickProcessor.prototype.isJsCode = function(name) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000302 return name !== "UNKNOWN" && !(name in this.codeTypes_);
Steve Blocka7e24c12009-10-30 11:49:00 +0000303};
304
305
306TickProcessor.prototype.processLogFile = function(fileName) {
307 this.lastLogFileName_ = fileName;
Andrei Popescu31002712010-02-23 13:46:05 +0000308 var line;
309 while (line = readline()) {
310 this.processLogLine(line);
311 }
312};
313
314
315TickProcessor.prototype.processLogFileInTest = function(fileName) {
316 // Hack file name to avoid dealing with platform specifics.
317 this.lastLogFileName_ = 'v8.log';
Steve Blocka7e24c12009-10-30 11:49:00 +0000318 var contents = readFile(fileName);
319 this.processLogChunk(contents);
320};
321
322
323TickProcessor.prototype.processSharedLibrary = function(
324 name, startAddr, endAddr) {
325 var entry = this.profile_.addLibrary(name, startAddr, endAddr);
326 this.setCodeType(entry.getName(), 'SHARED_LIB');
327
328 var self = this;
329 var libFuncs = this.cppEntriesProvider_.parseVmSymbols(
330 name, startAddr, endAddr, function(fName, fStart, fEnd) {
331 self.profile_.addStaticCode(fName, fStart, fEnd);
332 self.setCodeType(fName, 'CPP');
333 });
334};
335
336
337TickProcessor.prototype.processCodeCreation = function(
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000338 type, kind, start, size, name, maybe_func) {
Leon Clarkee46be812010-01-19 14:06:41 +0000339 name = this.deserializedEntriesNames_[start] || name;
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100340 if (maybe_func.length) {
341 var funcAddr = parseInt(maybe_func[0]);
342 var state = parseState(maybe_func[1]);
343 this.profile_.addFuncCode(type, name, start, size, funcAddr, state);
344 } else {
345 this.profile_.addCode(type, name, start, size);
346 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000347};
348
349
350TickProcessor.prototype.processCodeMove = function(from, to) {
351 this.profile_.moveCode(from, to);
352};
353
354
355TickProcessor.prototype.processCodeDelete = function(start) {
356 this.profile_.deleteCode(start);
357};
358
359
Leon Clarked91b9f72010-01-27 17:25:45 +0000360TickProcessor.prototype.processFunctionMove = function(from, to) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100361 this.profile_.moveFunc(from, to);
Leon Clarked91b9f72010-01-27 17:25:45 +0000362};
363
364
Leon Clarkee46be812010-01-19 14:06:41 +0000365TickProcessor.prototype.processSnapshotPosition = function(addr, pos) {
366 if (this.snapshotLogProcessor_) {
367 this.deserializedEntriesNames_[addr] =
368 this.snapshotLogProcessor_.getSerializedEntryName(pos);
369 }
370};
371
372
Steve Blocka7e24c12009-10-30 11:49:00 +0000373TickProcessor.prototype.includeTick = function(vmState) {
374 return this.stateFilter_ == null || this.stateFilter_ == vmState;
375};
376
Steve Block44f0eee2011-05-26 01:26:41 +0100377TickProcessor.prototype.processTick = function(pc,
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000378 ns_since_start,
Steve Block44f0eee2011-05-26 01:26:41 +0100379 is_external_callback,
380 tos_or_external_callback,
381 vmState,
382 stack) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000383 this.distortion += this.distortion_per_entry;
384 ns_since_start -= this.distortion;
385 if (ns_since_start < this.range_start || ns_since_start > this.range_end) {
386 return;
387 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000388 this.ticks_.total++;
389 if (vmState == TickProcessor.VmStates.GC) this.ticks_.gc++;
390 if (!this.includeTick(vmState)) {
391 this.ticks_.excluded++;
392 return;
393 }
Steve Block44f0eee2011-05-26 01:26:41 +0100394 if (is_external_callback) {
395 // Don't use PC when in external callback code, as it can point
396 // inside callback's code, and we will erroneously report
Ben Murdoch8b112d22011-06-08 16:22:53 +0100397 // that a callback calls itself. Instead we use tos_or_external_callback,
398 // as simply resetting PC will produce unaccounted ticks.
399 pc = tos_or_external_callback;
400 tos_or_external_callback = 0;
Steve Block44f0eee2011-05-26 01:26:41 +0100401 } else if (tos_or_external_callback) {
402 // Find out, if top of stack was pointing inside a JS function
403 // meaning that we have encountered a frameless invocation.
404 var funcEntry = this.profile_.findEntry(tos_or_external_callback);
Leon Clarked91b9f72010-01-27 17:25:45 +0000405 if (!funcEntry || !funcEntry.isJSFunction || !funcEntry.isJSFunction()) {
Steve Block44f0eee2011-05-26 01:26:41 +0100406 tos_or_external_callback = 0;
Leon Clarked91b9f72010-01-27 17:25:45 +0000407 }
408 }
409
Steve Block44f0eee2011-05-26 01:26:41 +0100410 this.profile_.recordTick(this.processStack(pc, tos_or_external_callback, stack));
Steve Blocka7e24c12009-10-30 11:49:00 +0000411};
412
413
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000414TickProcessor.prototype.advanceDistortion = function() {
415 this.distortion += this.distortion_per_entry;
416}
417
418
Steve Block3ce2e202009-11-05 08:53:23 +0000419TickProcessor.prototype.processHeapSampleBegin = function(space, state, ticks) {
420 if (space != 'Heap') return;
Steve Block1e0659c2011-05-24 12:43:12 +0100421 this.currentProducerProfile_ = new CallTree();
Steve Block3ce2e202009-11-05 08:53:23 +0000422};
423
424
425TickProcessor.prototype.processHeapSampleEnd = function(space, state) {
426 if (space != 'Heap' || !this.currentProducerProfile_) return;
427
428 print('Generation ' + this.generation_ + ':');
429 var tree = this.currentProducerProfile_;
430 tree.computeTotalWeights();
431 var producersView = this.viewBuilder_.buildView(tree);
432 // Sort by total time, desc, then by name, desc.
433 producersView.sort(function(rec1, rec2) {
434 return rec2.totalTime - rec1.totalTime ||
435 (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
436 this.printHeavyProfile(producersView.head.children);
437
438 this.currentProducerProfile_ = null;
439 this.generation_++;
440};
441
442
Steve Blocka7e24c12009-10-30 11:49:00 +0000443TickProcessor.prototype.printStatistics = function() {
444 print('Statistical profiling result from ' + this.lastLogFileName_ +
445 ', (' + this.ticks_.total +
446 ' ticks, ' + this.ticks_.unaccounted + ' unaccounted, ' +
447 this.ticks_.excluded + ' excluded).');
448
449 if (this.ticks_.total == 0) return;
450
Steve Blocka7e24c12009-10-30 11:49:00 +0000451 var flatProfile = this.profile_.getFlatProfile();
452 var flatView = this.viewBuilder_.buildView(flatProfile);
453 // Sort by self time, desc, then by name, desc.
454 flatView.sort(function(rec1, rec2) {
455 return rec2.selfTime - rec1.selfTime ||
456 (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
457 var totalTicks = this.ticks_.total;
458 if (this.ignoreUnknown_) {
459 totalTicks -= this.ticks_.unaccounted;
460 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000461 var printAllTicks = !this.onlySummary_;
Steve Blocka7e24c12009-10-30 11:49:00 +0000462
463 // Count library ticks
464 var flatViewNodes = flatView.head.children;
465 var self = this;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000466
Steve Blocka7e24c12009-10-30 11:49:00 +0000467 var libraryTicks = 0;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000468 if(printAllTicks) this.printHeader('Shared libraries');
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000469 this.printEntries(flatViewNodes, totalTicks, null,
Steve Blocka7e24c12009-10-30 11:49:00 +0000470 function(name) { return self.isSharedLibrary(name); },
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000471 function(rec) { libraryTicks += rec.selfTime; }, printAllTicks);
Steve Blocka7e24c12009-10-30 11:49:00 +0000472 var nonLibraryTicks = totalTicks - libraryTicks;
473
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000474 var jsTicks = 0;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000475 if(printAllTicks) this.printHeader('JavaScript');
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000476 this.printEntries(flatViewNodes, totalTicks, nonLibraryTicks,
477 function(name) { return self.isJsCode(name); },
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000478 function(rec) { jsTicks += rec.selfTime; }, printAllTicks);
Steve Blocka7e24c12009-10-30 11:49:00 +0000479
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000480 var cppTicks = 0;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000481 if(printAllTicks) this.printHeader('C++');
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000482 this.printEntries(flatViewNodes, totalTicks, nonLibraryTicks,
483 function(name) { return self.isCppCode(name); },
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000484 function(rec) { cppTicks += rec.selfTime; }, printAllTicks);
Steve Blocka7e24c12009-10-30 11:49:00 +0000485
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000486 this.printHeader('Summary');
487 this.printLine('JavaScript', jsTicks, totalTicks, nonLibraryTicks);
488 this.printLine('C++', cppTicks, totalTicks, nonLibraryTicks);
489 this.printLine('GC', this.ticks_.gc, totalTicks, nonLibraryTicks);
490 this.printLine('Shared libraries', libraryTicks, totalTicks, null);
491 if (!this.ignoreUnknown_ && this.ticks_.unaccounted > 0) {
492 this.printLine('Unaccounted', this.ticks_.unaccounted,
493 this.ticks_.total, null);
494 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000495
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000496 if(printAllTicks) {
497 print('\n [C++ entry points]:');
498 print(' ticks cpp total name');
499 var c_entry_functions = this.profile_.getCEntryProfile();
500 var total_c_entry = c_entry_functions[0].ticks;
501 for (var i = 1; i < c_entry_functions.length; i++) {
502 c = c_entry_functions[i];
503 this.printLine(c.name, c.ticks, total_c_entry, totalTicks);
504 }
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400505
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000506 this.printHeavyProfHeader();
507 var heavyProfile = this.profile_.getBottomUpProfile();
508 var heavyView = this.viewBuilder_.buildView(heavyProfile);
509 // To show the same percentages as in the flat profile.
510 heavyView.head.totalTime = totalTicks;
511 // Sort by total time, desc, then by name, desc.
512 heavyView.sort(function(rec1, rec2) {
513 return rec2.totalTime - rec1.totalTime ||
514 (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
515 this.printHeavyProfile(heavyView.head.children);
516 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000517};
518
519
520function padLeft(s, len) {
521 s = s.toString();
522 if (s.length < len) {
523 var padLength = len - s.length;
524 if (!(padLength in padLeft)) {
525 padLeft[padLength] = new Array(padLength + 1).join(' ');
526 }
527 s = padLeft[padLength] + s;
528 }
529 return s;
530};
531
532
533TickProcessor.prototype.printHeader = function(headerTitle) {
534 print('\n [' + headerTitle + ']:');
535 print(' ticks total nonlib name');
536};
537
538
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000539TickProcessor.prototype.printLine = function(
540 entry, ticks, totalTicks, nonLibTicks) {
541 var pct = ticks * 100 / totalTicks;
542 var nonLibPct = nonLibTicks != null
543 ? padLeft((ticks * 100 / nonLibTicks).toFixed(1), 5) + '% '
544 : ' ';
545 print(' ' + padLeft(ticks, 5) + ' ' +
546 padLeft(pct.toFixed(1), 5) + '% ' +
547 nonLibPct +
548 entry);
549}
550
Steve Blocka7e24c12009-10-30 11:49:00 +0000551TickProcessor.prototype.printHeavyProfHeader = function() {
552 print('\n [Bottom up (heavy) profile]:');
553 print(' Note: percentage shows a share of a particular caller in the ' +
554 'total\n' +
555 ' amount of its parent calls.');
556 print(' Callers occupying less than ' +
557 TickProcessor.CALL_PROFILE_CUTOFF_PCT.toFixed(1) +
558 '% are not shown.\n');
559 print(' ticks parent name');
560};
561
562
Steve Blocka7e24c12009-10-30 11:49:00 +0000563TickProcessor.prototype.processProfile = function(
564 profile, filterP, func) {
565 for (var i = 0, n = profile.length; i < n; ++i) {
566 var rec = profile[i];
567 if (!filterP(rec.internalFuncName)) {
568 continue;
569 }
570 func(rec);
571 }
572};
573
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000574TickProcessor.prototype.getLineAndColumn = function(name) {
575 var re = /:([0-9]+):([0-9]+)$/;
576 var array = re.exec(name);
577 if (!array) {
578 return null;
579 }
580 return {line: array[1], column: array[2]};
581}
582
583TickProcessor.prototype.hasSourceMap = function() {
584 return this.sourceMap != null;
585};
586
587
588TickProcessor.prototype.formatFunctionName = function(funcName) {
589 if (!this.hasSourceMap()) {
590 return funcName;
591 }
592 var lc = this.getLineAndColumn(funcName);
593 if (lc == null) {
594 return funcName;
595 }
596 // in source maps lines and columns are zero based
597 var lineNumber = lc.line - 1;
598 var column = lc.column - 1;
599 var entry = this.sourceMap.findEntry(lineNumber, column);
600 var sourceFile = entry[2];
601 var sourceLine = entry[3] + 1;
602 var sourceColumn = entry[4] + 1;
603
604 return sourceFile + ':' + sourceLine + ':' + sourceColumn + ' -> ' + funcName;
605};
Steve Blocka7e24c12009-10-30 11:49:00 +0000606
607TickProcessor.prototype.printEntries = function(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000608 profile, totalTicks, nonLibTicks, filterP, callback, printAllTicks) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000609 var that = this;
Steve Blocka7e24c12009-10-30 11:49:00 +0000610 this.processProfile(profile, filterP, function (rec) {
611 if (rec.selfTime == 0) return;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000612 callback(rec);
613 var funcName = that.formatFunctionName(rec.internalFuncName);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000614 if(printAllTicks) {
615 that.printLine(funcName, rec.selfTime, totalTicks, nonLibTicks);
616 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000617 });
618};
619
620
621TickProcessor.prototype.printHeavyProfile = function(profile, opt_indent) {
622 var self = this;
623 var indent = opt_indent || 0;
624 var indentStr = padLeft('', indent);
625 this.processProfile(profile, function() { return true; }, function (rec) {
626 // Cut off too infrequent callers.
627 if (rec.parentTotalPercent < TickProcessor.CALL_PROFILE_CUTOFF_PCT) return;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000628 var funcName = self.formatFunctionName(rec.internalFuncName);
Steve Blocka7e24c12009-10-30 11:49:00 +0000629 print(' ' + padLeft(rec.totalTime, 5) + ' ' +
630 padLeft(rec.parentTotalPercent.toFixed(1), 5) + '% ' +
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000631 indentStr + funcName);
Steve Blocka7e24c12009-10-30 11:49:00 +0000632 // Limit backtrace depth.
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100633 if (indent < 2 * self.callGraphSize_) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000634 self.printHeavyProfile(rec.children, indent + 2);
635 }
636 // Delimit top-level functions.
637 if (indent == 0) {
638 print('');
639 }
640 });
641};
642
643
644function CppEntriesProvider() {
645};
646
647
648CppEntriesProvider.prototype.parseVmSymbols = function(
649 libName, libStart, libEnd, processorFunc) {
650 this.loadSymbols(libName);
651
652 var prevEntry;
653
654 function addEntry(funcInfo) {
655 // Several functions can be mapped onto the same address. To avoid
656 // creating zero-sized entries, skip such duplicates.
657 // Also double-check that function belongs to the library address space.
658 if (prevEntry && !prevEntry.end &&
659 prevEntry.start < funcInfo.start &&
660 prevEntry.start >= libStart && funcInfo.start <= libEnd) {
661 processorFunc(prevEntry.name, prevEntry.start, funcInfo.start);
662 }
663 if (funcInfo.end &&
664 (!prevEntry || prevEntry.start != funcInfo.start) &&
665 funcInfo.start >= libStart && funcInfo.end <= libEnd) {
666 processorFunc(funcInfo.name, funcInfo.start, funcInfo.end);
667 }
668 prevEntry = funcInfo;
669 }
670
671 while (true) {
672 var funcInfo = this.parseNextLine();
673 if (funcInfo === null) {
674 continue;
675 } else if (funcInfo === false) {
676 break;
677 }
678 if (funcInfo.start < libStart && funcInfo.start < libEnd - libStart) {
679 funcInfo.start += libStart;
680 }
681 if (funcInfo.size) {
682 funcInfo.end = funcInfo.start + funcInfo.size;
683 }
684 addEntry(funcInfo);
685 }
686 addEntry({name: '', start: libEnd});
687};
688
689
690CppEntriesProvider.prototype.loadSymbols = function(libName) {
691};
692
693
694CppEntriesProvider.prototype.parseNextLine = function() {
695 return false;
696};
697
698
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000699function UnixCppEntriesProvider(nmExec, targetRootFS) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000700 this.symbols = [];
701 this.parsePos = 0;
702 this.nmExec = nmExec;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000703 this.targetRootFS = targetRootFS;
Steve Blocka7e24c12009-10-30 11:49:00 +0000704 this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ([0-9a-fA-F]{8,16} )?[tTwW] (.*)$/;
705};
706inherits(UnixCppEntriesProvider, CppEntriesProvider);
707
708
709UnixCppEntriesProvider.prototype.loadSymbols = function(libName) {
710 this.parsePos = 0;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000711 libName = this.targetRootFS + libName;
Steve Blocka7e24c12009-10-30 11:49:00 +0000712 try {
713 this.symbols = [
714 os.system(this.nmExec, ['-C', '-n', '-S', libName], -1, -1),
715 os.system(this.nmExec, ['-C', '-n', '-S', '-D', libName], -1, -1)
716 ];
717 } catch (e) {
718 // If the library cannot be found on this system let's not panic.
719 this.symbols = ['', ''];
720 }
721};
722
723
724UnixCppEntriesProvider.prototype.parseNextLine = function() {
725 if (this.symbols.length == 0) {
726 return false;
727 }
728 var lineEndPos = this.symbols[0].indexOf('\n', this.parsePos);
729 if (lineEndPos == -1) {
730 this.symbols.shift();
731 this.parsePos = 0;
732 return this.parseNextLine();
733 }
734
735 var line = this.symbols[0].substring(this.parsePos, lineEndPos);
736 this.parsePos = lineEndPos + 1;
737 var fields = line.match(this.FUNC_RE);
738 var funcInfo = null;
739 if (fields) {
740 funcInfo = { name: fields[3], start: parseInt(fields[1], 16) };
741 if (fields[2]) {
742 funcInfo.size = parseInt(fields[2], 16);
743 }
744 }
745 return funcInfo;
746};
747
748
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000749function MacCppEntriesProvider(nmExec, targetRootFS) {
750 UnixCppEntriesProvider.call(this, nmExec, targetRootFS);
Steve Blocka7e24c12009-10-30 11:49:00 +0000751 // Note an empty group. It is required, as UnixCppEntriesProvider expects 3 groups.
752 this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ()[iItT] (.*)$/;
753};
754inherits(MacCppEntriesProvider, UnixCppEntriesProvider);
755
756
757MacCppEntriesProvider.prototype.loadSymbols = function(libName) {
758 this.parsePos = 0;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000759 libName = this.targetRootFS + libName;
Steve Blocka7e24c12009-10-30 11:49:00 +0000760 try {
761 this.symbols = [os.system(this.nmExec, ['-n', '-f', libName], -1, -1), ''];
762 } catch (e) {
763 // If the library cannot be found on this system let's not panic.
764 this.symbols = '';
765 }
766};
767
768
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000769function WindowsCppEntriesProvider(_ignored_nmExec, targetRootFS) {
770 this.targetRootFS = targetRootFS;
Steve Blocka7e24c12009-10-30 11:49:00 +0000771 this.symbols = '';
772 this.parsePos = 0;
773};
774inherits(WindowsCppEntriesProvider, CppEntriesProvider);
775
776
777WindowsCppEntriesProvider.FILENAME_RE = /^(.*)\.([^.]+)$/;
778
779
780WindowsCppEntriesProvider.FUNC_RE =
781 /^\s+0001:[0-9a-fA-F]{8}\s+([_\?@$0-9a-zA-Z]+)\s+([0-9a-fA-F]{8}).*$/;
782
783
784WindowsCppEntriesProvider.IMAGE_BASE_RE =
785 /^\s+0000:00000000\s+___ImageBase\s+([0-9a-fA-F]{8}).*$/;
786
787
788// This is almost a constant on Windows.
789WindowsCppEntriesProvider.EXE_IMAGE_BASE = 0x00400000;
790
791
792WindowsCppEntriesProvider.prototype.loadSymbols = function(libName) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000793 libName = this.targetRootFS + libName;
Steve Blocka7e24c12009-10-30 11:49:00 +0000794 var fileNameFields = libName.match(WindowsCppEntriesProvider.FILENAME_RE);
795 if (!fileNameFields) return;
796 var mapFileName = fileNameFields[1] + '.map';
797 this.moduleType_ = fileNameFields[2].toLowerCase();
798 try {
799 this.symbols = read(mapFileName);
800 } catch (e) {
801 // If .map file cannot be found let's not panic.
802 this.symbols = '';
803 }
804};
805
806
807WindowsCppEntriesProvider.prototype.parseNextLine = function() {
808 var lineEndPos = this.symbols.indexOf('\r\n', this.parsePos);
809 if (lineEndPos == -1) {
810 return false;
811 }
812
813 var line = this.symbols.substring(this.parsePos, lineEndPos);
814 this.parsePos = lineEndPos + 2;
815
816 // Image base entry is above all other symbols, so we can just
817 // terminate parsing.
818 var imageBaseFields = line.match(WindowsCppEntriesProvider.IMAGE_BASE_RE);
819 if (imageBaseFields) {
820 var imageBase = parseInt(imageBaseFields[1], 16);
821 if ((this.moduleType_ == 'exe') !=
822 (imageBase == WindowsCppEntriesProvider.EXE_IMAGE_BASE)) {
823 return false;
824 }
825 }
826
827 var fields = line.match(WindowsCppEntriesProvider.FUNC_RE);
828 return fields ?
829 { name: this.unmangleName(fields[1]), start: parseInt(fields[2], 16) } :
830 null;
831};
832
833
834/**
835 * Performs very simple unmangling of C++ names.
836 *
837 * Does not handle arguments and template arguments. The mangled names have
838 * the form:
839 *
840 * ?LookupInDescriptor@JSObject@internal@v8@@...arguments info...
841 */
842WindowsCppEntriesProvider.prototype.unmangleName = function(name) {
843 // Empty or non-mangled name.
844 if (name.length < 1 || name.charAt(0) != '?') return name;
845 var nameEndPos = name.indexOf('@@');
846 var components = name.substring(1, nameEndPos).split('@');
847 components.reverse();
848 return components.join('::');
849};
850
851
852function ArgumentsProcessor(args) {
853 this.args_ = args;
854 this.result_ = ArgumentsProcessor.DEFAULTS;
855
856 this.argsDispatch_ = {
857 '-j': ['stateFilter', TickProcessor.VmStates.JS,
858 'Show only ticks from JS VM state'],
859 '-g': ['stateFilter', TickProcessor.VmStates.GC,
860 'Show only ticks from GC VM state'],
861 '-c': ['stateFilter', TickProcessor.VmStates.COMPILER,
862 'Show only ticks from COMPILER VM state'],
863 '-o': ['stateFilter', TickProcessor.VmStates.OTHER,
864 'Show only ticks from OTHER VM state'],
865 '-e': ['stateFilter', TickProcessor.VmStates.EXTERNAL,
866 'Show only ticks from EXTERNAL VM state'],
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100867 '--call-graph-size': ['callGraphSize', TickProcessor.CALL_GRAPH_SIZE,
868 'Set the call graph size'],
Steve Blocka7e24c12009-10-30 11:49:00 +0000869 '--ignore-unknown': ['ignoreUnknown', true,
870 'Exclude ticks of unknown code entries from processing'],
871 '--separate-ic': ['separateIc', true,
872 'Separate IC entries'],
873 '--unix': ['platform', 'unix',
874 'Specify that we are running on *nix platform'],
875 '--windows': ['platform', 'windows',
876 'Specify that we are running on Windows platform'],
877 '--mac': ['platform', 'mac',
878 'Specify that we are running on Mac OS X platform'],
879 '--nm': ['nm', 'nm',
Leon Clarkee46be812010-01-19 14:06:41 +0000880 'Specify the \'nm\' executable to use (e.g. --nm=/my_dir/nm)'],
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000881 '--target': ['targetRootFS', '',
882 'Specify the target root directory for cross environment'],
Leon Clarkee46be812010-01-19 14:06:41 +0000883 '--snapshot-log': ['snapshotLogFileName', 'snapshot.log',
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000884 'Specify snapshot log file to use (e.g. --snapshot-log=snapshot.log)'],
885 '--range': ['range', 'auto,auto',
886 'Specify the range limit as [start],[end]'],
887 '--distortion': ['distortion', 0,
888 'Specify the logging overhead in picoseconds'],
889 '--source-map': ['sourceMap', null,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000890 'Specify the source map that should be used for output'],
891 '--timed-range': ['timedRange', true,
892 'Ignore ticks before first and after last Date.now() call'],
893 '--pairwise-timed-range': ['pairwiseTimedRange', true,
894 'Ignore ticks outside pairs of Date.now() calls'],
895 '--only-summary': ['onlySummary', true,
896 'Print only tick summary, exclude other information']
Steve Blocka7e24c12009-10-30 11:49:00 +0000897 };
898 this.argsDispatch_['--js'] = this.argsDispatch_['-j'];
899 this.argsDispatch_['--gc'] = this.argsDispatch_['-g'];
900 this.argsDispatch_['--compiler'] = this.argsDispatch_['-c'];
901 this.argsDispatch_['--other'] = this.argsDispatch_['-o'];
902 this.argsDispatch_['--external'] = this.argsDispatch_['-e'];
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000903 this.argsDispatch_['--ptr'] = this.argsDispatch_['--pairwise-timed-range'];
Steve Blocka7e24c12009-10-30 11:49:00 +0000904};
905
906
907ArgumentsProcessor.DEFAULTS = {
908 logFileName: 'v8.log',
Leon Clarkee46be812010-01-19 14:06:41 +0000909 snapshotLogFileName: null,
Steve Blocka7e24c12009-10-30 11:49:00 +0000910 platform: 'unix',
911 stateFilter: null,
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100912 callGraphSize: 5,
Steve Blocka7e24c12009-10-30 11:49:00 +0000913 ignoreUnknown: false,
914 separateIc: false,
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000915 targetRootFS: '',
916 nm: 'nm',
917 range: 'auto,auto',
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000918 distortion: 0,
919 timedRange: false,
920 pairwiseTimedRange: false,
921 onlySummary: false
Steve Blocka7e24c12009-10-30 11:49:00 +0000922};
923
924
925ArgumentsProcessor.prototype.parse = function() {
926 while (this.args_.length) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000927 var arg = this.args_.shift();
Steve Blocka7e24c12009-10-30 11:49:00 +0000928 if (arg.charAt(0) != '-') {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000929 this.result_.logFileName = arg;
930 continue;
Steve Blocka7e24c12009-10-30 11:49:00 +0000931 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000932 var userValue = null;
933 var eqPos = arg.indexOf('=');
934 if (eqPos != -1) {
935 userValue = arg.substr(eqPos + 1);
936 arg = arg.substr(0, eqPos);
937 }
938 if (arg in this.argsDispatch_) {
939 var dispatch = this.argsDispatch_[arg];
940 this.result_[dispatch[0]] = userValue == null ? dispatch[1] : userValue;
941 } else {
942 return false;
943 }
944 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000945 return true;
946};
947
948
949ArgumentsProcessor.prototype.result = function() {
950 return this.result_;
951};
952
953
954ArgumentsProcessor.prototype.printUsageAndExit = function() {
955
956 function padRight(s, len) {
957 s = s.toString();
958 if (s.length < len) {
959 s = s + (new Array(len - s.length + 1).join(' '));
960 }
961 return s;
962 }
963
964 print('Cmdline args: [options] [log-file-name]\n' +
965 'Default log file name is "' +
966 ArgumentsProcessor.DEFAULTS.logFileName + '".\n');
967 print('Options:');
968 for (var arg in this.argsDispatch_) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000969 var synonyms = [arg];
Steve Blocka7e24c12009-10-30 11:49:00 +0000970 var dispatch = this.argsDispatch_[arg];
971 for (var synArg in this.argsDispatch_) {
972 if (arg !== synArg && dispatch === this.argsDispatch_[synArg]) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000973 synonyms.push(synArg);
Steve Blocka7e24c12009-10-30 11:49:00 +0000974 delete this.argsDispatch_[synArg];
975 }
976 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000977 print(' ' + padRight(synonyms.join(', '), 20) + " " + dispatch[2]);
Steve Blocka7e24c12009-10-30 11:49:00 +0000978 }
979 quit(2);
980};