OLD | NEW |
(Empty) | |
| 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 |
| 29 function Profile(separateIc) { |
| 30 devtools.profiler.Profile.call(this); |
| 31 if (!separateIc) { |
| 32 this.skipThisFunction = function(name) { return Profile.IC_RE.test(name); }; |
| 33 } |
| 34 }; |
| 35 Profile.prototype = devtools.profiler.Profile.prototype; |
| 36 |
| 37 |
| 38 Profile.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 */ |
| 45 function readFile(fileName) { |
| 46 try { |
| 47 return read(fileName); |
| 48 } catch (e) { |
| 49 print(fileName + ': ' + (e.message || e)); |
| 50 throw e; |
| 51 } |
| 52 } |
| 53 |
| 54 |
| 55 function TickProcessor( |
| 56 cppEntriesProvider, separateIc, ignoreUnknown, stateFilter) { |
| 57 this.cppEntriesProvider_ = cppEntriesProvider; |
| 58 this.ignoreUnknown_ = ignoreUnknown; |
| 59 this.stateFilter_ = stateFilter; |
| 60 var ticks = this.ticks_ = |
| 61 { total: 0, unaccounted: 0, excluded: 0, gc: 0 }; |
| 62 |
| 63 Profile.prototype.handleUnknownCode = function( |
| 64 operation, addr, opt_stackPos) { |
| 65 var op = devtools.profiler.Profile.Operation; |
| 66 switch (operation) { |
| 67 case op.MOVE: |
| 68 print('Code move event for unknown code: 0x' + addr.toString(16)); |
| 69 break; |
| 70 case op.DELETE: |
| 71 print('Code delete event for unknown code: 0x' + addr.toString(16)); |
| 72 break; |
| 73 case op.TICK: |
| 74 // Only unknown PCs (the first frame) are reported as unaccounted, |
| 75 // otherwise tick balance will be corrupted (this behavior is compatible |
| 76 // with the original tickprocessor.py script.) |
| 77 if (opt_stackPos == 0) { |
| 78 ticks.unaccounted++; |
| 79 } |
| 80 break; |
| 81 } |
| 82 }; |
| 83 |
| 84 this.profile_ = new Profile(separateIc); |
| 85 this.codeTypes_ = {}; |
| 86 // Count each tick as a time unit. |
| 87 this.viewBuilder_ = new devtools.profiler.ViewBuilder(1); |
| 88 this.lastLogFileName_ = null; |
| 89 }; |
| 90 |
| 91 |
| 92 TickProcessor.VmStates = { |
| 93 JS: 0, |
| 94 GC: 1, |
| 95 COMPILER: 2, |
| 96 OTHER: 3, |
| 97 EXTERNAL: 4 |
| 98 }; |
| 99 |
| 100 |
| 101 TickProcessor.CodeTypes = { |
| 102 JS: 0, |
| 103 CPP: 1, |
| 104 SHARED_LIB: 2 |
| 105 }; |
| 106 |
| 107 |
| 108 TickProcessor.RecordsDispatch = { |
| 109 'shared-library': { parsers: [null, parseInt, parseInt], |
| 110 processor: 'processSharedLibrary' }, |
| 111 'code-creation': { parsers: [null, parseInt, parseInt, null], |
| 112 processor: 'processCodeCreation' }, |
| 113 'code-move': { parsers: [parseInt, parseInt], |
| 114 processor: 'processCodeMove' }, |
| 115 'code-delete': { parsers: [parseInt], processor: 'processCodeDelete' }, |
| 116 'tick': { parsers: [parseInt, parseInt, parseInt, 'var-args'], |
| 117 processor: 'processTick' }, |
| 118 'profiler': null, |
| 119 // Obsolete row types. |
| 120 'code-allocate': null, |
| 121 'begin-code-region': null, |
| 122 'end-code-region': null |
| 123 }; |
| 124 |
| 125 |
| 126 TickProcessor.CALL_PROFILE_CUTOFF_PCT = 2.0; |
| 127 |
| 128 |
| 129 TickProcessor.prototype.setCodeType = function(name, type) { |
| 130 this.codeTypes_[name] = TickProcessor.CodeTypes[type]; |
| 131 }; |
| 132 |
| 133 |
| 134 TickProcessor.prototype.isSharedLibrary = function(name) { |
| 135 return this.codeTypes_[name] == TickProcessor.CodeTypes.SHARED_LIB; |
| 136 }; |
| 137 |
| 138 |
| 139 TickProcessor.prototype.isCppCode = function(name) { |
| 140 return this.codeTypes_[name] == TickProcessor.CodeTypes.CPP; |
| 141 }; |
| 142 |
| 143 |
| 144 TickProcessor.prototype.isJsCode = function(name) { |
| 145 return this.codeTypes_[name] == TickProcessor.CodeTypes.JS; |
| 146 }; |
| 147 |
| 148 |
| 149 TickProcessor.prototype.processLogFile = function(fileName) { |
| 150 this.lastLogFileName_ = fileName; |
| 151 var contents = readFile(fileName); |
| 152 this.processLog(contents.split('\n')); |
| 153 }; |
| 154 |
| 155 |
| 156 TickProcessor.prototype.processLog = function(lines) { |
| 157 var csvParser = new devtools.profiler.CsvParser(); |
| 158 try { |
| 159 for (var i = 0, n = lines.length; i < n; ++i) { |
| 160 var line = lines[i]; |
| 161 if (!line) { |
| 162 continue; |
| 163 } |
| 164 var fields = csvParser.parseLine(line); |
| 165 this.dispatchLogRow(fields); |
| 166 } |
| 167 } catch (e) { |
| 168 print('line ' + (i + 1) + ': ' + (e.message || e)); |
| 169 throw e; |
| 170 } |
| 171 }; |
| 172 |
| 173 |
| 174 TickProcessor.prototype.dispatchLogRow = function(fields) { |
| 175 // Obtain the dispatch. |
| 176 var command = fields[0]; |
| 177 if (!(command in TickProcessor.RecordsDispatch)) { |
| 178 throw new Error('unknown command: ' + command); |
| 179 } |
| 180 var dispatch = TickProcessor.RecordsDispatch[command]; |
| 181 |
| 182 if (dispatch === null) { |
| 183 return; |
| 184 } |
| 185 |
| 186 // Parse fields. |
| 187 var parsedFields = []; |
| 188 for (var i = 0; i < dispatch.parsers.length; ++i) { |
| 189 var parser = dispatch.parsers[i]; |
| 190 if (parser === null) { |
| 191 parsedFields.push(fields[1 + i]); |
| 192 } else if (typeof parser == 'function') { |
| 193 parsedFields.push(parser(fields[1 + i])); |
| 194 } else { |
| 195 // var-args |
| 196 parsedFields.push(fields.slice(1 + i)); |
| 197 break; |
| 198 } |
| 199 } |
| 200 |
| 201 // Run the processor. |
| 202 this[dispatch.processor].apply(this, parsedFields); |
| 203 }; |
| 204 |
| 205 |
| 206 TickProcessor.prototype.processSharedLibrary = function( |
| 207 name, startAddr, endAddr) { |
| 208 var entry = this.profile_.addStaticCode(name, startAddr, endAddr); |
| 209 this.setCodeType(entry.getName(), 'SHARED_LIB'); |
| 210 |
| 211 var self = this; |
| 212 var libFuncs = this.cppEntriesProvider_.parseVmSymbols( |
| 213 name, startAddr, endAddr, function(fName, fStart, fEnd) { |
| 214 self.profile_.addStaticCode(fName, fStart, fEnd); |
| 215 self.setCodeType(fName, 'CPP'); |
| 216 }); |
| 217 }; |
| 218 |
| 219 |
| 220 TickProcessor.prototype.processCodeCreation = function( |
| 221 type, start, size, name) { |
| 222 var entry = this.profile_.addCode(type, name, start, size); |
| 223 this.setCodeType(entry.getName(), 'JS'); |
| 224 }; |
| 225 |
| 226 |
| 227 TickProcessor.prototype.processCodeMove = function(from, to) { |
| 228 this.profile_.moveCode(from, to); |
| 229 }; |
| 230 |
| 231 |
| 232 TickProcessor.prototype.processCodeDelete = function(start) { |
| 233 this.profile_.deleteCode(start); |
| 234 }; |
| 235 |
| 236 |
| 237 TickProcessor.prototype.includeTick = function(vmState) { |
| 238 return this.stateFilter_ == null || this.stateFilter_ == vmState; |
| 239 }; |
| 240 |
| 241 |
| 242 TickProcessor.prototype.processTick = function(pc, sp, vmState, stack) { |
| 243 this.ticks_.total++; |
| 244 if (vmState == TickProcessor.VmStates.GC) this.ticks_.gc++; |
| 245 if (!this.includeTick(vmState)) { |
| 246 this.ticks_.excluded++; |
| 247 return; |
| 248 } |
| 249 |
| 250 var fullStack = [pc]; |
| 251 for (var i = 0, n = stack.length; i < n; ++i) { |
| 252 var frame = stack[i]; |
| 253 // Leave only numbers starting with 0x. Filter possible 'overflow' string. |
| 254 if (frame.charAt(0) == '0') { |
| 255 fullStack.push(parseInt(frame, 16)); |
| 256 } |
| 257 } |
| 258 this.profile_.recordTick(fullStack); |
| 259 }; |
| 260 |
| 261 |
| 262 TickProcessor.prototype.printStatistics = function() { |
| 263 print('Statistical profiling result from ' + this.lastLogFileName_ + |
| 264 ', (' + this.ticks_.total + |
| 265 ' ticks, ' + this.ticks_.unaccounted + ' unaccounted, ' + |
| 266 this.ticks_.excluded + ' excluded).'); |
| 267 |
| 268 if (this.ticks_.total == 0) return; |
| 269 |
| 270 // Print the unknown ticks percentage if they are not ignored. |
| 271 if (!this.ignoreUnknown_ && this.ticks_.unaccounted > 0) { |
| 272 this.printHeader('Unknown'); |
| 273 this.printCounter(this.ticks_.unaccounted, this.ticks_.total); |
| 274 } |
| 275 |
| 276 var flatProfile = this.profile_.getFlatProfile(); |
| 277 var flatView = this.viewBuilder_.buildView(flatProfile); |
| 278 // Sort by self time, desc, then by name, desc. |
| 279 flatView.sort(function(rec1, rec2) { |
| 280 return rec2.selfTime - rec1.selfTime || |
| 281 (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); }); |
| 282 var totalTicks = this.ticks_.total; |
| 283 if (this.ignoreUnknown_) { |
| 284 totalTicks -= this.ticks_.unaccounted; |
| 285 } |
| 286 // Our total time contains all the ticks encountered, |
| 287 // while profile only knows about the filtered ticks. |
| 288 flatView.head.totalTime = totalTicks; |
| 289 |
| 290 // Count library ticks |
| 291 var flatViewNodes = flatView.head.children; |
| 292 var self = this; |
| 293 var libraryTicks = 0; |
| 294 this.processProfile(flatViewNodes, |
| 295 function(name) { return self.isSharedLibrary(name); }, |
| 296 function(rec) { libraryTicks += rec.selfTime; }); |
| 297 var nonLibraryTicks = totalTicks - libraryTicks; |
| 298 |
| 299 this.printHeader('Shared libraries'); |
| 300 this.printEntries(flatViewNodes, null, |
| 301 function(name) { return self.isSharedLibrary(name); }); |
| 302 |
| 303 this.printHeader('JavaScript'); |
| 304 this.printEntries(flatViewNodes, nonLibraryTicks, |
| 305 function(name) { return self.isJsCode(name); }); |
| 306 |
| 307 this.printHeader('C++'); |
| 308 this.printEntries(flatViewNodes, nonLibraryTicks, |
| 309 function(name) { return self.isCppCode(name); }); |
| 310 |
| 311 this.printHeader('GC'); |
| 312 this.printCounter(this.ticks_.gc, totalTicks); |
| 313 |
| 314 this.printHeavyProfHeader(); |
| 315 var heavyProfile = this.profile_.getBottomUpProfile(); |
| 316 var heavyView = this.viewBuilder_.buildView(heavyProfile); |
| 317 // To show the same percentages as in the flat profile. |
| 318 heavyView.head.totalTime = totalTicks; |
| 319 // Sort by total time, desc, then by name, desc. |
| 320 heavyView.sort(function(rec1, rec2) { |
| 321 return rec2.totalTime - rec1.totalTime || |
| 322 (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); }); |
| 323 this.printHeavyProfile(heavyView.head.children); |
| 324 }; |
| 325 |
| 326 |
| 327 function padLeft(s, len) { |
| 328 s = s.toString(); |
| 329 if (s.length < len) { |
| 330 s = (new Array(len - s.length + 1).join(' ')) + s; |
| 331 } |
| 332 return s; |
| 333 }; |
| 334 |
| 335 |
| 336 TickProcessor.prototype.printHeader = function(headerTitle) { |
| 337 print('\n [' + headerTitle + ']:'); |
| 338 print(' ticks total nonlib name'); |
| 339 }; |
| 340 |
| 341 |
| 342 TickProcessor.prototype.printHeavyProfHeader = function() { |
| 343 print('\n [Bottom up (heavy) profile]:'); |
| 344 print(' Note: percentage shows a share of a particular caller in the ' + |
| 345 'total\n' + |
| 346 ' amount of its parent calls.'); |
| 347 print(' Callers occupying less than ' + |
| 348 TickProcessor.CALL_PROFILE_CUTOFF_PCT.toFixed(1) + |
| 349 '% are not shown.\n'); |
| 350 print(' ticks parent name'); |
| 351 }; |
| 352 |
| 353 |
| 354 TickProcessor.prototype.printCounter = function(ticksCount, totalTicksCount) { |
| 355 var pct = ticksCount * 100.0 / totalTicksCount; |
| 356 print(' ' + padLeft(ticksCount, 5) + ' ' + padLeft(pct.toFixed(1), 5) + '%')
; |
| 357 }; |
| 358 |
| 359 |
| 360 TickProcessor.prototype.processProfile = function( |
| 361 profile, filterP, func) { |
| 362 for (var i = 0, n = profile.length; i < n; ++i) { |
| 363 var rec = profile[i]; |
| 364 // An empty record corresponds to a tree root. |
| 365 if (!rec.internalFuncName || !filterP(rec.internalFuncName)) { |
| 366 continue; |
| 367 } |
| 368 func(rec); |
| 369 } |
| 370 }; |
| 371 |
| 372 |
| 373 TickProcessor.prototype.printEntries = function( |
| 374 profile, nonLibTicks, filterP) { |
| 375 this.processProfile(profile, filterP, function (rec) { |
| 376 if (rec.selfTime == 0) return; |
| 377 var nonLibPct = nonLibTicks != null ? |
| 378 rec.selfTime * 100.0 / nonLibTicks : 0.0; |
| 379 print(' ' + padLeft(rec.selfTime, 5) + ' ' + |
| 380 padLeft(rec.selfPercent.toFixed(1), 5) + '% ' + |
| 381 padLeft(nonLibPct.toFixed(1), 5) + '% ' + |
| 382 rec.internalFuncName); |
| 383 }); |
| 384 }; |
| 385 |
| 386 |
| 387 TickProcessor.prototype.printHeavyProfile = function(profile, opt_indent) { |
| 388 var self = this; |
| 389 var indent = opt_indent || 0; |
| 390 var indentStr = padLeft('', indent); |
| 391 this.processProfile(profile, function() { return true; }, function (rec) { |
| 392 // Cut off too infrequent callers. |
| 393 if (rec.parentTotalPercent < TickProcessor.CALL_PROFILE_CUTOFF_PCT) return; |
| 394 print(' ' + padLeft(rec.totalTime, 5) + ' ' + |
| 395 padLeft(rec.parentTotalPercent.toFixed(1), 5) + '% ' + |
| 396 indentStr + rec.internalFuncName); |
| 397 // Limit backtrace depth. |
| 398 if (indent < 10) { |
| 399 self.printHeavyProfile(rec.children, indent + 2); |
| 400 } |
| 401 // Delimit top-level functions. |
| 402 if (indent == 0) { |
| 403 print(''); |
| 404 } |
| 405 }); |
| 406 }; |
| 407 |
| 408 |
| 409 function CppEntriesProvider() { |
| 410 }; |
| 411 |
| 412 |
| 413 CppEntriesProvider.prototype.parseVmSymbols = function( |
| 414 libName, libStart, libEnd, processorFunc) { |
| 415 var syms = this.loadSymbols(libName); |
| 416 if (syms.length == 0) return; |
| 417 |
| 418 var prevEntry; |
| 419 |
| 420 function addPrevEntry(end) { |
| 421 // Several functions can be mapped onto the same address. To avoid |
| 422 // creating zero-sized entries, skip such duplicates. |
| 423 if (prevEntry && prevEntry.start != end) { |
| 424 processorFunc(prevEntry.name, prevEntry.start, end); |
| 425 } |
| 426 } |
| 427 |
| 428 for (var i = 0, n = syms.length; i < n; ++i) { |
| 429 var line = syms[i]; |
| 430 var funcInfo = this.parseLine(line); |
| 431 if (!funcInfo) { |
| 432 continue; |
| 433 } |
| 434 if (funcInfo.start < libStart && funcInfo.start < libEnd - libStart) { |
| 435 funcInfo.start += libStart; |
| 436 } |
| 437 addPrevEntry(funcInfo.start); |
| 438 prevEntry = funcInfo; |
| 439 } |
| 440 addPrevEntry(libEnd); |
| 441 }; |
| 442 |
| 443 |
| 444 CppEntriesProvider.prototype.loadSymbols = function(libName) { |
| 445 return []; |
| 446 }; |
| 447 |
| 448 |
| 449 CppEntriesProvider.prototype.parseLine = function(line) { |
| 450 return { name: '', start: 0 }; |
| 451 }; |
| 452 |
| 453 |
| 454 function inherits(childCtor, parentCtor) { |
| 455 function tempCtor() {}; |
| 456 tempCtor.prototype = parentCtor.prototype; |
| 457 childCtor.prototype = new tempCtor(); |
| 458 }; |
| 459 |
| 460 |
| 461 function UnixCppEntriesProvider() { |
| 462 }; |
| 463 inherits(UnixCppEntriesProvider, CppEntriesProvider); |
| 464 |
| 465 |
| 466 UnixCppEntriesProvider.FUNC_RE = /^([0-9a-fA-F]{8}) . (.*)$/; |
| 467 |
| 468 |
| 469 UnixCppEntriesProvider.prototype.loadSymbols = function(libName) { |
| 470 var normalSyms = os.system('nm', ['-C', '-n', libName], -1, -1); |
| 471 var dynaSyms = os.system('nm', ['-C', '-n', '-D', libName], -1, -1); |
| 472 var syms = (normalSyms + dynaSyms).split('\n'); |
| 473 return syms; |
| 474 }; |
| 475 |
| 476 |
| 477 UnixCppEntriesProvider.prototype.parseLine = function(line) { |
| 478 var fields = line.match(UnixCppEntriesProvider.FUNC_RE); |
| 479 return fields ? { name: fields[2], start: parseInt(fields[1], 16) } : null; |
| 480 }; |
| 481 |
| 482 |
| 483 function WindowsCppEntriesProvider() { |
| 484 }; |
| 485 inherits(WindowsCppEntriesProvider, CppEntriesProvider); |
| 486 |
| 487 |
| 488 WindowsCppEntriesProvider.FILENAME_RE = /^(.*)\.exe$/; |
| 489 |
| 490 |
| 491 WindowsCppEntriesProvider.FUNC_RE = |
| 492 /^ 0001:[0-9a-fA-F]{8}\s+([_\?@$0-9a-zA-Z]+)\s+([0-9a-fA-F]{8}).*$/; |
| 493 |
| 494 |
| 495 WindowsCppEntriesProvider.prototype.loadSymbols = function(libName) { |
| 496 var fileNameFields = libName.match(WindowsCppEntriesProvider.FILENAME_RE); |
| 497 // Only try to load symbols for the .exe file. |
| 498 if (!fileNameFields) return []; |
| 499 var mapFileName = fileNameFields[1] + '.map'; |
| 500 return readFile(mapFileName).split('\r\n'); |
| 501 }; |
| 502 |
| 503 |
| 504 WindowsCppEntriesProvider.prototype.parseLine = function(line) { |
| 505 var fields = line.match(WindowsCppEntriesProvider.FUNC_RE); |
| 506 return fields ? |
| 507 { name: this.unmangleName(fields[1]), start: parseInt(fields[2], 16) } : |
| 508 null; |
| 509 }; |
| 510 |
| 511 |
| 512 /** |
| 513 * Performs very simple unmangling of C++ names. |
| 514 * |
| 515 * Does not handle arguments and template arguments. The mangled names have |
| 516 * the form: |
| 517 * |
| 518 * ?LookupInDescriptor@JSObject@internal@v8@@...arguments info... |
| 519 */ |
| 520 WindowsCppEntriesProvider.prototype.unmangleName = function(name) { |
| 521 // Empty or non-mangled name. |
| 522 if (name.length < 1 || name.charAt(0) != '?') return name; |
| 523 var nameEndPos = name.indexOf('@@'); |
| 524 var components = name.substring(1, nameEndPos).split('@'); |
| 525 components.reverse(); |
| 526 return components.join('::'); |
| 527 }; |
| 528 |
| 529 |
| 530 function padRight(s, len) { |
| 531 s = s.toString(); |
| 532 if (s.length < len) { |
| 533 s = s + (new Array(len - s.length + 1).join(' ')); |
| 534 } |
| 535 return s; |
| 536 }; |
| 537 |
| 538 |
| 539 function processArguments(args) { |
| 540 var result = { |
| 541 logFileName: 'v8.log', |
| 542 platform: 'unix', |
| 543 stateFilter: null, |
| 544 ignoreUnknown: false, |
| 545 separateIc: false |
| 546 }; |
| 547 var argsDispatch = { |
| 548 '-j': ['stateFilter', TickProcessor.VmStates.JS, |
| 549 'Show only ticks from JS VM state'], |
| 550 '-g': ['stateFilter', TickProcessor.VmStates.GC, |
| 551 'Show only ticks from GC VM state'], |
| 552 '-c': ['stateFilter', TickProcessor.VmStates.COMPILER, |
| 553 'Show only ticks from COMPILER VM state'], |
| 554 '-o': ['stateFilter', TickProcessor.VmStates.OTHER, |
| 555 'Show only ticks from OTHER VM state'], |
| 556 '-e': ['stateFilter', TickProcessor.VmStates.EXTERNAL, |
| 557 'Show only ticks from EXTERNAL VM state'], |
| 558 '--ignore-unknown': ['ignoreUnknown', true, |
| 559 'Exclude ticks of unknown code entries from processing'], |
| 560 '--separate-ic': ['separateIc', true, |
| 561 'Separate IC entries'], |
| 562 '--unix': ['platform', 'unix', |
| 563 'Specify that we are running on *nix platform'], |
| 564 '--windows': ['platform', 'windows', |
| 565 'Specify that we are running on Windows platform'] |
| 566 }; |
| 567 argsDispatch['--js'] = argsDispatch['-j']; |
| 568 argsDispatch['--gc'] = argsDispatch['-g']; |
| 569 argsDispatch['--compiler'] = argsDispatch['-c']; |
| 570 argsDispatch['--other'] = argsDispatch['-o']; |
| 571 argsDispatch['--external'] = argsDispatch['-e']; |
| 572 |
| 573 function printUsageAndExit() { |
| 574 print('Cmdline args: [options] [log-file-name]\n' + |
| 575 'Default log file name is "v8.log".\n'); |
| 576 print('Options:'); |
| 577 for (var arg in argsDispatch) { |
| 578 var synonims = [arg]; |
| 579 var dispatch = argsDispatch[arg]; |
| 580 for (var synArg in argsDispatch) { |
| 581 if (arg !== synArg && dispatch === argsDispatch[synArg]) { |
| 582 synonims.push(synArg); |
| 583 delete argsDispatch[synArg]; |
| 584 } |
| 585 } |
| 586 print(' ' + padRight(synonims.join(', '), 20) + dispatch[2]); |
| 587 } |
| 588 quit(2); |
| 589 } |
| 590 |
| 591 while (args.length) { |
| 592 var arg = args[0]; |
| 593 if (arg.charAt(0) != '-') { |
| 594 break; |
| 595 } |
| 596 args.shift(); |
| 597 if (arg in argsDispatch) { |
| 598 var dispatch = argsDispatch[arg]; |
| 599 result[dispatch[0]] = dispatch[1]; |
| 600 } else { |
| 601 printUsageAndExit(); |
| 602 } |
| 603 } |
| 604 |
| 605 if (args.length >= 1) { |
| 606 result.logFileName = args.shift(); |
| 607 } |
| 608 return result; |
| 609 }; |
| 610 |
| 611 |
| 612 var params = processArguments(arguments); |
| 613 var tickProcessor = new TickProcessor( |
| 614 params.platform == 'unix' ? new UnixCppEntriesProvider() : |
| 615 new WindowsCppEntriesProvider(), |
| 616 params.separateIc, |
| 617 params.ignoreUnknown, |
| 618 params.stateFilter); |
| 619 tickProcessor.processLogFile(params.logFileName); |
| 620 tickProcessor.printStatistics(); |
OLD | NEW |