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