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