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