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