123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939 |
- #!/usr/bin/env ruby
- # Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
- #
- # Redistribution and use in source and binary forms, with or without
- # modification, are permitted provided that the following conditions
- # are met:
- # 1. Redistributions of source code must retain the above copyright
- # notice, this list of conditions and the following disclaimer.
- # 2. Redistributions in binary form must reproduce the above copyright
- # notice, this list of conditions and the following disclaimer in the
- # documentation and/or other materials provided with the distribution.
- #
- # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
- # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- # THE POSSIBILITY OF SUCH DAMAGE.
- require 'rubygems'
- require 'readline'
- begin
- require 'json'
- require 'highline'
- rescue LoadError
- $stderr.puts "Error: some required gems are not installed!"
- $stderr.puts
- $stderr.puts "Try running:"
- $stderr.puts
- $stderr.puts "sudo gem install json"
- $stderr.puts "sudo gem install highline"
- exit 1
- end
- class Bytecode
- attr_accessor :bytecodes, :bytecodeIndex, :opcode, :description, :topCounts, :bottomCounts, :machineInlinees, :osrExits
-
- def initialize(bytecodes, bytecodeIndex, opcode, description)
- @bytecodes = bytecodes
- @bytecodeIndex = bytecodeIndex
- @opcode = opcode
- @description = description
- @topCounts = [] # "source" counts
- @bottomCounts = {} # "machine" counts, maps compilations to counts
- @machineInlinees = {} # maps my compilation to a set of inlinees
- @osrExits = []
- end
-
- def shouldHaveCounts?
- @opcode != "op_call_put_result"
- end
-
- def addTopCount(count)
- @topCounts << count
- end
-
- def addBottomCountForCompilation(count, compilation)
- @bottomCounts[compilation] = [] unless @bottomCounts[compilation]
- @bottomCounts[compilation] << count
- end
-
- def addMachineInlinee(compilation, inlinee)
- @machineInlinees[compilation] = {} unless @machineInlinees[compilation]
- @machineInlinees[compilation][inlinee] = true
- end
-
- def totalTopExecutionCount
- sum = 0
- @topCounts.each {
- | value |
- sum += value.count
- }
- sum
- end
-
- def topExecutionCount(engine)
- sum = 0
- @topCounts.each {
- | value |
- if value.engine == engine
- sum += value.count
- end
- }
- sum
- end
-
- def totalBottomExecutionCount
- sum = 0
- @bottomCounts.each_value {
- | counts |
- max = 0
- counts.each {
- | value |
- max = [max, value.count].max
- }
- sum += max
- }
- sum
- end
-
- def bottomExecutionCount(engine)
- sum = 0
- @bottomCounts.each_pair {
- | compilation, counts |
- if compilation.engine == engine
- max = 0
- counts.each {
- | value |
- max = [max, value.count].max
- }
- sum += max
- end
- }
- sum
- end
-
- def totalExitCount
- sum = 0
- @osrExits.each {
- | exit |
- sum += exit.count
- }
- sum
- end
- end
- class Bytecodes
- attr_accessor :codeHash, :inferredName, :source, :instructionCount, :machineInlineSites, :compilations
-
- def initialize(json)
- @codeHash = json["hash"].to_s
- @inferredName = json["inferredName"].to_s
- @source = json["sourceCode"].to_s
- @instructionCount = json["instructionCount"].to_i
- @bytecode = {}
- json["bytecode"].each {
- | subJson |
- index = subJson["bytecodeIndex"].to_i
- @bytecode[index] = Bytecode.new(self, index, subJson["opcode"].to_s, subJson["description"].to_s)
- }
- @machineInlineSites = {} # maps compilation to a set of origins
- @compilations = []
- end
-
- def name(limit)
- if to_s.size > limit
- "\##{@codeHash}"
- else
- to_s
- end
- end
-
- def to_s
- "#{@inferredName}\##{@codeHash}"
- end
-
- def matches(pattern)
- if pattern =~ /^#/
- $~.post_match == @codeHash
- elsif pattern =~ /#/
- pattern == to_s
- else
- pattern == @inferredName or pattern == @codeHash
- end
- end
-
- def each
- @bytecode.values.sort{|a, b| a.bytecodeIndex <=> b.bytecodeIndex}.each {
- | value |
- yield value
- }
- end
-
- def bytecode(bytecodeIndex)
- @bytecode[bytecodeIndex]
- end
-
- def addMachineInlineSite(compilation, origin)
- @machineInlineSites[compilation] = {} unless @machineInlineSites[compilation]
- @machineInlineSites[compilation][origin] = true
- end
-
- def totalMachineInlineSites
- sum = 0
- @machineInlineSites.each_value {
- | set |
- sum += set.size
- }
- sum
- end
-
- def sourceMachineInlineSites
- set = {}
- @machineInlineSites.each_value {
- | mySet |
- set.merge!(mySet)
- }
- set.size
- end
-
- def totalMaxTopExecutionCount
- max = 0
- @bytecode.each_value {
- | bytecode |
- max = [max, bytecode.totalTopExecutionCount].max
- }
- max
- end
-
- def maxTopExecutionCount(engine)
- max = 0
- @bytecode.each_value {
- | bytecode |
- max = [max, bytecode.topExecutionCount(engine)].max
- }
- max
- end
-
- def totalMaxBottomExecutionCount
- max = 0
- @bytecode.each_value {
- | bytecode |
- max = [max, bytecode.totalBottomExecutionCount].max
- }
- max
- end
-
- def maxBottomExecutionCount(engine)
- max = 0
- @bytecode.each_value {
- | bytecode |
- max = [max, bytecode.bottomExecutionCount(engine)].max
- }
- max
- end
-
- def totalExitCount
- sum = 0
- each {
- | bytecode |
- sum += bytecode.totalExitCount
- }
- sum
- end
- end
- class ProfiledBytecode
- attr_reader :bytecodeIndex, :description
-
- def initialize(json)
- @bytecodeIndex = json["bytecodeIndex"].to_i
- @description = json["description"].to_s
- end
- end
- class ProfiledBytecodes
- attr_reader :header, :bytecodes
-
- def initialize(json)
- @header = json["header"]
- @bytecodes = $bytecodes[json["bytecodesID"].to_i]
- @sequence = json["bytecode"].map {
- | subJson |
- ProfiledBytecode.new(subJson)
- }
- end
-
- def each
- @sequence.each {
- | description |
- yield description
- }
- end
- end
- def originStackFromJSON(json)
- json.map {
- | subJson |
- $bytecodes[subJson["bytecodesID"].to_i].bytecode(subJson["bytecodeIndex"].to_i)
- }
- end
- class CompiledBytecode
- attr_accessor :origin, :description
-
- def initialize(json)
- @origin = originStackFromJSON(json["origin"])
- @description = json["description"].to_s
- end
- end
- class ExecutionCounter
- attr_accessor :origin, :engine, :count
-
- def initialize(origin, engine, count)
- @origin = origin
- @engine = engine
- @count = count
- end
- end
- class OSRExit
- attr_reader :compilation, :origin, :codeAddresses, :exitKind, :isWatchpoint, :count
-
- def initialize(compilation, origin, codeAddresses, exitKind, isWatchpoint, count)
- @compilation = compilation
- @origin = origin
- @codeAddresses = codeAddresses
- @exitKind = exitKind
- @isWatchpoint = isWatchpoint
- @count = count
- end
-
- def dumpForDisplay(prefix)
- puts(prefix + "EXIT: due to #{@exitKind}, #{@count} times")
- end
- end
- class Compilation
- attr_accessor :bytecode, :engine, :descriptions, :counters, :compilationIndex
- attr_accessor :osrExits, :profiledBytecodes, :numInlinedGetByIds, :numInlinedPutByIds
- attr_accessor :numInlinedCalls
-
- def initialize(json)
- @bytecode = $bytecodes[json["bytecodesID"].to_i]
- @bytecode.compilations << self
- @compilationIndex = @bytecode.compilations.size
- @engine = json["compilationKind"]
- @descriptions = json["descriptions"].map {
- | subJson |
- CompiledBytecode.new(subJson)
- }
- @descriptions.each {
- | description |
- next if description.origin.empty?
- description.origin[1..-1].each_with_index {
- | inlinee, index |
- description.origin[0].addMachineInlinee(self, inlinee.bytecodes)
- inlinee.bytecodes.addMachineInlineSite(self, description.origin[0...index])
- }
- }
- @counters = {}
- json["counters"].each {
- | subJson |
- origin = originStackFromJSON(subJson["origin"])
- counter = ExecutionCounter.new(origin, @engine, subJson["executionCount"].to_i)
- @counters[origin] = counter
- origin[-1].addTopCount(counter)
- origin[0].addBottomCountForCompilation(counter, self)
- }
- @osrExits = {}
- json["osrExits"].each {
- | subJson |
- osrExit = OSRExit.new(self, originStackFromJSON(subJson["origin"]),
- json["osrExitSites"][subJson["id"]].map {
- | value |
- value.hex
- }, subJson["exitKind"], subJson["isWatchpoint"],
- subJson["count"])
- osrExit.codeAddresses.each {
- | codeAddress |
- osrExits[codeAddress] = [] unless osrExits[codeAddress]
- osrExits[codeAddress] << osrExit
- }
- osrExit.origin[-1].osrExits << osrExit
- }
- @profiledBytecodes = []
- json["profiledBytecodes"].each {
- | subJson |
- @profiledBytecodes << ProfiledBytecodes.new(subJson)
- }
- @numInlinedGetByIds = json["numInlinedGetByIds"]
- @numInlinedPutByIds = json["numInlinedPutByIds"]
- @numInlinedCalls = json["numInlinedCalls"]
- end
-
- def counter(origin)
- @counters[origin]
- end
-
- def to_s
- "#{bytecode}-#{compilationIndex}-#{engine}"
- end
- end
- class DescriptionLine
- attr_reader :actualCountsString, :sourceCountsString, :disassembly, :shouldShow
-
- def initialize(actualCountsString, sourceCountsString, disassembly, shouldShow)
- @actualCountsString = actualCountsString
- @sourceCountsString = sourceCountsString
- @disassembly = disassembly
- @shouldShow = shouldShow
- end
-
- def codeAddress
- if @disassembly =~ /^\s*(0x[0-9a-fA-F]+):/
- $1.hex
- else
- nil
- end
- end
- end
-
- if ARGV.length != 1
- $stderr.puts "Usage: display-profiler-output <path to profiler output file>"
- $stderr.puts
- $stderr.puts "The typical usage pattern for the profiler currently looks something like:"
- $stderr.puts
- $stderr.puts "Path/To/jsc -p profile.json myprogram.js"
- $stderr.puts "display-profiler-output profile.json"
- exit 1
- end
- $json = JSON::parse(IO::read(ARGV[0]))
- $bytecodes = $json["bytecodes"].map {
- | subJson |
- Bytecodes.new(subJson)
- }
- $compilations = $json["compilations"].map {
- | subJson |
- Compilation.new(subJson)
- }
- $engines = ["Baseline", "DFG"]
- def lpad(str,chars)
- if str.length>chars
- str
- else
- "%#{chars}s"%(str)
- end
- end
- def rpad(str, chars)
- while str.length < chars
- str += " "
- end
- str
- end
- def center(str, chars)
- while str.length < chars
- str += " "
- if str.length < chars
- str = " " + str
- end
- end
- str
- end
- def mayBeHash(hash)
- hash =~ /#/ or hash.size == 6
- end
- def sourceOnOneLine(source, limit)
- source.gsub(/\s+/, ' ')[0...limit]
- end
- def screenWidth
- if $stdin.tty?
- HighLine::SystemExtensions.terminal_size[0]
- else
- 200
- end
- end
- def summary(mode)
- remaining = screenWidth
-
- # Figure out how many columns we need for the code block names, and for counts
- maxCount = 0
- maxName = 0
- $bytecodes.each {
- | bytecodes |
- maxCount = ([maxCount] + $engines.map {
- | engine |
- bytecodes.maxTopExecutionCount(engine)
- } + $engines.map {
- | engine |
- bytecodes.maxBottomExecutionCount(engine)
- }).max
- maxName = [bytecodes.to_s.size, maxName].max
- }
- maxCountDigits = maxCount.to_s.size
-
- hashCols = [[maxName, 30].min, "CodeBlock".size].max
- remaining -= hashCols + 1
-
- countCols = [maxCountDigits * $engines.size, "Source Counts".size].max
- remaining -= countCols + 1
-
- if mode == :full
- instructionCountCols = 6
- remaining -= instructionCountCols + 1
-
- machineCountCols = [maxCountDigits * $engines.size, "Machine Counts".size].max
- remaining -= machineCountCols + 1
-
- compilationsCols = 7
- remaining -= compilationsCols + 1
-
- inlinesCols = 9
- remaining -= inlinesCols + 1
-
- exitCountCols = 7
- remaining -= exitCountCols + 1
-
- recentOptsCols = 12
- remaining -= recentOptsCols + 1
- end
-
- if remaining > 0
- sourceCols = remaining
- else
- sourceCols = nil
- end
-
- print(center("CodeBlock", hashCols))
- if mode == :full
- print(" " + center("#Instr", instructionCountCols))
- end
- print(" " + center("Source Counts", countCols))
- if mode == :full
- print(" " + center("Machine Counts", machineCountCols))
- print(" " + center("#Compil", compilationsCols))
- print(" " + center("Inlines", inlinesCols))
- print(" " + center("#Exits", exitCountCols))
- print(" " + center("Last Opts", recentOptsCols))
- end
- if sourceCols
- print(" " + center("Source", sourceCols))
- end
- puts
-
- print(center("", hashCols))
- if mode == :full
- print(" " + (" " * instructionCountCols))
- end
- print(" " + center("Base/DFG", countCols))
- if mode == :full
- print(" " + center("Base/DFG", machineCountCols))
- print(" " + (" " * compilationsCols))
- print(" " + center("Src/Total", inlinesCols))
- print(" " + (" " * exitCountCols))
- print(" " + center("Get/Put/Call", recentOptsCols))
- end
- puts
- $bytecodes.sort {
- | a, b |
- b.totalMaxTopExecutionCount <=> a.totalMaxTopExecutionCount
- }.each {
- | bytecode |
- print(center(bytecode.name(hashCols), hashCols))
- if mode == :full
- print(" " + center(bytecode.instructionCount.to_s, instructionCountCols))
- end
- print(" " +
- center($engines.map {
- | engine |
- bytecode.maxTopExecutionCount(engine).to_s
- }.join("/"), countCols))
- if mode == :full
- print(" " + center($engines.map {
- | engine |
- bytecode.maxBottomExecutionCount(engine).to_s
- }.join("/"), machineCountCols))
- print(" " + center(bytecode.compilations.size.to_s, compilationsCols))
- print(" " + center(bytecode.sourceMachineInlineSites.to_s + "/" + bytecode.totalMachineInlineSites.to_s, inlinesCols))
- print(" " + center(bytecode.totalExitCount.to_s, exitCountCols))
- lastCompilation = bytecode.compilations[-1]
- if lastCompilation
- optData = [lastCompilation.numInlinedGetByIds,
- lastCompilation.numInlinedPutByIds,
- lastCompilation.numInlinedCalls]
- else
- optData = ["N/A"]
- end
- print(" " + center(optData.join('/'), recentOptsCols))
- end
- if sourceCols
- print(" " + sourceOnOneLine(bytecode.source, sourceCols))
- end
- puts
- }
- end
- def executeCommand(*commandArray)
- command = commandArray[0]
- args = commandArray[1..-1]
- case command
- when "help", "h", "?"
- puts "summary (s) Print a summary of code block execution rates."
- puts "full (f) Same as summary, but prints more information."
- puts "source Show the source for a code block."
- puts "bytecode (b) Show the bytecode for a code block, with counts."
- puts "profiling (p) Show the (internal) profiling data for a code block."
- puts "display (d) Display details for a code block."
- puts "inlines Show all inlining stacks that the code block was on."
- puts "help (h) Print this message."
- puts "quit (q) Quit."
- when "quit", "q", "exit"
- exit 0
- when "summary", "s"
- summary(:summary)
- when "full", "f"
- summary(:full)
- when "source"
- if args.length != 1
- puts "Usage: source <code block hash>"
- return
- end
- $bytecodes.each {
- | bytecode |
- if bytecode.matches(args[0])
- puts bytecode.source
- end
- }
- when "bytecode", "b"
- if args.length != 1
- puts "Usage: source <code block hash>"
- return
- end
-
- hash = args[0]
-
- countCols = 10 * $engines.size
- machineCols = 10 * $engines.size
- pad = 1
- while (countCols + 1 + machineCols + pad) % 8 != 0
- pad += 1
- end
-
- $bytecodes.each {
- | bytecodes |
- next unless bytecodes.matches(hash)
- puts(center("Source Counts", countCols) + " " + center("Machine Counts", machineCols) +
- (" " * pad) + center("Bytecode for #{bytecodes}", screenWidth - pad - countCols - 1 - machineCols))
- puts(center("Base/DFG", countCols) + " " + center("Base/DFG", countCols))
- bytecodes.each {
- | bytecode |
- if bytecode.shouldHaveCounts?
- countsString = $engines.map {
- | myEngine |
- bytecode.topExecutionCount(myEngine)
- }.join("/")
- machineString = $engines.map {
- | myEngine |
- bytecode.bottomExecutionCount(myEngine)
- }.join("/")
- else
- countsString = ""
- machineString = ""
- end
- puts(center(countsString, countCols) + " " + center(machineString, machineCols) + (" " * pad) + bytecode.description.chomp)
- bytecode.osrExits.each {
- | exit |
- puts(center("!!!!!", countCols) + " " + center("!!!!!", machineCols) + (" " * (pad + 10)) +
- "EXIT: in #{exit.compilation} due to #{exit.exitKind}, #{exit.count} times")
- }
- }
- }
- when "profiling", "p"
- if args.length != 1
- puts "Usage: profiling <code block hash>"
- return
- end
-
- hash = args[0]
-
- first = true
- $compilations.each {
- | compilation |
-
- compilation.profiledBytecodes.each {
- | profiledBytecodes |
- if profiledBytecodes.bytecodes.matches(hash)
- if first
- first = false
- else
- puts
- end
-
- puts "Compilation #{compilation}:"
- profiledBytecodes.header.each {
- | header |
- puts(" " * 6 + header)
- }
- profiledBytecodes.each {
- | bytecode |
- puts(" " * 8 + bytecode.description)
- profiledBytecodes.bytecodes.bytecode(bytecode.bytecodeIndex).osrExits.each {
- | exit |
- if exit.compilation == compilation
- puts(" !!!!! EXIT: due to #{exit.exitKind}, #{exit.count} times")
- end
- }
- }
- end
- }
- }
- when "inlines"
- if args.length != 1
- puts "Usage: inlines <code block hash>"
- return
- end
-
- hash = args[0]
-
- $bytecodes.each {
- | bytecodes |
- next unless bytecodes.matches(hash)
-
- # FIXME: print something useful to say more about which code block this is.
-
- $compilations.each {
- | compilation |
- myOrigins = []
- compilation.descriptions.each {
- | description |
- if description.origin.index {
- | myBytecode |
- bytecodes == myBytecode.bytecodes
- }
- myOrigins << description.origin
- end
- }
- myOrigins.uniq!
- myOrigins.sort! {
- | a, b |
- result = 0
- [a.size, b.size].min.times {
- | index |
- result = a[index].bytecodeIndex <=> b[index].bytecodeIndex
- break if result != 0
- }
- result
- }
-
- next if myOrigins.empty?
- printArray = []
- lastPrintStack = []
-
- def originToPrintStack(origin)
- (0...(origin.size - 1)).map {
- | index |
- "bc\##{origin[index].bytecodeIndex} --> #{origin[index + 1].bytecodes}"
- }
- end
-
- def printStack(printArray, stack, lastStack)
- stillCommon = true
- stack.each_with_index {
- | entry, index |
- next if stillCommon and entry == lastStack[index]
- printArray << (" " * (index + 1) + entry)
- stillCommon = false
- }
- end
-
- myOrigins.each {
- | origin |
- currentPrintStack = originToPrintStack(origin)
- printStack(printArray, currentPrintStack, lastPrintStack)
- lastPrintStack = currentPrintStack
- }
- next if printArray.empty?
-
- puts "Compilation #{compilation}:"
- printArray.each {
- | entry |
- puts entry
- }
- }
- }
- when "display", "d"
- compilationIndex = nil
-
- case args.length
- when 1
- if args[0] == "*"
- hash = nil
- else
- hash = args[0]
- end
- engine = nil
- when 2
- if mayBeHash(args[0])
- hash = args[0]
- engine = args[1]
- else
- engine = args[0]
- hash = args[1]
- end
- else
- puts "Usage: summary <code block hash> <engine>"
- return
- end
-
- if hash and hash =~ /-([0-9]+)-/
- hash = $~.pre_match
- engine = $~.post_match
- compilationIndex = $1.to_i
- end
-
- if engine and not $engines.index(engine)
- pattern = Regexp.new(Regexp.escape(engine), "i")
- trueEngine = nil
- $engines.each {
- | myEngine |
- if myEngine =~ pattern
- trueEngine = myEngine
- break
- end
- }
- unless trueEngine
- puts "#{engine} is not a valid engine, try #{$engines.join(' or ')}."
- return
- end
- engine = trueEngine
- end
-
- actualCountCols = 13
- sourceCountCols = 10 * $engines.size
-
- first = true
- $compilations.each {
- | compilation |
- next if hash and not compilation.bytecode.matches(hash)
- next if engine and compilation.engine != engine
- next if compilationIndex and compilation.compilationIndex != compilationIndex
-
- if first
- first = false
- else
- puts
- end
-
- puts("Compilation #{compilation}:")
- puts(" Num inlined: GetByIds: #{compilation.numInlinedGetByIds} PutByIds: #{compilation.numInlinedPutByIds} Calls: #{compilation.numInlinedCalls}")
- puts(center("Actual Counts", actualCountCols) + " " + center("Source Counts", sourceCountCols) + " " + center("Disassembly in #{compilation.engine}", screenWidth - 1 - sourceCountCols - 1 - actualCountCols))
- puts((" " * actualCountCols) + " " + center("Base/DFG", sourceCountCols))
- lines = []
- compilation.descriptions.each {
- | description |
- # FIXME: We should have a better way of detecting things like CountExecution nodes
- # and slow path entries in the baseline JIT.
- if description.description =~ /CountExecution\(/ and compilation.engine == "DFG"
- shouldShow = false
- else
- shouldShow = true
- end
- if description.origin.empty? or not description.origin[-1].shouldHaveCounts? or (compilation.engine == "Baseline" and description.description =~ /^\s*\(S\)/)
- actualCountsString = ""
- sourceCountsString = ""
- else
- actualCountsString = compilation.counter(description.origin).count.to_s
- sourceCountsString = $engines.map {
- | myEngine |
- description.origin[-1].topExecutionCount(myEngine)
- }.join("/")
- end
- description.description.split("\n").each {
- | line |
- lines << DescriptionLine.new(actualCountsString, sourceCountsString, line.chomp, shouldShow)
- }
- }
-
- exitPrefix = center("!!!!!", actualCountCols) + " " + center("!!!!!", sourceCountCols) + (" " * 25)
-
- lines.each_with_index {
- | line, index |
- codeAddress = line.codeAddress
- if codeAddress
- list = compilation.osrExits[codeAddress]
- if list
- list.each {
- | exit |
- if exit.isWatchpoint
- exit.dumpForDisplay(exitPrefix)
- end
- }
- end
- end
- if line.shouldShow
- puts(center(line.actualCountsString, actualCountCols) + " " + center(line.sourceCountsString, sourceCountCols) + " " + line.disassembly)
- end
- if codeAddress
- # Find the next disassembly address.
- endIndex = index + 1
- endAddress = nil
- while endIndex < lines.size
- myAddress = lines[endIndex].codeAddress
- if myAddress
- endAddress = myAddress
- break
- end
- endIndex += 1
- end
-
- if endAddress
- list = compilation.osrExits[endAddress]
- if list
- list.each {
- | exit |
- unless exit.isWatchpoint
- exit.dumpForDisplay(exitPrefix)
- end
- }
- end
- end
- end
- }
- }
- else
- puts "Invalid command: #{command}"
- end
- end
- if $stdin.tty?
- executeCommand("full")
- end
- while commandLine = Readline.readline("> ", true)
- executeCommand(*commandLine.split)
- end
|