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