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