1// Copyright 2012 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 28function inherits(childCtor, parentCtor) { 29 childCtor.prototype.__proto__ = parentCtor.prototype; 30}; 31 32 33function V8Profile(separateIc, separateBytecodes, separateBuiltins, 34 separateStubs) { 35 Profile.call(this); 36 var regexps = []; 37 if (!separateIc) regexps.push(V8Profile.IC_RE); 38 if (!separateBytecodes) regexps.push(V8Profile.BYTECODES_RE); 39 if (!separateBuiltins) regexps.push(V8Profile.BUILTINS_RE); 40 if (!separateStubs) regexps.push(V8Profile.STUBS_RE); 41 if (regexps.length > 0) { 42 this.skipThisFunction = function(name) { 43 for (var i=0; i<regexps.length; i++) { 44 if (regexps[i].test(name)) return true; 45 } 46 return false; 47 }; 48 } 49}; 50inherits(V8Profile, Profile); 51 52 53V8Profile.IC_RE = 54 /^(LoadGlobalIC: )|(Handler: )|(?:CallIC|LoadIC|StoreIC)|(?:Builtin: (?:Keyed)?(?:Load|Store)IC_)/; 55V8Profile.BYTECODES_RE = /^(BytecodeHandler: )/ 56V8Profile.BUILTINS_RE = /^(Builtin: )/ 57V8Profile.STUBS_RE = /^(Stub: )/ 58 59 60/** 61 * A thin wrapper around shell's 'read' function showing a file name on error. 62 */ 63function readFile(fileName) { 64 try { 65 return read(fileName); 66 } catch (e) { 67 printErr(fileName + ': ' + (e.message || e)); 68 throw e; 69 } 70} 71 72 73/** 74 * Parser for dynamic code optimization state. 75 */ 76function parseState(s) { 77 switch (s) { 78 case "": return Profile.CodeState.COMPILED; 79 case "~": return Profile.CodeState.OPTIMIZABLE; 80 case "*": return Profile.CodeState.OPTIMIZED; 81 } 82 throw new Error("unknown code state: " + s); 83} 84 85 86function TickProcessor( 87 cppEntriesProvider, 88 separateIc, 89 separateBytecodes, 90 separateBuiltins, 91 separateStubs, 92 callGraphSize, 93 ignoreUnknown, 94 stateFilter, 95 distortion, 96 range, 97 sourceMap, 98 timedRange, 99 pairwiseTimedRange, 100 onlySummary, 101 runtimeTimerFilter, 102 preprocessJson) { 103 this.preprocessJson = preprocessJson; 104 LogReader.call(this, { 105 'shared-library': { parsers: [parseString, parseInt, parseInt, parseInt], 106 processor: this.processSharedLibrary }, 107 'code-creation': { 108 parsers: [parseString, parseInt, parseInt, parseInt, parseInt, 109 parseString, parseVarArgs], 110 processor: this.processCodeCreation }, 111 'code-deopt': { 112 parsers: [parseInt, parseInt, parseInt, parseInt, parseInt, 113 parseString, parseString, parseString], 114 processor: this.processCodeDeopt }, 115 'code-move': { parsers: [parseInt, parseInt, ], 116 processor: this.processCodeMove }, 117 'code-delete': { parsers: [parseInt], 118 processor: this.processCodeDelete }, 119 'code-source-info': { 120 parsers: [parseInt, parseInt, parseInt, parseInt, parseString, 121 parseString, parseString], 122 processor: this.processCodeSourceInfo }, 123 'script-source': { 124 parsers: [parseInt, parseString, parseString], 125 processor: this.processScriptSource }, 126 'sfi-move': { parsers: [parseInt, parseInt], 127 processor: this.processFunctionMove }, 128 'active-runtime-timer': { 129 parsers: [parseString], 130 processor: this.processRuntimeTimerEvent }, 131 'tick': { 132 parsers: [parseInt, parseInt, parseInt, 133 parseInt, parseInt, parseVarArgs], 134 processor: this.processTick }, 135 'heap-sample-begin': { parsers: [parseString, parseString, parseInt], 136 processor: this.processHeapSampleBegin }, 137 'heap-sample-end': { parsers: [parseString, parseString], 138 processor: this.processHeapSampleEnd }, 139 'timer-event-start' : { parsers: [parseString, parseString, parseString], 140 processor: this.advanceDistortion }, 141 'timer-event-end' : { parsers: [parseString, parseString, parseString], 142 processor: this.advanceDistortion }, 143 // Ignored events. 144 'profiler': null, 145 'function-creation': null, 146 'function-move': null, 147 'function-delete': null, 148 'heap-sample-item': null, 149 'current-time': null, // Handled specially, not parsed. 150 // Obsolete row types. 151 'code-allocate': null, 152 'begin-code-region': null, 153 'end-code-region': null }, 154 timedRange, 155 pairwiseTimedRange); 156 157 this.cppEntriesProvider_ = cppEntriesProvider; 158 this.callGraphSize_ = callGraphSize; 159 this.ignoreUnknown_ = ignoreUnknown; 160 this.stateFilter_ = stateFilter; 161 this.runtimeTimerFilter_ = runtimeTimerFilter; 162 this.sourceMap = sourceMap; 163 var ticks = this.ticks_ = 164 { total: 0, unaccounted: 0, excluded: 0, gc: 0 }; 165 166 distortion = parseInt(distortion); 167 // Convert picoseconds to nanoseconds. 168 this.distortion_per_entry = isNaN(distortion) ? 0 : (distortion / 1000); 169 this.distortion = 0; 170 var rangelimits = range ? range.split(",") : []; 171 var range_start = parseInt(rangelimits[0]); 172 var range_end = parseInt(rangelimits[1]); 173 // Convert milliseconds to nanoseconds. 174 this.range_start = isNaN(range_start) ? -Infinity : (range_start * 1000); 175 this.range_end = isNaN(range_end) ? Infinity : (range_end * 1000) 176 177 V8Profile.prototype.handleUnknownCode = function( 178 operation, addr, opt_stackPos) { 179 var op = Profile.Operation; 180 switch (operation) { 181 case op.MOVE: 182 printErr('Code move event for unknown code: 0x' + addr.toString(16)); 183 break; 184 case op.DELETE: 185 printErr('Code delete event for unknown code: 0x' + addr.toString(16)); 186 break; 187 case op.TICK: 188 // Only unknown PCs (the first frame) are reported as unaccounted, 189 // otherwise tick balance will be corrupted (this behavior is compatible 190 // with the original tickprocessor.py script.) 191 if (opt_stackPos == 0) { 192 ticks.unaccounted++; 193 } 194 break; 195 } 196 }; 197 198 if (preprocessJson) { 199 this.profile_ = new JsonProfile(); 200 } else { 201 this.profile_ = new V8Profile(separateIc, separateBytecodes, 202 separateBuiltins, separateStubs); 203 } 204 this.codeTypes_ = {}; 205 // Count each tick as a time unit. 206 this.viewBuilder_ = new ViewBuilder(1); 207 this.lastLogFileName_ = null; 208 209 this.generation_ = 1; 210 this.currentProducerProfile_ = null; 211 this.onlySummary_ = onlySummary; 212}; 213inherits(TickProcessor, LogReader); 214 215 216TickProcessor.VmStates = { 217 JS: 0, 218 GC: 1, 219 PARSER: 2, 220 BYTECODE_COMPILER: 3, 221 COMPILER: 4, 222 OTHER: 5, 223 EXTERNAL: 6, 224 IDLE: 7, 225}; 226 227 228TickProcessor.CodeTypes = { 229 CPP: 0, 230 SHARED_LIB: 1 231}; 232// Otherwise, this is JS-related code. We are not adding it to 233// codeTypes_ map because there can be zillions of them. 234 235 236TickProcessor.CALL_PROFILE_CUTOFF_PCT = 1.0; 237 238TickProcessor.CALL_GRAPH_SIZE = 5; 239 240/** 241 * @override 242 */ 243TickProcessor.prototype.printError = function(str) { 244 printErr(str); 245}; 246 247 248TickProcessor.prototype.setCodeType = function(name, type) { 249 this.codeTypes_[name] = TickProcessor.CodeTypes[type]; 250}; 251 252 253TickProcessor.prototype.isSharedLibrary = function(name) { 254 return this.codeTypes_[name] == TickProcessor.CodeTypes.SHARED_LIB; 255}; 256 257 258TickProcessor.prototype.isCppCode = function(name) { 259 return this.codeTypes_[name] == TickProcessor.CodeTypes.CPP; 260}; 261 262 263TickProcessor.prototype.isJsCode = function(name) { 264 return name !== "UNKNOWN" && !(name in this.codeTypes_); 265}; 266 267 268TickProcessor.prototype.processLogFile = function(fileName) { 269 this.lastLogFileName_ = fileName; 270 var line; 271 while (line = readline()) { 272 this.processLogLine(line); 273 } 274}; 275 276 277TickProcessor.prototype.processLogFileInTest = function(fileName) { 278 // Hack file name to avoid dealing with platform specifics. 279 this.lastLogFileName_ = 'v8.log'; 280 var contents = readFile(fileName); 281 this.processLogChunk(contents); 282}; 283 284 285TickProcessor.prototype.processSharedLibrary = function( 286 name, startAddr, endAddr, aslrSlide) { 287 var entry = this.profile_.addLibrary(name, startAddr, endAddr, aslrSlide); 288 this.setCodeType(entry.getName(), 'SHARED_LIB'); 289 290 var self = this; 291 var libFuncs = this.cppEntriesProvider_.parseVmSymbols( 292 name, startAddr, endAddr, aslrSlide, function(fName, fStart, fEnd) { 293 self.profile_.addStaticCode(fName, fStart, fEnd); 294 self.setCodeType(fName, 'CPP'); 295 }); 296}; 297 298 299TickProcessor.prototype.processCodeCreation = function( 300 type, kind, timestamp, start, size, name, maybe_func) { 301 if (maybe_func.length) { 302 var funcAddr = parseInt(maybe_func[0]); 303 var state = parseState(maybe_func[1]); 304 this.profile_.addFuncCode(type, name, timestamp, start, size, funcAddr, state); 305 } else { 306 this.profile_.addCode(type, name, timestamp, start, size); 307 } 308}; 309 310 311TickProcessor.prototype.processCodeDeopt = function( 312 timestamp, size, code, inliningId, scriptOffset, bailoutType, 313 sourcePositionText, deoptReasonText) { 314 this.profile_.deoptCode(timestamp, code, inliningId, scriptOffset, 315 bailoutType, sourcePositionText, deoptReasonText); 316}; 317 318 319TickProcessor.prototype.processCodeMove = function(from, to) { 320 this.profile_.moveCode(from, to); 321}; 322 323TickProcessor.prototype.processCodeDelete = function(start) { 324 this.profile_.deleteCode(start); 325}; 326 327TickProcessor.prototype.processCodeSourceInfo = function( 328 start, script, startPos, endPos, sourcePositions, inliningPositions, 329 inlinedFunctions) { 330 this.profile_.addSourcePositions(start, script, startPos, 331 endPos, sourcePositions, inliningPositions, inlinedFunctions); 332}; 333 334TickProcessor.prototype.processScriptSource = function(script, url, source) { 335 this.profile_.addScriptSource(script, url, source); 336}; 337 338TickProcessor.prototype.processFunctionMove = function(from, to) { 339 this.profile_.moveFunc(from, to); 340}; 341 342 343TickProcessor.prototype.includeTick = function(vmState) { 344 if (this.stateFilter_ !== null) { 345 return this.stateFilter_ == vmState; 346 } else if (this.runtimeTimerFilter_ !== null) { 347 return this.currentRuntimeTimer == this.runtimeTimerFilter_; 348 } 349 return true; 350}; 351 352TickProcessor.prototype.processRuntimeTimerEvent = function(name) { 353 this.currentRuntimeTimer = name; 354} 355 356TickProcessor.prototype.processTick = function(pc, 357 ns_since_start, 358 is_external_callback, 359 tos_or_external_callback, 360 vmState, 361 stack) { 362 this.distortion += this.distortion_per_entry; 363 ns_since_start -= this.distortion; 364 if (ns_since_start < this.range_start || ns_since_start > this.range_end) { 365 return; 366 } 367 this.ticks_.total++; 368 if (vmState == TickProcessor.VmStates.GC) this.ticks_.gc++; 369 if (!this.includeTick(vmState)) { 370 this.ticks_.excluded++; 371 return; 372 } 373 if (is_external_callback) { 374 // Don't use PC when in external callback code, as it can point 375 // inside callback's code, and we will erroneously report 376 // that a callback calls itself. Instead we use tos_or_external_callback, 377 // as simply resetting PC will produce unaccounted ticks. 378 pc = tos_or_external_callback; 379 tos_or_external_callback = 0; 380 } else if (tos_or_external_callback) { 381 // Find out, if top of stack was pointing inside a JS function 382 // meaning that we have encountered a frameless invocation. 383 var funcEntry = this.profile_.findEntry(tos_or_external_callback); 384 if (!funcEntry || !funcEntry.isJSFunction || !funcEntry.isJSFunction()) { 385 tos_or_external_callback = 0; 386 } 387 } 388 389 this.profile_.recordTick( 390 ns_since_start, vmState, 391 this.processStack(pc, tos_or_external_callback, stack)); 392}; 393 394 395TickProcessor.prototype.advanceDistortion = function() { 396 this.distortion += this.distortion_per_entry; 397} 398 399 400TickProcessor.prototype.processHeapSampleBegin = function(space, state, ticks) { 401 if (space != 'Heap') return; 402 this.currentProducerProfile_ = new CallTree(); 403}; 404 405 406TickProcessor.prototype.processHeapSampleEnd = function(space, state) { 407 if (space != 'Heap' || !this.currentProducerProfile_) return; 408 409 print('Generation ' + this.generation_ + ':'); 410 var tree = this.currentProducerProfile_; 411 tree.computeTotalWeights(); 412 var producersView = this.viewBuilder_.buildView(tree); 413 // Sort by total time, desc, then by name, desc. 414 producersView.sort(function(rec1, rec2) { 415 return rec2.totalTime - rec1.totalTime || 416 (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); }); 417 this.printHeavyProfile(producersView.head.children); 418 419 this.currentProducerProfile_ = null; 420 this.generation_++; 421}; 422 423 424TickProcessor.prototype.printStatistics = function() { 425 if (this.preprocessJson) { 426 this.profile_.writeJson(); 427 return; 428 } 429 430 print('Statistical profiling result from ' + this.lastLogFileName_ + 431 ', (' + this.ticks_.total + 432 ' ticks, ' + this.ticks_.unaccounted + ' unaccounted, ' + 433 this.ticks_.excluded + ' excluded).'); 434 435 if (this.ticks_.total == 0) return; 436 437 var flatProfile = this.profile_.getFlatProfile(); 438 var flatView = this.viewBuilder_.buildView(flatProfile); 439 // Sort by self time, desc, then by name, desc. 440 flatView.sort(function(rec1, rec2) { 441 return rec2.selfTime - rec1.selfTime || 442 (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); }); 443 var totalTicks = this.ticks_.total; 444 if (this.ignoreUnknown_) { 445 totalTicks -= this.ticks_.unaccounted; 446 } 447 var printAllTicks = !this.onlySummary_; 448 449 // Count library ticks 450 var flatViewNodes = flatView.head.children; 451 var self = this; 452 453 var libraryTicks = 0; 454 if(printAllTicks) this.printHeader('Shared libraries'); 455 this.printEntries(flatViewNodes, totalTicks, null, 456 function(name) { return self.isSharedLibrary(name); }, 457 function(rec) { libraryTicks += rec.selfTime; }, printAllTicks); 458 var nonLibraryTicks = totalTicks - libraryTicks; 459 460 var jsTicks = 0; 461 if(printAllTicks) this.printHeader('JavaScript'); 462 this.printEntries(flatViewNodes, totalTicks, nonLibraryTicks, 463 function(name) { return self.isJsCode(name); }, 464 function(rec) { jsTicks += rec.selfTime; }, printAllTicks); 465 466 var cppTicks = 0; 467 if(printAllTicks) this.printHeader('C++'); 468 this.printEntries(flatViewNodes, totalTicks, nonLibraryTicks, 469 function(name) { return self.isCppCode(name); }, 470 function(rec) { cppTicks += rec.selfTime; }, printAllTicks); 471 472 this.printHeader('Summary'); 473 this.printLine('JavaScript', jsTicks, totalTicks, nonLibraryTicks); 474 this.printLine('C++', cppTicks, totalTicks, nonLibraryTicks); 475 this.printLine('GC', this.ticks_.gc, totalTicks, nonLibraryTicks); 476 this.printLine('Shared libraries', libraryTicks, totalTicks, null); 477 if (!this.ignoreUnknown_ && this.ticks_.unaccounted > 0) { 478 this.printLine('Unaccounted', this.ticks_.unaccounted, 479 this.ticks_.total, null); 480 } 481 482 if(printAllTicks) { 483 print('\n [C++ entry points]:'); 484 print(' ticks cpp total name'); 485 var c_entry_functions = this.profile_.getCEntryProfile(); 486 var total_c_entry = c_entry_functions[0].ticks; 487 for (var i = 1; i < c_entry_functions.length; i++) { 488 c = c_entry_functions[i]; 489 this.printLine(c.name, c.ticks, total_c_entry, totalTicks); 490 } 491 492 this.printHeavyProfHeader(); 493 var heavyProfile = this.profile_.getBottomUpProfile(); 494 var heavyView = this.viewBuilder_.buildView(heavyProfile); 495 // To show the same percentages as in the flat profile. 496 heavyView.head.totalTime = totalTicks; 497 // Sort by total time, desc, then by name, desc. 498 heavyView.sort(function(rec1, rec2) { 499 return rec2.totalTime - rec1.totalTime || 500 (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); }); 501 this.printHeavyProfile(heavyView.head.children); 502 } 503}; 504 505 506function padLeft(s, len) { 507 s = s.toString(); 508 if (s.length < len) { 509 var padLength = len - s.length; 510 if (!(padLength in padLeft)) { 511 padLeft[padLength] = new Array(padLength + 1).join(' '); 512 } 513 s = padLeft[padLength] + s; 514 } 515 return s; 516}; 517 518 519TickProcessor.prototype.printHeader = function(headerTitle) { 520 print('\n [' + headerTitle + ']:'); 521 print(' ticks total nonlib name'); 522}; 523 524 525TickProcessor.prototype.printLine = function( 526 entry, ticks, totalTicks, nonLibTicks) { 527 var pct = ticks * 100 / totalTicks; 528 var nonLibPct = nonLibTicks != null 529 ? padLeft((ticks * 100 / nonLibTicks).toFixed(1), 5) + '% ' 530 : ' '; 531 print(' ' + padLeft(ticks, 5) + ' ' + 532 padLeft(pct.toFixed(1), 5) + '% ' + 533 nonLibPct + 534 entry); 535} 536 537TickProcessor.prototype.printHeavyProfHeader = function() { 538 print('\n [Bottom up (heavy) profile]:'); 539 print(' Note: percentage shows a share of a particular caller in the ' + 540 'total\n' + 541 ' amount of its parent calls.'); 542 print(' Callers occupying less than ' + 543 TickProcessor.CALL_PROFILE_CUTOFF_PCT.toFixed(1) + 544 '% are not shown.\n'); 545 print(' ticks parent name'); 546}; 547 548 549TickProcessor.prototype.processProfile = function( 550 profile, filterP, func) { 551 for (var i = 0, n = profile.length; i < n; ++i) { 552 var rec = profile[i]; 553 if (!filterP(rec.internalFuncName)) { 554 continue; 555 } 556 func(rec); 557 } 558}; 559 560TickProcessor.prototype.getLineAndColumn = function(name) { 561 var re = /:([0-9]+):([0-9]+)$/; 562 var array = re.exec(name); 563 if (!array) { 564 return null; 565 } 566 return {line: array[1], column: array[2]}; 567} 568 569TickProcessor.prototype.hasSourceMap = function() { 570 return this.sourceMap != null; 571}; 572 573 574TickProcessor.prototype.formatFunctionName = function(funcName) { 575 if (!this.hasSourceMap()) { 576 return funcName; 577 } 578 var lc = this.getLineAndColumn(funcName); 579 if (lc == null) { 580 return funcName; 581 } 582 // in source maps lines and columns are zero based 583 var lineNumber = lc.line - 1; 584 var column = lc.column - 1; 585 var entry = this.sourceMap.findEntry(lineNumber, column); 586 var sourceFile = entry[2]; 587 var sourceLine = entry[3] + 1; 588 var sourceColumn = entry[4] + 1; 589 590 return sourceFile + ':' + sourceLine + ':' + sourceColumn + ' -> ' + funcName; 591}; 592 593TickProcessor.prototype.printEntries = function( 594 profile, totalTicks, nonLibTicks, filterP, callback, printAllTicks) { 595 var that = this; 596 this.processProfile(profile, filterP, function (rec) { 597 if (rec.selfTime == 0) return; 598 callback(rec); 599 var funcName = that.formatFunctionName(rec.internalFuncName); 600 if(printAllTicks) { 601 that.printLine(funcName, rec.selfTime, totalTicks, nonLibTicks); 602 } 603 }); 604}; 605 606 607TickProcessor.prototype.printHeavyProfile = function(profile, opt_indent) { 608 var self = this; 609 var indent = opt_indent || 0; 610 var indentStr = padLeft('', indent); 611 this.processProfile(profile, function() { return true; }, function (rec) { 612 // Cut off too infrequent callers. 613 if (rec.parentTotalPercent < TickProcessor.CALL_PROFILE_CUTOFF_PCT) return; 614 var funcName = self.formatFunctionName(rec.internalFuncName); 615 print(' ' + padLeft(rec.totalTime, 5) + ' ' + 616 padLeft(rec.parentTotalPercent.toFixed(1), 5) + '% ' + 617 indentStr + funcName); 618 // Limit backtrace depth. 619 if (indent < 2 * self.callGraphSize_) { 620 self.printHeavyProfile(rec.children, indent + 2); 621 } 622 // Delimit top-level functions. 623 if (indent == 0) { 624 print(''); 625 } 626 }); 627}; 628 629 630function CppEntriesProvider() { 631}; 632 633 634CppEntriesProvider.prototype.parseVmSymbols = function( 635 libName, libStart, libEnd, libASLRSlide, processorFunc) { 636 this.loadSymbols(libName); 637 638 var prevEntry; 639 640 function addEntry(funcInfo) { 641 // Several functions can be mapped onto the same address. To avoid 642 // creating zero-sized entries, skip such duplicates. 643 // Also double-check that function belongs to the library address space. 644 if (prevEntry && !prevEntry.end && 645 prevEntry.start < funcInfo.start && 646 prevEntry.start >= libStart && funcInfo.start <= libEnd) { 647 processorFunc(prevEntry.name, prevEntry.start, funcInfo.start); 648 } 649 if (funcInfo.end && 650 (!prevEntry || prevEntry.start != funcInfo.start) && 651 funcInfo.start >= libStart && funcInfo.end <= libEnd) { 652 processorFunc(funcInfo.name, funcInfo.start, funcInfo.end); 653 } 654 prevEntry = funcInfo; 655 } 656 657 while (true) { 658 var funcInfo = this.parseNextLine(); 659 if (funcInfo === null) { 660 continue; 661 } else if (funcInfo === false) { 662 break; 663 } 664 if (funcInfo.start < libStart - libASLRSlide && 665 funcInfo.start < libEnd - libStart) { 666 funcInfo.start += libStart; 667 } else { 668 funcInfo.start += libASLRSlide; 669 } 670 if (funcInfo.size) { 671 funcInfo.end = funcInfo.start + funcInfo.size; 672 } 673 addEntry(funcInfo); 674 } 675 addEntry({name: '', start: libEnd}); 676}; 677 678 679CppEntriesProvider.prototype.loadSymbols = function(libName) { 680}; 681 682 683CppEntriesProvider.prototype.parseNextLine = function() { 684 return false; 685}; 686 687 688function UnixCppEntriesProvider(nmExec, targetRootFS) { 689 this.symbols = []; 690 this.parsePos = 0; 691 this.nmExec = nmExec; 692 this.targetRootFS = targetRootFS; 693 this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ([0-9a-fA-F]{8,16} )?[tTwW] (.*)$/; 694}; 695inherits(UnixCppEntriesProvider, CppEntriesProvider); 696 697 698UnixCppEntriesProvider.prototype.loadSymbols = function(libName) { 699 this.parsePos = 0; 700 libName = this.targetRootFS + libName; 701 try { 702 this.symbols = [ 703 os.system(this.nmExec, ['-C', '-n', '-S', libName], -1, -1), 704 os.system(this.nmExec, ['-C', '-n', '-S', '-D', libName], -1, -1) 705 ]; 706 } catch (e) { 707 // If the library cannot be found on this system let's not panic. 708 this.symbols = ['', '']; 709 } 710}; 711 712 713UnixCppEntriesProvider.prototype.parseNextLine = function() { 714 if (this.symbols.length == 0) { 715 return false; 716 } 717 var lineEndPos = this.symbols[0].indexOf('\n', this.parsePos); 718 if (lineEndPos == -1) { 719 this.symbols.shift(); 720 this.parsePos = 0; 721 return this.parseNextLine(); 722 } 723 724 var line = this.symbols[0].substring(this.parsePos, lineEndPos); 725 this.parsePos = lineEndPos + 1; 726 var fields = line.match(this.FUNC_RE); 727 var funcInfo = null; 728 if (fields) { 729 funcInfo = { name: fields[3], start: parseInt(fields[1], 16) }; 730 if (fields[2]) { 731 funcInfo.size = parseInt(fields[2], 16); 732 } 733 } 734 return funcInfo; 735}; 736 737 738function MacCppEntriesProvider(nmExec, targetRootFS) { 739 UnixCppEntriesProvider.call(this, nmExec, targetRootFS); 740 // Note an empty group. It is required, as UnixCppEntriesProvider expects 3 groups. 741 this.FUNC_RE = /^([0-9a-fA-F]{8,16})() (.*)$/; 742}; 743inherits(MacCppEntriesProvider, UnixCppEntriesProvider); 744 745 746MacCppEntriesProvider.prototype.loadSymbols = function(libName) { 747 this.parsePos = 0; 748 libName = this.targetRootFS + libName; 749 750 // It seems that in OS X `nm` thinks that `-f` is a format option, not a 751 // "flat" display option flag. 752 try { 753 this.symbols = [os.system(this.nmExec, ['-n', libName], -1, -1), '']; 754 } catch (e) { 755 // If the library cannot be found on this system let's not panic. 756 this.symbols = ''; 757 } 758}; 759 760 761function WindowsCppEntriesProvider(_ignored_nmExec, targetRootFS) { 762 this.targetRootFS = targetRootFS; 763 this.symbols = ''; 764 this.parsePos = 0; 765}; 766inherits(WindowsCppEntriesProvider, CppEntriesProvider); 767 768 769WindowsCppEntriesProvider.FILENAME_RE = /^(.*)\.([^.]+)$/; 770 771 772WindowsCppEntriesProvider.FUNC_RE = 773 /^\s+0001:[0-9a-fA-F]{8}\s+([_\?@$0-9a-zA-Z]+)\s+([0-9a-fA-F]{8}).*$/; 774 775 776WindowsCppEntriesProvider.IMAGE_BASE_RE = 777 /^\s+0000:00000000\s+___ImageBase\s+([0-9a-fA-F]{8}).*$/; 778 779 780// This is almost a constant on Windows. 781WindowsCppEntriesProvider.EXE_IMAGE_BASE = 0x00400000; 782 783 784WindowsCppEntriesProvider.prototype.loadSymbols = function(libName) { 785 libName = this.targetRootFS + libName; 786 var fileNameFields = libName.match(WindowsCppEntriesProvider.FILENAME_RE); 787 if (!fileNameFields) return; 788 var mapFileName = fileNameFields[1] + '.map'; 789 this.moduleType_ = fileNameFields[2].toLowerCase(); 790 try { 791 this.symbols = read(mapFileName); 792 } catch (e) { 793 // If .map file cannot be found let's not panic. 794 this.symbols = ''; 795 } 796}; 797 798 799WindowsCppEntriesProvider.prototype.parseNextLine = function() { 800 var lineEndPos = this.symbols.indexOf('\r\n', this.parsePos); 801 if (lineEndPos == -1) { 802 return false; 803 } 804 805 var line = this.symbols.substring(this.parsePos, lineEndPos); 806 this.parsePos = lineEndPos + 2; 807 808 // Image base entry is above all other symbols, so we can just 809 // terminate parsing. 810 var imageBaseFields = line.match(WindowsCppEntriesProvider.IMAGE_BASE_RE); 811 if (imageBaseFields) { 812 var imageBase = parseInt(imageBaseFields[1], 16); 813 if ((this.moduleType_ == 'exe') != 814 (imageBase == WindowsCppEntriesProvider.EXE_IMAGE_BASE)) { 815 return false; 816 } 817 } 818 819 var fields = line.match(WindowsCppEntriesProvider.FUNC_RE); 820 return fields ? 821 { name: this.unmangleName(fields[1]), start: parseInt(fields[2], 16) } : 822 null; 823}; 824 825 826/** 827 * Performs very simple unmangling of C++ names. 828 * 829 * Does not handle arguments and template arguments. The mangled names have 830 * the form: 831 * 832 * ?LookupInDescriptor@JSObject@internal@v8@@...arguments info... 833 */ 834WindowsCppEntriesProvider.prototype.unmangleName = function(name) { 835 // Empty or non-mangled name. 836 if (name.length < 1 || name.charAt(0) != '?') return name; 837 var nameEndPos = name.indexOf('@@'); 838 var components = name.substring(1, nameEndPos).split('@'); 839 components.reverse(); 840 return components.join('::'); 841}; 842 843 844class ArgumentsProcessor extends BaseArgumentsProcessor { 845 getArgsDispatch() { 846 let dispatch = { 847 '-j': ['stateFilter', TickProcessor.VmStates.JS, 848 'Show only ticks from JS VM state'], 849 '-g': ['stateFilter', TickProcessor.VmStates.GC, 850 'Show only ticks from GC VM state'], 851 '-p': ['stateFilter', TickProcessor.VmStates.PARSER, 852 'Show only ticks from PARSER VM state'], 853 '-b': ['stateFilter', TickProcessor.VmStates.BYTECODE_COMPILER, 854 'Show only ticks from BYTECODE_COMPILER VM state'], 855 '-c': ['stateFilter', TickProcessor.VmStates.COMPILER, 856 'Show only ticks from COMPILER VM state'], 857 '-o': ['stateFilter', TickProcessor.VmStates.OTHER, 858 'Show only ticks from OTHER VM state'], 859 '-e': ['stateFilter', TickProcessor.VmStates.EXTERNAL, 860 'Show only ticks from EXTERNAL VM state'], 861 '--filter-runtime-timer': ['runtimeTimerFilter', null, 862 'Show only ticks matching the given runtime timer scope'], 863 '--call-graph-size': ['callGraphSize', TickProcessor.CALL_GRAPH_SIZE, 864 'Set the call graph size'], 865 '--ignore-unknown': ['ignoreUnknown', true, 866 'Exclude ticks of unknown code entries from processing'], 867 '--separate-ic': ['separateIc', parseBool, 868 'Separate IC entries'], 869 '--separate-bytecodes': ['separateBytecodes', parseBool, 870 'Separate Bytecode entries'], 871 '--separate-builtins': ['separateBuiltins', parseBool, 872 'Separate Builtin entries'], 873 '--separate-stubs': ['separateStubs', parseBool, 874 'Separate Stub entries'], 875 '--unix': ['platform', 'unix', 876 'Specify that we are running on *nix platform'], 877 '--windows': ['platform', 'windows', 878 'Specify that we are running on Windows platform'], 879 '--mac': ['platform', 'mac', 880 'Specify that we are running on Mac OS X platform'], 881 '--nm': ['nm', 'nm', 882 'Specify the \'nm\' executable to use (e.g. --nm=/my_dir/nm)'], 883 '--target': ['targetRootFS', '', 884 'Specify the target root directory for cross environment'], 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, 890 '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'], 897 '--preprocess': ['preprocessJson', true, 898 'Preprocess for consumption with web interface'] 899 }; 900 dispatch['--js'] = dispatch['-j']; 901 dispatch['--gc'] = dispatch['-g']; 902 dispatch['--compiler'] = dispatch['-c']; 903 dispatch['--other'] = dispatch['-o']; 904 dispatch['--external'] = dispatch['-e']; 905 dispatch['--ptr'] = dispatch['--pairwise-timed-range']; 906 return dispatch; 907 } 908 909 getDefaultResults() { 910 return { 911 logFileName: 'v8.log', 912 platform: 'unix', 913 stateFilter: null, 914 callGraphSize: 5, 915 ignoreUnknown: false, 916 separateIc: true, 917 separateBytecodes: false, 918 separateBuiltins: true, 919 separateStubs: true, 920 preprocessJson: null, 921 targetRootFS: '', 922 nm: 'nm', 923 range: 'auto,auto', 924 distortion: 0, 925 timedRange: false, 926 pairwiseTimedRange: false, 927 onlySummary: false, 928 runtimeTimerFilter: null, 929 }; 930 } 931} 932