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