1 // Copyright 2009 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 29 function inherits(childCtor, parentCtor) { 30 childCtor.prototype.__proto__ = parentCtor.prototype; 31 }; 32 33 34 function V8Profile(separateIc) { 35 Profile.call(this); 36 if (!separateIc) { 37 this.skipThisFunction = function(name) { return V8Profile.IC_RE.test(name); }; 38 } 39 }; 40 inherits(V8Profile, Profile); 41 42 43 V8Profile.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 */ 50 function 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 */ 63 function 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 73 function SnapshotLogProcessor() { 74 LogReader.call(this, { 75 'code-creation': { 76 parsers: [null, 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 } 106 inherits(SnapshotLogProcessor, LogReader); 107 108 109 SnapshotLogProcessor.prototype.processCodeCreation = function( 110 type, start, size, name, maybe_func) { 111 if (maybe_func.length) { 112 var funcAddr = parseInt(maybe_func[0]); 113 var state = parseState(maybe_func[1]); 114 this.profile_.addFuncCode(type, name, start, size, funcAddr, state); 115 } else { 116 this.profile_.addCode(type, name, start, size); 117 } 118 }; 119 120 121 SnapshotLogProcessor.prototype.processCodeMove = function(from, to) { 122 this.profile_.moveCode(from, to); 123 }; 124 125 126 SnapshotLogProcessor.prototype.processCodeDelete = function(start) { 127 this.profile_.deleteCode(start); 128 }; 129 130 131 SnapshotLogProcessor.prototype.processSnapshotPosition = function(addr, pos) { 132 this.serializedEntries_[pos] = this.profile_.findEntry(addr); 133 }; 134 135 136 SnapshotLogProcessor.prototype.processLogFile = function(fileName) { 137 var contents = readFile(fileName); 138 this.processLogChunk(contents); 139 }; 140 141 142 SnapshotLogProcessor.prototype.getSerializedEntryName = function(pos) { 143 var entry = this.serializedEntries_[pos]; 144 return entry ? entry.getRawName() : null; 145 }; 146 147 148 function TickProcessor( 149 cppEntriesProvider, separateIc, ignoreUnknown, stateFilter, snapshotLogProcessor) { 150 LogReader.call(this, { 151 'shared-library': { parsers: [null, parseInt, parseInt], 152 processor: this.processSharedLibrary }, 153 'code-creation': { 154 parsers: [null, parseInt, parseInt, null, 'var-args'], 155 processor: this.processCodeCreation }, 156 'code-move': { parsers: [parseInt, parseInt], 157 processor: this.processCodeMove }, 158 'code-delete': { parsers: [parseInt], 159 processor: this.processCodeDelete }, 160 'sfi-move': { parsers: [parseInt, parseInt], 161 processor: this.processFunctionMove }, 162 'snapshot-pos': { parsers: [parseInt, parseInt], 163 processor: this.processSnapshotPosition }, 164 'tick': { 165 parsers: [parseInt, parseInt, parseInt, 166 parseInt, parseInt, 'var-args'], 167 processor: this.processTick }, 168 'heap-sample-begin': { parsers: [null, null, parseInt], 169 processor: this.processHeapSampleBegin }, 170 'heap-sample-end': { parsers: [null, null], 171 processor: this.processHeapSampleEnd }, 172 'heap-js-prod-item': { parsers: [null, 'var-args'], 173 processor: this.processJSProducer }, 174 // Ignored events. 175 'profiler': null, 176 'function-creation': null, 177 'function-move': null, 178 'function-delete': null, 179 'heap-sample-stats': null, 180 'heap-sample-item': null, 181 'heap-js-cons-item': null, 182 'heap-js-ret-item': null, 183 // Obsolete row types. 184 'code-allocate': null, 185 'begin-code-region': null, 186 'end-code-region': null }); 187 188 this.cppEntriesProvider_ = cppEntriesProvider; 189 this.ignoreUnknown_ = ignoreUnknown; 190 this.stateFilter_ = stateFilter; 191 this.snapshotLogProcessor_ = snapshotLogProcessor; 192 this.deserializedEntriesNames_ = []; 193 var ticks = this.ticks_ = 194 { total: 0, unaccounted: 0, excluded: 0, gc: 0 }; 195 196 V8Profile.prototype.handleUnknownCode = function( 197 operation, addr, opt_stackPos) { 198 var op = Profile.Operation; 199 switch (operation) { 200 case op.MOVE: 201 print('Code move event for unknown code: 0x' + addr.toString(16)); 202 break; 203 case op.DELETE: 204 print('Code delete event for unknown code: 0x' + addr.toString(16)); 205 break; 206 case op.TICK: 207 // Only unknown PCs (the first frame) are reported as unaccounted, 208 // otherwise tick balance will be corrupted (this behavior is compatible 209 // with the original tickprocessor.py script.) 210 if (opt_stackPos == 0) { 211 ticks.unaccounted++; 212 } 213 break; 214 } 215 }; 216 217 this.profile_ = new V8Profile(separateIc); 218 this.codeTypes_ = {}; 219 // Count each tick as a time unit. 220 this.viewBuilder_ = new ViewBuilder(1); 221 this.lastLogFileName_ = null; 222 223 this.generation_ = 1; 224 this.currentProducerProfile_ = null; 225 }; 226 inherits(TickProcessor, LogReader); 227 228 229 TickProcessor.VmStates = { 230 JS: 0, 231 GC: 1, 232 COMPILER: 2, 233 OTHER: 3, 234 EXTERNAL: 4 235 }; 236 237 238 TickProcessor.CodeTypes = { 239 CPP: 0, 240 SHARED_LIB: 1 241 }; 242 // Otherwise, this is JS-related code. We are not adding it to 243 // codeTypes_ map because there can be zillions of them. 244 245 246 TickProcessor.CALL_PROFILE_CUTOFF_PCT = 2.0; 247 248 249 /** 250 * @override 251 */ 252 TickProcessor.prototype.printError = function(str) { 253 print(str); 254 }; 255 256 257 TickProcessor.prototype.setCodeType = function(name, type) { 258 this.codeTypes_[name] = TickProcessor.CodeTypes[type]; 259 }; 260 261 262 TickProcessor.prototype.isSharedLibrary = function(name) { 263 return this.codeTypes_[name] == TickProcessor.CodeTypes.SHARED_LIB; 264 }; 265 266 267 TickProcessor.prototype.isCppCode = function(name) { 268 return this.codeTypes_[name] == TickProcessor.CodeTypes.CPP; 269 }; 270 271 272 TickProcessor.prototype.isJsCode = function(name) { 273 return !(name in this.codeTypes_); 274 }; 275 276 277 TickProcessor.prototype.processLogFile = function(fileName) { 278 this.lastLogFileName_ = fileName; 279 var line; 280 while (line = readline()) { 281 this.processLogLine(line); 282 } 283 }; 284 285 286 TickProcessor.prototype.processLogFileInTest = function(fileName) { 287 // Hack file name to avoid dealing with platform specifics. 288 this.lastLogFileName_ = 'v8.log'; 289 var contents = readFile(fileName); 290 this.processLogChunk(contents); 291 }; 292 293 294 TickProcessor.prototype.processSharedLibrary = function( 295 name, startAddr, endAddr) { 296 var entry = this.profile_.addLibrary(name, startAddr, endAddr); 297 this.setCodeType(entry.getName(), 'SHARED_LIB'); 298 299 var self = this; 300 var libFuncs = this.cppEntriesProvider_.parseVmSymbols( 301 name, startAddr, endAddr, function(fName, fStart, fEnd) { 302 self.profile_.addStaticCode(fName, fStart, fEnd); 303 self.setCodeType(fName, 'CPP'); 304 }); 305 }; 306 307 308 TickProcessor.prototype.processCodeCreation = function( 309 type, start, size, name, maybe_func) { 310 name = this.deserializedEntriesNames_[start] || name; 311 if (maybe_func.length) { 312 var funcAddr = parseInt(maybe_func[0]); 313 var state = parseState(maybe_func[1]); 314 this.profile_.addFuncCode(type, name, start, size, funcAddr, state); 315 } else { 316 this.profile_.addCode(type, name, start, size); 317 } 318 }; 319 320 321 TickProcessor.prototype.processCodeMove = function(from, to) { 322 this.profile_.moveCode(from, to); 323 }; 324 325 326 TickProcessor.prototype.processCodeDelete = function(start) { 327 this.profile_.deleteCode(start); 328 }; 329 330 331 TickProcessor.prototype.processFunctionMove = function(from, to) { 332 this.profile_.moveFunc(from, to); 333 }; 334 335 336 TickProcessor.prototype.processSnapshotPosition = function(addr, pos) { 337 if (this.snapshotLogProcessor_) { 338 this.deserializedEntriesNames_[addr] = 339 this.snapshotLogProcessor_.getSerializedEntryName(pos); 340 } 341 }; 342 343 344 TickProcessor.prototype.includeTick = function(vmState) { 345 return this.stateFilter_ == null || this.stateFilter_ == vmState; 346 }; 347 348 TickProcessor.prototype.processTick = function(pc, 349 sp, 350 is_external_callback, 351 tos_or_external_callback, 352 vmState, 353 stack) { 354 this.ticks_.total++; 355 if (vmState == TickProcessor.VmStates.GC) this.ticks_.gc++; 356 if (!this.includeTick(vmState)) { 357 this.ticks_.excluded++; 358 return; 359 } 360 if (is_external_callback) { 361 // Don't use PC when in external callback code, as it can point 362 // inside callback's code, and we will erroneously report 363 // that a callback calls itself. Instead we use tos_or_external_callback, 364 // as simply resetting PC will produce unaccounted ticks. 365 pc = tos_or_external_callback; 366 tos_or_external_callback = 0; 367 } else if (tos_or_external_callback) { 368 // Find out, if top of stack was pointing inside a JS function 369 // meaning that we have encountered a frameless invocation. 370 var funcEntry = this.profile_.findEntry(tos_or_external_callback); 371 if (!funcEntry || !funcEntry.isJSFunction || !funcEntry.isJSFunction()) { 372 tos_or_external_callback = 0; 373 } 374 } 375 376 this.profile_.recordTick(this.processStack(pc, tos_or_external_callback, stack)); 377 }; 378 379 380 TickProcessor.prototype.processHeapSampleBegin = function(space, state, ticks) { 381 if (space != 'Heap') return; 382 this.currentProducerProfile_ = new CallTree(); 383 }; 384 385 386 TickProcessor.prototype.processHeapSampleEnd = function(space, state) { 387 if (space != 'Heap' || !this.currentProducerProfile_) return; 388 389 print('Generation ' + this.generation_ + ':'); 390 var tree = this.currentProducerProfile_; 391 tree.computeTotalWeights(); 392 var producersView = this.viewBuilder_.buildView(tree); 393 // Sort by total time, desc, then by name, desc. 394 producersView.sort(function(rec1, rec2) { 395 return rec2.totalTime - rec1.totalTime || 396 (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); }); 397 this.printHeavyProfile(producersView.head.children); 398 399 this.currentProducerProfile_ = null; 400 this.generation_++; 401 }; 402 403 404 TickProcessor.prototype.processJSProducer = function(constructor, stack) { 405 if (!this.currentProducerProfile_) return; 406 if (stack.length == 0) return; 407 var first = stack.shift(); 408 var processedStack = 409 this.profile_.resolveAndFilterFuncs_(this.processStack(first, 0, stack)); 410 processedStack.unshift(constructor); 411 this.currentProducerProfile_.addPath(processedStack); 412 }; 413 414 415 TickProcessor.prototype.printStatistics = function() { 416 print('Statistical profiling result from ' + this.lastLogFileName_ + 417 ', (' + this.ticks_.total + 418 ' ticks, ' + this.ticks_.unaccounted + ' unaccounted, ' + 419 this.ticks_.excluded + ' excluded).'); 420 421 if (this.ticks_.total == 0) return; 422 423 // Print the unknown ticks percentage if they are not ignored. 424 if (!this.ignoreUnknown_ && this.ticks_.unaccounted > 0) { 425 this.printHeader('Unknown'); 426 this.printCounter(this.ticks_.unaccounted, this.ticks_.total); 427 } 428 429 var flatProfile = this.profile_.getFlatProfile(); 430 var flatView = this.viewBuilder_.buildView(flatProfile); 431 // Sort by self time, desc, then by name, desc. 432 flatView.sort(function(rec1, rec2) { 433 return rec2.selfTime - rec1.selfTime || 434 (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); }); 435 var totalTicks = this.ticks_.total; 436 if (this.ignoreUnknown_) { 437 totalTicks -= this.ticks_.unaccounted; 438 } 439 // Our total time contains all the ticks encountered, 440 // while profile only knows about the filtered ticks. 441 flatView.head.totalTime = totalTicks; 442 443 // Count library ticks 444 var flatViewNodes = flatView.head.children; 445 var self = this; 446 var libraryTicks = 0; 447 this.processProfile(flatViewNodes, 448 function(name) { return self.isSharedLibrary(name); }, 449 function(rec) { libraryTicks += rec.selfTime; }); 450 var nonLibraryTicks = totalTicks - libraryTicks; 451 452 this.printHeader('Shared libraries'); 453 this.printEntries(flatViewNodes, null, 454 function(name) { return self.isSharedLibrary(name); }); 455 456 this.printHeader('JavaScript'); 457 this.printEntries(flatViewNodes, nonLibraryTicks, 458 function(name) { return self.isJsCode(name); }); 459 460 this.printHeader('C++'); 461 this.printEntries(flatViewNodes, nonLibraryTicks, 462 function(name) { return self.isCppCode(name); }); 463 464 this.printHeader('GC'); 465 this.printCounter(this.ticks_.gc, totalTicks); 466 467 this.printHeavyProfHeader(); 468 var heavyProfile = this.profile_.getBottomUpProfile(); 469 var heavyView = this.viewBuilder_.buildView(heavyProfile); 470 // To show the same percentages as in the flat profile. 471 heavyView.head.totalTime = totalTicks; 472 // Sort by total time, desc, then by name, desc. 473 heavyView.sort(function(rec1, rec2) { 474 return rec2.totalTime - rec1.totalTime || 475 (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); }); 476 this.printHeavyProfile(heavyView.head.children); 477 }; 478 479 480 function padLeft(s, len) { 481 s = s.toString(); 482 if (s.length < len) { 483 var padLength = len - s.length; 484 if (!(padLength in padLeft)) { 485 padLeft[padLength] = new Array(padLength + 1).join(' '); 486 } 487 s = padLeft[padLength] + s; 488 } 489 return s; 490 }; 491 492 493 TickProcessor.prototype.printHeader = function(headerTitle) { 494 print('\n [' + headerTitle + ']:'); 495 print(' ticks total nonlib name'); 496 }; 497 498 499 TickProcessor.prototype.printHeavyProfHeader = function() { 500 print('\n [Bottom up (heavy) profile]:'); 501 print(' Note: percentage shows a share of a particular caller in the ' + 502 'total\n' + 503 ' amount of its parent calls.'); 504 print(' Callers occupying less than ' + 505 TickProcessor.CALL_PROFILE_CUTOFF_PCT.toFixed(1) + 506 '% are not shown.\n'); 507 print(' ticks parent name'); 508 }; 509 510 511 TickProcessor.prototype.printCounter = function(ticksCount, totalTicksCount) { 512 var pct = ticksCount * 100.0 / totalTicksCount; 513 print(' ' + padLeft(ticksCount, 5) + ' ' + padLeft(pct.toFixed(1), 5) + '%'); 514 }; 515 516 517 TickProcessor.prototype.processProfile = function( 518 profile, filterP, func) { 519 for (var i = 0, n = profile.length; i < n; ++i) { 520 var rec = profile[i]; 521 if (!filterP(rec.internalFuncName)) { 522 continue; 523 } 524 func(rec); 525 } 526 }; 527 528 529 TickProcessor.prototype.printEntries = function( 530 profile, nonLibTicks, filterP) { 531 this.processProfile(profile, filterP, function (rec) { 532 if (rec.selfTime == 0) return; 533 var nonLibPct = nonLibTicks != null ? 534 rec.selfTime * 100.0 / nonLibTicks : 0.0; 535 print(' ' + padLeft(rec.selfTime, 5) + ' ' + 536 padLeft(rec.selfPercent.toFixed(1), 5) + '% ' + 537 padLeft(nonLibPct.toFixed(1), 5) + '% ' + 538 rec.internalFuncName); 539 }); 540 }; 541 542 543 TickProcessor.prototype.printHeavyProfile = function(profile, opt_indent) { 544 var self = this; 545 var indent = opt_indent || 0; 546 var indentStr = padLeft('', indent); 547 this.processProfile(profile, function() { return true; }, function (rec) { 548 // Cut off too infrequent callers. 549 if (rec.parentTotalPercent < TickProcessor.CALL_PROFILE_CUTOFF_PCT) return; 550 print(' ' + padLeft(rec.totalTime, 5) + ' ' + 551 padLeft(rec.parentTotalPercent.toFixed(1), 5) + '% ' + 552 indentStr + rec.internalFuncName); 553 // Limit backtrace depth. 554 if (indent < 10) { 555 self.printHeavyProfile(rec.children, indent + 2); 556 } 557 // Delimit top-level functions. 558 if (indent == 0) { 559 print(''); 560 } 561 }); 562 }; 563 564 565 function CppEntriesProvider() { 566 }; 567 568 569 CppEntriesProvider.prototype.parseVmSymbols = function( 570 libName, libStart, libEnd, processorFunc) { 571 this.loadSymbols(libName); 572 573 var prevEntry; 574 575 function addEntry(funcInfo) { 576 // Several functions can be mapped onto the same address. To avoid 577 // creating zero-sized entries, skip such duplicates. 578 // Also double-check that function belongs to the library address space. 579 if (prevEntry && !prevEntry.end && 580 prevEntry.start < funcInfo.start && 581 prevEntry.start >= libStart && funcInfo.start <= libEnd) { 582 processorFunc(prevEntry.name, prevEntry.start, funcInfo.start); 583 } 584 if (funcInfo.end && 585 (!prevEntry || prevEntry.start != funcInfo.start) && 586 funcInfo.start >= libStart && funcInfo.end <= libEnd) { 587 processorFunc(funcInfo.name, funcInfo.start, funcInfo.end); 588 } 589 prevEntry = funcInfo; 590 } 591 592 while (true) { 593 var funcInfo = this.parseNextLine(); 594 if (funcInfo === null) { 595 continue; 596 } else if (funcInfo === false) { 597 break; 598 } 599 if (funcInfo.start < libStart && funcInfo.start < libEnd - libStart) { 600 funcInfo.start += libStart; 601 } 602 if (funcInfo.size) { 603 funcInfo.end = funcInfo.start + funcInfo.size; 604 } 605 addEntry(funcInfo); 606 } 607 addEntry({name: '', start: libEnd}); 608 }; 609 610 611 CppEntriesProvider.prototype.loadSymbols = function(libName) { 612 }; 613 614 615 CppEntriesProvider.prototype.parseNextLine = function() { 616 return false; 617 }; 618 619 620 function UnixCppEntriesProvider(nmExec) { 621 this.symbols = []; 622 this.parsePos = 0; 623 this.nmExec = nmExec; 624 this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ([0-9a-fA-F]{8,16} )?[tTwW] (.*)$/; 625 }; 626 inherits(UnixCppEntriesProvider, CppEntriesProvider); 627 628 629 UnixCppEntriesProvider.prototype.loadSymbols = function(libName) { 630 this.parsePos = 0; 631 try { 632 this.symbols = [ 633 os.system(this.nmExec, ['-C', '-n', '-S', libName], -1, -1), 634 os.system(this.nmExec, ['-C', '-n', '-S', '-D', libName], -1, -1) 635 ]; 636 } catch (e) { 637 // If the library cannot be found on this system let's not panic. 638 this.symbols = ['', '']; 639 } 640 }; 641 642 643 UnixCppEntriesProvider.prototype.parseNextLine = function() { 644 if (this.symbols.length == 0) { 645 return false; 646 } 647 var lineEndPos = this.symbols[0].indexOf('\n', this.parsePos); 648 if (lineEndPos == -1) { 649 this.symbols.shift(); 650 this.parsePos = 0; 651 return this.parseNextLine(); 652 } 653 654 var line = this.symbols[0].substring(this.parsePos, lineEndPos); 655 this.parsePos = lineEndPos + 1; 656 var fields = line.match(this.FUNC_RE); 657 var funcInfo = null; 658 if (fields) { 659 funcInfo = { name: fields[3], start: parseInt(fields[1], 16) }; 660 if (fields[2]) { 661 funcInfo.size = parseInt(fields[2], 16); 662 } 663 } 664 return funcInfo; 665 }; 666 667 668 function MacCppEntriesProvider(nmExec) { 669 UnixCppEntriesProvider.call(this, nmExec); 670 // Note an empty group. It is required, as UnixCppEntriesProvider expects 3 groups. 671 this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ()[iItT] (.*)$/; 672 }; 673 inherits(MacCppEntriesProvider, UnixCppEntriesProvider); 674 675 676 MacCppEntriesProvider.prototype.loadSymbols = function(libName) { 677 this.parsePos = 0; 678 try { 679 this.symbols = [os.system(this.nmExec, ['-n', '-f', libName], -1, -1), '']; 680 } catch (e) { 681 // If the library cannot be found on this system let's not panic. 682 this.symbols = ''; 683 } 684 }; 685 686 687 function WindowsCppEntriesProvider() { 688 this.symbols = ''; 689 this.parsePos = 0; 690 }; 691 inherits(WindowsCppEntriesProvider, CppEntriesProvider); 692 693 694 WindowsCppEntriesProvider.FILENAME_RE = /^(.*)\.([^.]+)$/; 695 696 697 WindowsCppEntriesProvider.FUNC_RE = 698 /^\s+0001:[0-9a-fA-F]{8}\s+([_\?@$0-9a-zA-Z]+)\s+([0-9a-fA-F]{8}).*$/; 699 700 701 WindowsCppEntriesProvider.IMAGE_BASE_RE = 702 /^\s+0000:00000000\s+___ImageBase\s+([0-9a-fA-F]{8}).*$/; 703 704 705 // This is almost a constant on Windows. 706 WindowsCppEntriesProvider.EXE_IMAGE_BASE = 0x00400000; 707 708 709 WindowsCppEntriesProvider.prototype.loadSymbols = function(libName) { 710 var fileNameFields = libName.match(WindowsCppEntriesProvider.FILENAME_RE); 711 if (!fileNameFields) return; 712 var mapFileName = fileNameFields[1] + '.map'; 713 this.moduleType_ = fileNameFields[2].toLowerCase(); 714 try { 715 this.symbols = read(mapFileName); 716 } catch (e) { 717 // If .map file cannot be found let's not panic. 718 this.symbols = ''; 719 } 720 }; 721 722 723 WindowsCppEntriesProvider.prototype.parseNextLine = function() { 724 var lineEndPos = this.symbols.indexOf('\r\n', this.parsePos); 725 if (lineEndPos == -1) { 726 return false; 727 } 728 729 var line = this.symbols.substring(this.parsePos, lineEndPos); 730 this.parsePos = lineEndPos + 2; 731 732 // Image base entry is above all other symbols, so we can just 733 // terminate parsing. 734 var imageBaseFields = line.match(WindowsCppEntriesProvider.IMAGE_BASE_RE); 735 if (imageBaseFields) { 736 var imageBase = parseInt(imageBaseFields[1], 16); 737 if ((this.moduleType_ == 'exe') != 738 (imageBase == WindowsCppEntriesProvider.EXE_IMAGE_BASE)) { 739 return false; 740 } 741 } 742 743 var fields = line.match(WindowsCppEntriesProvider.FUNC_RE); 744 return fields ? 745 { name: this.unmangleName(fields[1]), start: parseInt(fields[2], 16) } : 746 null; 747 }; 748 749 750 /** 751 * Performs very simple unmangling of C++ names. 752 * 753 * Does not handle arguments and template arguments. The mangled names have 754 * the form: 755 * 756 * ?LookupInDescriptor@JSObject@internal@v8@@...arguments info... 757 */ 758 WindowsCppEntriesProvider.prototype.unmangleName = function(name) { 759 // Empty or non-mangled name. 760 if (name.length < 1 || name.charAt(0) != '?') return name; 761 var nameEndPos = name.indexOf('@@'); 762 var components = name.substring(1, nameEndPos).split('@'); 763 components.reverse(); 764 return components.join('::'); 765 }; 766 767 768 function ArgumentsProcessor(args) { 769 this.args_ = args; 770 this.result_ = ArgumentsProcessor.DEFAULTS; 771 772 this.argsDispatch_ = { 773 '-j': ['stateFilter', TickProcessor.VmStates.JS, 774 'Show only ticks from JS VM state'], 775 '-g': ['stateFilter', TickProcessor.VmStates.GC, 776 'Show only ticks from GC VM state'], 777 '-c': ['stateFilter', TickProcessor.VmStates.COMPILER, 778 'Show only ticks from COMPILER VM state'], 779 '-o': ['stateFilter', TickProcessor.VmStates.OTHER, 780 'Show only ticks from OTHER VM state'], 781 '-e': ['stateFilter', TickProcessor.VmStates.EXTERNAL, 782 'Show only ticks from EXTERNAL VM state'], 783 '--ignore-unknown': ['ignoreUnknown', true, 784 'Exclude ticks of unknown code entries from processing'], 785 '--separate-ic': ['separateIc', true, 786 'Separate IC entries'], 787 '--unix': ['platform', 'unix', 788 'Specify that we are running on *nix platform'], 789 '--windows': ['platform', 'windows', 790 'Specify that we are running on Windows platform'], 791 '--mac': ['platform', 'mac', 792 'Specify that we are running on Mac OS X platform'], 793 '--nm': ['nm', 'nm', 794 'Specify the \'nm\' executable to use (e.g. --nm=/my_dir/nm)'], 795 '--snapshot-log': ['snapshotLogFileName', 'snapshot.log', 796 'Specify snapshot log file to use (e.g. --snapshot-log=snapshot.log)'] 797 }; 798 this.argsDispatch_['--js'] = this.argsDispatch_['-j']; 799 this.argsDispatch_['--gc'] = this.argsDispatch_['-g']; 800 this.argsDispatch_['--compiler'] = this.argsDispatch_['-c']; 801 this.argsDispatch_['--other'] = this.argsDispatch_['-o']; 802 this.argsDispatch_['--external'] = this.argsDispatch_['-e']; 803 }; 804 805 806 ArgumentsProcessor.DEFAULTS = { 807 logFileName: 'v8.log', 808 snapshotLogFileName: null, 809 platform: 'unix', 810 stateFilter: null, 811 ignoreUnknown: false, 812 separateIc: false, 813 nm: 'nm' 814 }; 815 816 817 ArgumentsProcessor.prototype.parse = function() { 818 while (this.args_.length) { 819 var arg = this.args_[0]; 820 if (arg.charAt(0) != '-') { 821 break; 822 } 823 this.args_.shift(); 824 var userValue = null; 825 var eqPos = arg.indexOf('='); 826 if (eqPos != -1) { 827 userValue = arg.substr(eqPos + 1); 828 arg = arg.substr(0, eqPos); 829 } 830 if (arg in this.argsDispatch_) { 831 var dispatch = this.argsDispatch_[arg]; 832 this.result_[dispatch[0]] = userValue == null ? dispatch[1] : userValue; 833 } else { 834 return false; 835 } 836 } 837 838 if (this.args_.length >= 1) { 839 this.result_.logFileName = this.args_.shift(); 840 } 841 return true; 842 }; 843 844 845 ArgumentsProcessor.prototype.result = function() { 846 return this.result_; 847 }; 848 849 850 ArgumentsProcessor.prototype.printUsageAndExit = function() { 851 852 function padRight(s, len) { 853 s = s.toString(); 854 if (s.length < len) { 855 s = s + (new Array(len - s.length + 1).join(' ')); 856 } 857 return s; 858 } 859 860 print('Cmdline args: [options] [log-file-name]\n' + 861 'Default log file name is "' + 862 ArgumentsProcessor.DEFAULTS.logFileName + '".\n'); 863 print('Options:'); 864 for (var arg in this.argsDispatch_) { 865 var synonims = [arg]; 866 var dispatch = this.argsDispatch_[arg]; 867 for (var synArg in this.argsDispatch_) { 868 if (arg !== synArg && dispatch === this.argsDispatch_[synArg]) { 869 synonims.push(synArg); 870 delete this.argsDispatch_[synArg]; 871 } 872 } 873 print(' ' + padRight(synonims.join(', '), 20) + dispatch[2]); 874 } 875 quit(2); 876 }; 877 878