blob: ec56d49d9046aa65d9332387fa440b1aab43f2f9 [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
Steve Blocka7e24c12009-10-30 11:49:00 +000073function TickProcessor(
Ben Murdoch3ef787d2012-04-12 10:51:47 +010074 cppEntriesProvider,
75 separateIc,
76 callGraphSize,
77 ignoreUnknown,
78 stateFilter,
Ben Murdochb8a8cc12014-11-26 15:28:44 +000079 distortion,
80 range,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000081 sourceMap,
82 timedRange,
83 pairwiseTimedRange,
Ben Murdoch61f157c2016-09-16 13:49:30 +010084 onlySummary,
85 runtimeTimerFilter) {
Steve Block1e0659c2011-05-24 12:43:12 +010086 LogReader.call(this, {
Ben Murdochc5610432016-08-08 18:44:38 +010087 'shared-library': { parsers: [null, parseInt, parseInt, parseInt],
Steve Blocka7e24c12009-10-30 11:49:00 +000088 processor: this.processSharedLibrary },
89 'code-creation': {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000090 parsers: [null, parseInt, parseInt, parseInt, null, 'var-args'],
Ben Murdochb0fe1622011-05-05 13:52:32 +010091 processor: this.processCodeCreation },
92 'code-move': { parsers: [parseInt, parseInt],
93 processor: this.processCodeMove },
94 'code-delete': { parsers: [parseInt],
95 processor: this.processCodeDelete },
Ben Murdoche0cee9b2011-05-25 10:26:03 +010096 'sfi-move': { parsers: [parseInt, parseInt],
Ben Murdochb0fe1622011-05-05 13:52:32 +010097 processor: this.processFunctionMove },
Ben Murdoch61f157c2016-09-16 13:49:30 +010098 'active-runtime-timer': {
99 parsers: [null],
100 processor: this.processRuntimeTimerEvent },
Steve Block44f0eee2011-05-26 01:26:41 +0100101 'tick': {
102 parsers: [parseInt, parseInt, parseInt,
103 parseInt, parseInt, 'var-args'],
Ben Murdochb0fe1622011-05-05 13:52:32 +0100104 processor: this.processTick },
Steve Block3ce2e202009-11-05 08:53:23 +0000105 'heap-sample-begin': { parsers: [null, null, parseInt],
106 processor: this.processHeapSampleBegin },
107 'heap-sample-end': { parsers: [null, null],
108 processor: this.processHeapSampleEnd },
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000109 'timer-event-start' : { parsers: [null, null, null],
110 processor: this.advanceDistortion },
111 'timer-event-end' : { parsers: [null, null, null],
112 processor: this.advanceDistortion },
Steve Block3ce2e202009-11-05 08:53:23 +0000113 // Ignored events.
Steve Blocka7e24c12009-10-30 11:49:00 +0000114 'profiler': null,
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100115 'function-creation': null,
116 'function-move': null,
117 'function-delete': null,
Steve Block3ce2e202009-11-05 08:53:23 +0000118 'heap-sample-item': null,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000119 'current-time': null, // Handled specially, not parsed.
Steve Blocka7e24c12009-10-30 11:49:00 +0000120 // Obsolete row types.
121 'code-allocate': null,
122 'begin-code-region': null,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000123 'end-code-region': null },
124 timedRange,
125 pairwiseTimedRange);
Steve Blocka7e24c12009-10-30 11:49:00 +0000126
127 this.cppEntriesProvider_ = cppEntriesProvider;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100128 this.callGraphSize_ = callGraphSize;
Steve Blocka7e24c12009-10-30 11:49:00 +0000129 this.ignoreUnknown_ = ignoreUnknown;
130 this.stateFilter_ = stateFilter;
Ben Murdoch61f157c2016-09-16 13:49:30 +0100131 this.runtimeTimerFilter_ = runtimeTimerFilter;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000132 this.sourceMap = sourceMap;
Leon Clarkee46be812010-01-19 14:06:41 +0000133 this.deserializedEntriesNames_ = [];
Steve Blocka7e24c12009-10-30 11:49:00 +0000134 var ticks = this.ticks_ =
135 { total: 0, unaccounted: 0, excluded: 0, gc: 0 };
136
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000137 distortion = parseInt(distortion);
138 // Convert picoseconds to nanoseconds.
139 this.distortion_per_entry = isNaN(distortion) ? 0 : (distortion / 1000);
140 this.distortion = 0;
141 var rangelimits = range ? range.split(",") : [];
142 var range_start = parseInt(rangelimits[0]);
143 var range_end = parseInt(rangelimits[1]);
144 // Convert milliseconds to nanoseconds.
145 this.range_start = isNaN(range_start) ? -Infinity : (range_start * 1000);
146 this.range_end = isNaN(range_end) ? Infinity : (range_end * 1000)
147
Steve Block1e0659c2011-05-24 12:43:12 +0100148 V8Profile.prototype.handleUnknownCode = function(
Steve Blocka7e24c12009-10-30 11:49:00 +0000149 operation, addr, opt_stackPos) {
Steve Block1e0659c2011-05-24 12:43:12 +0100150 var op = Profile.Operation;
Steve Blocka7e24c12009-10-30 11:49:00 +0000151 switch (operation) {
152 case op.MOVE:
153 print('Code move event for unknown code: 0x' + addr.toString(16));
154 break;
155 case op.DELETE:
156 print('Code delete event for unknown code: 0x' + addr.toString(16));
157 break;
158 case op.TICK:
159 // Only unknown PCs (the first frame) are reported as unaccounted,
160 // otherwise tick balance will be corrupted (this behavior is compatible
161 // with the original tickprocessor.py script.)
162 if (opt_stackPos == 0) {
163 ticks.unaccounted++;
164 }
165 break;
166 }
167 };
168
Steve Block1e0659c2011-05-24 12:43:12 +0100169 this.profile_ = new V8Profile(separateIc);
Steve Blocka7e24c12009-10-30 11:49:00 +0000170 this.codeTypes_ = {};
171 // Count each tick as a time unit.
Steve Block1e0659c2011-05-24 12:43:12 +0100172 this.viewBuilder_ = new ViewBuilder(1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000173 this.lastLogFileName_ = null;
Steve Block3ce2e202009-11-05 08:53:23 +0000174
175 this.generation_ = 1;
176 this.currentProducerProfile_ = null;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000177 this.onlySummary_ = onlySummary;
Steve Blocka7e24c12009-10-30 11:49:00 +0000178};
Steve Block1e0659c2011-05-24 12:43:12 +0100179inherits(TickProcessor, LogReader);
Steve Blocka7e24c12009-10-30 11:49:00 +0000180
181
182TickProcessor.VmStates = {
183 JS: 0,
184 GC: 1,
185 COMPILER: 2,
186 OTHER: 3,
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000187 EXTERNAL: 4,
188 IDLE: 5
Steve Blocka7e24c12009-10-30 11:49:00 +0000189};
190
191
192TickProcessor.CodeTypes = {
193 CPP: 0,
194 SHARED_LIB: 1
195};
196// Otherwise, this is JS-related code. We are not adding it to
197// codeTypes_ map because there can be zillions of them.
198
199
200TickProcessor.CALL_PROFILE_CUTOFF_PCT = 2.0;
201
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100202TickProcessor.CALL_GRAPH_SIZE = 5;
Steve Blocka7e24c12009-10-30 11:49:00 +0000203
204/**
205 * @override
206 */
207TickProcessor.prototype.printError = function(str) {
208 print(str);
209};
210
211
212TickProcessor.prototype.setCodeType = function(name, type) {
213 this.codeTypes_[name] = TickProcessor.CodeTypes[type];
214};
215
216
217TickProcessor.prototype.isSharedLibrary = function(name) {
218 return this.codeTypes_[name] == TickProcessor.CodeTypes.SHARED_LIB;
219};
220
221
222TickProcessor.prototype.isCppCode = function(name) {
223 return this.codeTypes_[name] == TickProcessor.CodeTypes.CPP;
224};
225
226
227TickProcessor.prototype.isJsCode = function(name) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000228 return name !== "UNKNOWN" && !(name in this.codeTypes_);
Steve Blocka7e24c12009-10-30 11:49:00 +0000229};
230
231
232TickProcessor.prototype.processLogFile = function(fileName) {
233 this.lastLogFileName_ = fileName;
Andrei Popescu31002712010-02-23 13:46:05 +0000234 var line;
235 while (line = readline()) {
236 this.processLogLine(line);
237 }
238};
239
240
241TickProcessor.prototype.processLogFileInTest = function(fileName) {
242 // Hack file name to avoid dealing with platform specifics.
243 this.lastLogFileName_ = 'v8.log';
Steve Blocka7e24c12009-10-30 11:49:00 +0000244 var contents = readFile(fileName);
245 this.processLogChunk(contents);
246};
247
248
249TickProcessor.prototype.processSharedLibrary = function(
Ben Murdochc5610432016-08-08 18:44:38 +0100250 name, startAddr, endAddr, aslrSlide) {
251 var entry = this.profile_.addLibrary(name, startAddr, endAddr, aslrSlide);
Steve Blocka7e24c12009-10-30 11:49:00 +0000252 this.setCodeType(entry.getName(), 'SHARED_LIB');
253
254 var self = this;
255 var libFuncs = this.cppEntriesProvider_.parseVmSymbols(
Ben Murdochc5610432016-08-08 18:44:38 +0100256 name, startAddr, endAddr, aslrSlide, function(fName, fStart, fEnd) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000257 self.profile_.addStaticCode(fName, fStart, fEnd);
258 self.setCodeType(fName, 'CPP');
259 });
260};
261
262
263TickProcessor.prototype.processCodeCreation = function(
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000264 type, kind, start, size, name, maybe_func) {
Leon Clarkee46be812010-01-19 14:06:41 +0000265 name = this.deserializedEntriesNames_[start] || name;
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100266 if (maybe_func.length) {
267 var funcAddr = parseInt(maybe_func[0]);
268 var state = parseState(maybe_func[1]);
269 this.profile_.addFuncCode(type, name, start, size, funcAddr, state);
270 } else {
271 this.profile_.addCode(type, name, start, size);
272 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000273};
274
275
276TickProcessor.prototype.processCodeMove = function(from, to) {
277 this.profile_.moveCode(from, to);
278};
279
280
281TickProcessor.prototype.processCodeDelete = function(start) {
282 this.profile_.deleteCode(start);
283};
284
285
Leon Clarked91b9f72010-01-27 17:25:45 +0000286TickProcessor.prototype.processFunctionMove = function(from, to) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100287 this.profile_.moveFunc(from, to);
Leon Clarked91b9f72010-01-27 17:25:45 +0000288};
289
290
Steve Blocka7e24c12009-10-30 11:49:00 +0000291TickProcessor.prototype.includeTick = function(vmState) {
Ben Murdoch61f157c2016-09-16 13:49:30 +0100292 if (this.stateFilter_ !== null) {
293 return this.stateFilter_ == vmState;
294 } else if (this.runtimeTimerFilter_ !== null) {
295 return this.currentRuntimeTimer == this.runtimeTimerFilter_;
296 }
297 return true;
Steve Blocka7e24c12009-10-30 11:49:00 +0000298};
299
Ben Murdoch61f157c2016-09-16 13:49:30 +0100300TickProcessor.prototype.processRuntimeTimerEvent = function(name) {
301 this.currentRuntimeTimer = name;
302}
303
Steve Block44f0eee2011-05-26 01:26:41 +0100304TickProcessor.prototype.processTick = function(pc,
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000305 ns_since_start,
Steve Block44f0eee2011-05-26 01:26:41 +0100306 is_external_callback,
307 tos_or_external_callback,
308 vmState,
309 stack) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000310 this.distortion += this.distortion_per_entry;
311 ns_since_start -= this.distortion;
312 if (ns_since_start < this.range_start || ns_since_start > this.range_end) {
313 return;
314 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000315 this.ticks_.total++;
316 if (vmState == TickProcessor.VmStates.GC) this.ticks_.gc++;
317 if (!this.includeTick(vmState)) {
318 this.ticks_.excluded++;
319 return;
320 }
Steve Block44f0eee2011-05-26 01:26:41 +0100321 if (is_external_callback) {
322 // Don't use PC when in external callback code, as it can point
323 // inside callback's code, and we will erroneously report
Ben Murdoch8b112d22011-06-08 16:22:53 +0100324 // that a callback calls itself. Instead we use tos_or_external_callback,
325 // as simply resetting PC will produce unaccounted ticks.
326 pc = tos_or_external_callback;
327 tos_or_external_callback = 0;
Steve Block44f0eee2011-05-26 01:26:41 +0100328 } else if (tos_or_external_callback) {
329 // Find out, if top of stack was pointing inside a JS function
330 // meaning that we have encountered a frameless invocation.
331 var funcEntry = this.profile_.findEntry(tos_or_external_callback);
Leon Clarked91b9f72010-01-27 17:25:45 +0000332 if (!funcEntry || !funcEntry.isJSFunction || !funcEntry.isJSFunction()) {
Steve Block44f0eee2011-05-26 01:26:41 +0100333 tos_or_external_callback = 0;
Leon Clarked91b9f72010-01-27 17:25:45 +0000334 }
335 }
336
Steve Block44f0eee2011-05-26 01:26:41 +0100337 this.profile_.recordTick(this.processStack(pc, tos_or_external_callback, stack));
Steve Blocka7e24c12009-10-30 11:49:00 +0000338};
339
340
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000341TickProcessor.prototype.advanceDistortion = function() {
342 this.distortion += this.distortion_per_entry;
343}
344
345
Steve Block3ce2e202009-11-05 08:53:23 +0000346TickProcessor.prototype.processHeapSampleBegin = function(space, state, ticks) {
347 if (space != 'Heap') return;
Steve Block1e0659c2011-05-24 12:43:12 +0100348 this.currentProducerProfile_ = new CallTree();
Steve Block3ce2e202009-11-05 08:53:23 +0000349};
350
351
352TickProcessor.prototype.processHeapSampleEnd = function(space, state) {
353 if (space != 'Heap' || !this.currentProducerProfile_) return;
354
355 print('Generation ' + this.generation_ + ':');
356 var tree = this.currentProducerProfile_;
357 tree.computeTotalWeights();
358 var producersView = this.viewBuilder_.buildView(tree);
359 // Sort by total time, desc, then by name, desc.
360 producersView.sort(function(rec1, rec2) {
361 return rec2.totalTime - rec1.totalTime ||
362 (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
363 this.printHeavyProfile(producersView.head.children);
364
365 this.currentProducerProfile_ = null;
366 this.generation_++;
367};
368
369
Steve Blocka7e24c12009-10-30 11:49:00 +0000370TickProcessor.prototype.printStatistics = function() {
371 print('Statistical profiling result from ' + this.lastLogFileName_ +
372 ', (' + this.ticks_.total +
373 ' ticks, ' + this.ticks_.unaccounted + ' unaccounted, ' +
374 this.ticks_.excluded + ' excluded).');
375
376 if (this.ticks_.total == 0) return;
377
Steve Blocka7e24c12009-10-30 11:49:00 +0000378 var flatProfile = this.profile_.getFlatProfile();
379 var flatView = this.viewBuilder_.buildView(flatProfile);
380 // Sort by self time, desc, then by name, desc.
381 flatView.sort(function(rec1, rec2) {
382 return rec2.selfTime - rec1.selfTime ||
383 (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
384 var totalTicks = this.ticks_.total;
385 if (this.ignoreUnknown_) {
386 totalTicks -= this.ticks_.unaccounted;
387 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000388 var printAllTicks = !this.onlySummary_;
Steve Blocka7e24c12009-10-30 11:49:00 +0000389
390 // Count library ticks
391 var flatViewNodes = flatView.head.children;
392 var self = this;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000393
Steve Blocka7e24c12009-10-30 11:49:00 +0000394 var libraryTicks = 0;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000395 if(printAllTicks) this.printHeader('Shared libraries');
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000396 this.printEntries(flatViewNodes, totalTicks, null,
Steve Blocka7e24c12009-10-30 11:49:00 +0000397 function(name) { return self.isSharedLibrary(name); },
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000398 function(rec) { libraryTicks += rec.selfTime; }, printAllTicks);
Steve Blocka7e24c12009-10-30 11:49:00 +0000399 var nonLibraryTicks = totalTicks - libraryTicks;
400
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000401 var jsTicks = 0;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000402 if(printAllTicks) this.printHeader('JavaScript');
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000403 this.printEntries(flatViewNodes, totalTicks, nonLibraryTicks,
404 function(name) { return self.isJsCode(name); },
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000405 function(rec) { jsTicks += rec.selfTime; }, printAllTicks);
Steve Blocka7e24c12009-10-30 11:49:00 +0000406
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000407 var cppTicks = 0;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000408 if(printAllTicks) this.printHeader('C++');
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000409 this.printEntries(flatViewNodes, totalTicks, nonLibraryTicks,
410 function(name) { return self.isCppCode(name); },
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000411 function(rec) { cppTicks += rec.selfTime; }, printAllTicks);
Steve Blocka7e24c12009-10-30 11:49:00 +0000412
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000413 this.printHeader('Summary');
414 this.printLine('JavaScript', jsTicks, totalTicks, nonLibraryTicks);
415 this.printLine('C++', cppTicks, totalTicks, nonLibraryTicks);
416 this.printLine('GC', this.ticks_.gc, totalTicks, nonLibraryTicks);
417 this.printLine('Shared libraries', libraryTicks, totalTicks, null);
418 if (!this.ignoreUnknown_ && this.ticks_.unaccounted > 0) {
419 this.printLine('Unaccounted', this.ticks_.unaccounted,
420 this.ticks_.total, null);
421 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000422
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000423 if(printAllTicks) {
424 print('\n [C++ entry points]:');
425 print(' ticks cpp total name');
426 var c_entry_functions = this.profile_.getCEntryProfile();
427 var total_c_entry = c_entry_functions[0].ticks;
428 for (var i = 1; i < c_entry_functions.length; i++) {
429 c = c_entry_functions[i];
430 this.printLine(c.name, c.ticks, total_c_entry, totalTicks);
431 }
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400432
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000433 this.printHeavyProfHeader();
434 var heavyProfile = this.profile_.getBottomUpProfile();
435 var heavyView = this.viewBuilder_.buildView(heavyProfile);
436 // To show the same percentages as in the flat profile.
437 heavyView.head.totalTime = totalTicks;
438 // Sort by total time, desc, then by name, desc.
439 heavyView.sort(function(rec1, rec2) {
440 return rec2.totalTime - rec1.totalTime ||
441 (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
442 this.printHeavyProfile(heavyView.head.children);
443 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000444};
445
446
447function padLeft(s, len) {
448 s = s.toString();
449 if (s.length < len) {
450 var padLength = len - s.length;
451 if (!(padLength in padLeft)) {
452 padLeft[padLength] = new Array(padLength + 1).join(' ');
453 }
454 s = padLeft[padLength] + s;
455 }
456 return s;
457};
458
459
460TickProcessor.prototype.printHeader = function(headerTitle) {
461 print('\n [' + headerTitle + ']:');
462 print(' ticks total nonlib name');
463};
464
465
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000466TickProcessor.prototype.printLine = function(
467 entry, ticks, totalTicks, nonLibTicks) {
468 var pct = ticks * 100 / totalTicks;
469 var nonLibPct = nonLibTicks != null
470 ? padLeft((ticks * 100 / nonLibTicks).toFixed(1), 5) + '% '
471 : ' ';
472 print(' ' + padLeft(ticks, 5) + ' ' +
473 padLeft(pct.toFixed(1), 5) + '% ' +
474 nonLibPct +
475 entry);
476}
477
Steve Blocka7e24c12009-10-30 11:49:00 +0000478TickProcessor.prototype.printHeavyProfHeader = function() {
479 print('\n [Bottom up (heavy) profile]:');
480 print(' Note: percentage shows a share of a particular caller in the ' +
481 'total\n' +
482 ' amount of its parent calls.');
483 print(' Callers occupying less than ' +
484 TickProcessor.CALL_PROFILE_CUTOFF_PCT.toFixed(1) +
485 '% are not shown.\n');
486 print(' ticks parent name');
487};
488
489
Steve Blocka7e24c12009-10-30 11:49:00 +0000490TickProcessor.prototype.processProfile = function(
491 profile, filterP, func) {
492 for (var i = 0, n = profile.length; i < n; ++i) {
493 var rec = profile[i];
494 if (!filterP(rec.internalFuncName)) {
495 continue;
496 }
497 func(rec);
498 }
499};
500
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000501TickProcessor.prototype.getLineAndColumn = function(name) {
502 var re = /:([0-9]+):([0-9]+)$/;
503 var array = re.exec(name);
504 if (!array) {
505 return null;
506 }
507 return {line: array[1], column: array[2]};
508}
509
510TickProcessor.prototype.hasSourceMap = function() {
511 return this.sourceMap != null;
512};
513
514
515TickProcessor.prototype.formatFunctionName = function(funcName) {
516 if (!this.hasSourceMap()) {
517 return funcName;
518 }
519 var lc = this.getLineAndColumn(funcName);
520 if (lc == null) {
521 return funcName;
522 }
523 // in source maps lines and columns are zero based
524 var lineNumber = lc.line - 1;
525 var column = lc.column - 1;
526 var entry = this.sourceMap.findEntry(lineNumber, column);
527 var sourceFile = entry[2];
528 var sourceLine = entry[3] + 1;
529 var sourceColumn = entry[4] + 1;
530
531 return sourceFile + ':' + sourceLine + ':' + sourceColumn + ' -> ' + funcName;
532};
Steve Blocka7e24c12009-10-30 11:49:00 +0000533
534TickProcessor.prototype.printEntries = function(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000535 profile, totalTicks, nonLibTicks, filterP, callback, printAllTicks) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000536 var that = this;
Steve Blocka7e24c12009-10-30 11:49:00 +0000537 this.processProfile(profile, filterP, function (rec) {
538 if (rec.selfTime == 0) return;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000539 callback(rec);
540 var funcName = that.formatFunctionName(rec.internalFuncName);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000541 if(printAllTicks) {
542 that.printLine(funcName, rec.selfTime, totalTicks, nonLibTicks);
543 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000544 });
545};
546
547
548TickProcessor.prototype.printHeavyProfile = function(profile, opt_indent) {
549 var self = this;
550 var indent = opt_indent || 0;
551 var indentStr = padLeft('', indent);
552 this.processProfile(profile, function() { return true; }, function (rec) {
553 // Cut off too infrequent callers.
554 if (rec.parentTotalPercent < TickProcessor.CALL_PROFILE_CUTOFF_PCT) return;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000555 var funcName = self.formatFunctionName(rec.internalFuncName);
Steve Blocka7e24c12009-10-30 11:49:00 +0000556 print(' ' + padLeft(rec.totalTime, 5) + ' ' +
557 padLeft(rec.parentTotalPercent.toFixed(1), 5) + '% ' +
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000558 indentStr + funcName);
Steve Blocka7e24c12009-10-30 11:49:00 +0000559 // Limit backtrace depth.
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100560 if (indent < 2 * self.callGraphSize_) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000561 self.printHeavyProfile(rec.children, indent + 2);
562 }
563 // Delimit top-level functions.
564 if (indent == 0) {
565 print('');
566 }
567 });
568};
569
570
571function CppEntriesProvider() {
572};
573
574
575CppEntriesProvider.prototype.parseVmSymbols = function(
Ben Murdochc5610432016-08-08 18:44:38 +0100576 libName, libStart, libEnd, libASLRSlide, processorFunc) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000577 this.loadSymbols(libName);
578
579 var prevEntry;
580
581 function addEntry(funcInfo) {
582 // Several functions can be mapped onto the same address. To avoid
583 // creating zero-sized entries, skip such duplicates.
584 // Also double-check that function belongs to the library address space.
585 if (prevEntry && !prevEntry.end &&
586 prevEntry.start < funcInfo.start &&
587 prevEntry.start >= libStart && funcInfo.start <= libEnd) {
588 processorFunc(prevEntry.name, prevEntry.start, funcInfo.start);
589 }
590 if (funcInfo.end &&
591 (!prevEntry || prevEntry.start != funcInfo.start) &&
592 funcInfo.start >= libStart && funcInfo.end <= libEnd) {
593 processorFunc(funcInfo.name, funcInfo.start, funcInfo.end);
594 }
595 prevEntry = funcInfo;
596 }
597
598 while (true) {
599 var funcInfo = this.parseNextLine();
600 if (funcInfo === null) {
601 continue;
602 } else if (funcInfo === false) {
603 break;
604 }
Ben Murdochc5610432016-08-08 18:44:38 +0100605 funcInfo.start += libASLRSlide;
Steve Blocka7e24c12009-10-30 11:49:00 +0000606 if (funcInfo.start < libStart && funcInfo.start < libEnd - libStart) {
607 funcInfo.start += libStart;
608 }
609 if (funcInfo.size) {
610 funcInfo.end = funcInfo.start + funcInfo.size;
611 }
612 addEntry(funcInfo);
613 }
614 addEntry({name: '', start: libEnd});
615};
616
617
618CppEntriesProvider.prototype.loadSymbols = function(libName) {
619};
620
621
622CppEntriesProvider.prototype.parseNextLine = function() {
623 return false;
624};
625
626
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000627function UnixCppEntriesProvider(nmExec, targetRootFS) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000628 this.symbols = [];
629 this.parsePos = 0;
630 this.nmExec = nmExec;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000631 this.targetRootFS = targetRootFS;
Steve Blocka7e24c12009-10-30 11:49:00 +0000632 this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ([0-9a-fA-F]{8,16} )?[tTwW] (.*)$/;
633};
634inherits(UnixCppEntriesProvider, CppEntriesProvider);
635
636
637UnixCppEntriesProvider.prototype.loadSymbols = function(libName) {
638 this.parsePos = 0;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000639 libName = this.targetRootFS + libName;
Steve Blocka7e24c12009-10-30 11:49:00 +0000640 try {
641 this.symbols = [
642 os.system(this.nmExec, ['-C', '-n', '-S', libName], -1, -1),
643 os.system(this.nmExec, ['-C', '-n', '-S', '-D', libName], -1, -1)
644 ];
645 } catch (e) {
646 // If the library cannot be found on this system let's not panic.
647 this.symbols = ['', ''];
648 }
649};
650
651
652UnixCppEntriesProvider.prototype.parseNextLine = function() {
653 if (this.symbols.length == 0) {
654 return false;
655 }
656 var lineEndPos = this.symbols[0].indexOf('\n', this.parsePos);
657 if (lineEndPos == -1) {
658 this.symbols.shift();
659 this.parsePos = 0;
660 return this.parseNextLine();
661 }
662
663 var line = this.symbols[0].substring(this.parsePos, lineEndPos);
664 this.parsePos = lineEndPos + 1;
665 var fields = line.match(this.FUNC_RE);
666 var funcInfo = null;
667 if (fields) {
668 funcInfo = { name: fields[3], start: parseInt(fields[1], 16) };
669 if (fields[2]) {
670 funcInfo.size = parseInt(fields[2], 16);
671 }
672 }
673 return funcInfo;
674};
675
676
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000677function MacCppEntriesProvider(nmExec, targetRootFS) {
678 UnixCppEntriesProvider.call(this, nmExec, targetRootFS);
Steve Blocka7e24c12009-10-30 11:49:00 +0000679 // Note an empty group. It is required, as UnixCppEntriesProvider expects 3 groups.
680 this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ()[iItT] (.*)$/;
681};
682inherits(MacCppEntriesProvider, UnixCppEntriesProvider);
683
684
685MacCppEntriesProvider.prototype.loadSymbols = function(libName) {
686 this.parsePos = 0;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000687 libName = this.targetRootFS + libName;
Ben Murdochda12d292016-06-02 14:46:10 +0100688
689 // It seems that in OS X `nm` thinks that `-f` is a format option, not a
690 // "flat" display option flag.
Steve Blocka7e24c12009-10-30 11:49:00 +0000691 try {
Ben Murdochda12d292016-06-02 14:46:10 +0100692 this.symbols = [os.system(this.nmExec, ['-n', libName], -1, -1), ''];
Steve Blocka7e24c12009-10-30 11:49:00 +0000693 } catch (e) {
694 // If the library cannot be found on this system let's not panic.
695 this.symbols = '';
696 }
697};
698
699
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000700function WindowsCppEntriesProvider(_ignored_nmExec, targetRootFS) {
701 this.targetRootFS = targetRootFS;
Steve Blocka7e24c12009-10-30 11:49:00 +0000702 this.symbols = '';
703 this.parsePos = 0;
704};
705inherits(WindowsCppEntriesProvider, CppEntriesProvider);
706
707
708WindowsCppEntriesProvider.FILENAME_RE = /^(.*)\.([^.]+)$/;
709
710
711WindowsCppEntriesProvider.FUNC_RE =
712 /^\s+0001:[0-9a-fA-F]{8}\s+([_\?@$0-9a-zA-Z]+)\s+([0-9a-fA-F]{8}).*$/;
713
714
715WindowsCppEntriesProvider.IMAGE_BASE_RE =
716 /^\s+0000:00000000\s+___ImageBase\s+([0-9a-fA-F]{8}).*$/;
717
718
719// This is almost a constant on Windows.
720WindowsCppEntriesProvider.EXE_IMAGE_BASE = 0x00400000;
721
722
723WindowsCppEntriesProvider.prototype.loadSymbols = function(libName) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000724 libName = this.targetRootFS + libName;
Steve Blocka7e24c12009-10-30 11:49:00 +0000725 var fileNameFields = libName.match(WindowsCppEntriesProvider.FILENAME_RE);
726 if (!fileNameFields) return;
727 var mapFileName = fileNameFields[1] + '.map';
728 this.moduleType_ = fileNameFields[2].toLowerCase();
729 try {
730 this.symbols = read(mapFileName);
731 } catch (e) {
732 // If .map file cannot be found let's not panic.
733 this.symbols = '';
734 }
735};
736
737
738WindowsCppEntriesProvider.prototype.parseNextLine = function() {
739 var lineEndPos = this.symbols.indexOf('\r\n', this.parsePos);
740 if (lineEndPos == -1) {
741 return false;
742 }
743
744 var line = this.symbols.substring(this.parsePos, lineEndPos);
745 this.parsePos = lineEndPos + 2;
746
747 // Image base entry is above all other symbols, so we can just
748 // terminate parsing.
749 var imageBaseFields = line.match(WindowsCppEntriesProvider.IMAGE_BASE_RE);
750 if (imageBaseFields) {
751 var imageBase = parseInt(imageBaseFields[1], 16);
752 if ((this.moduleType_ == 'exe') !=
753 (imageBase == WindowsCppEntriesProvider.EXE_IMAGE_BASE)) {
754 return false;
755 }
756 }
757
758 var fields = line.match(WindowsCppEntriesProvider.FUNC_RE);
759 return fields ?
760 { name: this.unmangleName(fields[1]), start: parseInt(fields[2], 16) } :
761 null;
762};
763
764
765/**
766 * Performs very simple unmangling of C++ names.
767 *
768 * Does not handle arguments and template arguments. The mangled names have
769 * the form:
770 *
771 * ?LookupInDescriptor@JSObject@internal@v8@@...arguments info...
772 */
773WindowsCppEntriesProvider.prototype.unmangleName = function(name) {
774 // Empty or non-mangled name.
775 if (name.length < 1 || name.charAt(0) != '?') return name;
776 var nameEndPos = name.indexOf('@@');
777 var components = name.substring(1, nameEndPos).split('@');
778 components.reverse();
779 return components.join('::');
780};
781
782
783function ArgumentsProcessor(args) {
784 this.args_ = args;
785 this.result_ = ArgumentsProcessor.DEFAULTS;
786
787 this.argsDispatch_ = {
788 '-j': ['stateFilter', TickProcessor.VmStates.JS,
789 'Show only ticks from JS VM state'],
790 '-g': ['stateFilter', TickProcessor.VmStates.GC,
791 'Show only ticks from GC VM state'],
792 '-c': ['stateFilter', TickProcessor.VmStates.COMPILER,
793 'Show only ticks from COMPILER VM state'],
794 '-o': ['stateFilter', TickProcessor.VmStates.OTHER,
795 'Show only ticks from OTHER VM state'],
796 '-e': ['stateFilter', TickProcessor.VmStates.EXTERNAL,
797 'Show only ticks from EXTERNAL VM state'],
Ben Murdoch61f157c2016-09-16 13:49:30 +0100798 '--filter-runtime-timer': ['runtimeTimerFilter', null,
799 'Show only ticks matching the given runtime timer scope'],
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100800 '--call-graph-size': ['callGraphSize', TickProcessor.CALL_GRAPH_SIZE,
801 'Set the call graph size'],
Steve Blocka7e24c12009-10-30 11:49:00 +0000802 '--ignore-unknown': ['ignoreUnknown', true,
803 'Exclude ticks of unknown code entries from processing'],
804 '--separate-ic': ['separateIc', true,
805 'Separate IC entries'],
806 '--unix': ['platform', 'unix',
807 'Specify that we are running on *nix platform'],
808 '--windows': ['platform', 'windows',
809 'Specify that we are running on Windows platform'],
810 '--mac': ['platform', 'mac',
811 'Specify that we are running on Mac OS X platform'],
812 '--nm': ['nm', 'nm',
Leon Clarkee46be812010-01-19 14:06:41 +0000813 'Specify the \'nm\' executable to use (e.g. --nm=/my_dir/nm)'],
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000814 '--target': ['targetRootFS', '',
815 'Specify the target root directory for cross environment'],
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000816 '--range': ['range', 'auto,auto',
817 'Specify the range limit as [start],[end]'],
818 '--distortion': ['distortion', 0,
819 'Specify the logging overhead in picoseconds'],
820 '--source-map': ['sourceMap', null,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000821 'Specify the source map that should be used for output'],
822 '--timed-range': ['timedRange', true,
823 'Ignore ticks before first and after last Date.now() call'],
824 '--pairwise-timed-range': ['pairwiseTimedRange', true,
825 'Ignore ticks outside pairs of Date.now() calls'],
826 '--only-summary': ['onlySummary', true,
827 'Print only tick summary, exclude other information']
Steve Blocka7e24c12009-10-30 11:49:00 +0000828 };
829 this.argsDispatch_['--js'] = this.argsDispatch_['-j'];
830 this.argsDispatch_['--gc'] = this.argsDispatch_['-g'];
831 this.argsDispatch_['--compiler'] = this.argsDispatch_['-c'];
832 this.argsDispatch_['--other'] = this.argsDispatch_['-o'];
833 this.argsDispatch_['--external'] = this.argsDispatch_['-e'];
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000834 this.argsDispatch_['--ptr'] = this.argsDispatch_['--pairwise-timed-range'];
Steve Blocka7e24c12009-10-30 11:49:00 +0000835};
836
837
838ArgumentsProcessor.DEFAULTS = {
839 logFileName: 'v8.log',
Steve Blocka7e24c12009-10-30 11:49:00 +0000840 platform: 'unix',
841 stateFilter: null,
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100842 callGraphSize: 5,
Steve Blocka7e24c12009-10-30 11:49:00 +0000843 ignoreUnknown: false,
844 separateIc: false,
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000845 targetRootFS: '',
846 nm: 'nm',
847 range: 'auto,auto',
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000848 distortion: 0,
849 timedRange: false,
850 pairwiseTimedRange: false,
Ben Murdoch61f157c2016-09-16 13:49:30 +0100851 onlySummary: false,
852 runtimeTimerFilter: null,
Steve Blocka7e24c12009-10-30 11:49:00 +0000853};
854
855
856ArgumentsProcessor.prototype.parse = function() {
857 while (this.args_.length) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000858 var arg = this.args_.shift();
Steve Blocka7e24c12009-10-30 11:49:00 +0000859 if (arg.charAt(0) != '-') {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000860 this.result_.logFileName = arg;
861 continue;
Steve Blocka7e24c12009-10-30 11:49:00 +0000862 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000863 var userValue = null;
864 var eqPos = arg.indexOf('=');
865 if (eqPos != -1) {
866 userValue = arg.substr(eqPos + 1);
867 arg = arg.substr(0, eqPos);
868 }
869 if (arg in this.argsDispatch_) {
870 var dispatch = this.argsDispatch_[arg];
871 this.result_[dispatch[0]] = userValue == null ? dispatch[1] : userValue;
872 } else {
873 return false;
874 }
875 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000876 return true;
877};
878
879
880ArgumentsProcessor.prototype.result = function() {
881 return this.result_;
882};
883
884
885ArgumentsProcessor.prototype.printUsageAndExit = function() {
886
887 function padRight(s, len) {
888 s = s.toString();
889 if (s.length < len) {
890 s = s + (new Array(len - s.length + 1).join(' '));
891 }
892 return s;
893 }
894
895 print('Cmdline args: [options] [log-file-name]\n' +
896 'Default log file name is "' +
897 ArgumentsProcessor.DEFAULTS.logFileName + '".\n');
898 print('Options:');
899 for (var arg in this.argsDispatch_) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000900 var synonyms = [arg];
Steve Blocka7e24c12009-10-30 11:49:00 +0000901 var dispatch = this.argsDispatch_[arg];
902 for (var synArg in this.argsDispatch_) {
903 if (arg !== synArg && dispatch === this.argsDispatch_[synArg]) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000904 synonyms.push(synArg);
Steve Blocka7e24c12009-10-30 11:49:00 +0000905 delete this.argsDispatch_[synArg];
906 }
907 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000908 print(' ' + padRight(synonyms.join(', '), 20) + " " + dispatch[2]);
Steve Blocka7e24c12009-10-30 11:49:00 +0000909 }
910 quit(2);
911};