Title: [136756] trunk/Tools
Revision
136756
Author
fpi...@apple.com
Date
2012-12-05 13:40:32 -0800 (Wed, 05 Dec 2012)

Log Message

DFG profiler should display more information
https://bugs.webkit.org/show_bug.cgi?id=104163

Reviewed by Gavin Barraclough.

Added the following:
        
- Distinguish between source counts and bytecode counts.
        
- Add a "full" summary view that shows both kinds of counts, plus inline counts.
        
- Add a "source" command to see the source.
        
- Add a "bytecode" command to see the bytecode and counts.

* Scripts/display-profiler-output:

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (136755 => 136756)


--- trunk/Tools/ChangeLog	2012-12-05 21:40:26 UTC (rev 136755)
+++ trunk/Tools/ChangeLog	2012-12-05 21:40:32 UTC (rev 136756)
@@ -1,3 +1,22 @@
+2012-12-05  Filip Pizlo  <fpi...@apple.com>
+
+        DFG profiler should display more information
+        https://bugs.webkit.org/show_bug.cgi?id=104163
+
+        Reviewed by Gavin Barraclough.
+
+        Added the following:
+        
+        - Distinguish between source counts and bytecode counts.
+        
+        - Add a "full" summary view that shows both kinds of counts, plus inline counts.
+        
+        - Add a "source" command to see the source.
+        
+        - Add a "bytecode" command to see the bytecode and counts.
+
+        * Scripts/display-profiler-output:
+
 2012-12-05  Yong Li  <y...@rim.com>
 
         [BlackBerry] Build with libjpegturbo

Modified: trunk/Tools/Scripts/display-profiler-output (136755 => 136756)


--- trunk/Tools/Scripts/display-profiler-output	2012-12-05 21:40:26 UTC (rev 136755)
+++ trunk/Tools/Scripts/display-profiler-output	2012-12-05 21:40:32 UTC (rev 136756)
@@ -27,15 +27,19 @@
 
 require 'json'
 require 'readline'
+require 'highline'
 
 class Bytecode
-    attr_accessor :bytecodeIndex, :opcode, :description, :topCounts
+    attr_accessor :bytecodes, :bytecodeIndex, :opcode, :description, :topCounts, :bottomCounts, :machineInlinees
     
-    def initialize(bytecodeIndex, opcode, description)
+    def initialize(bytecodes, bytecodeIndex, opcode, description)
+        @bytecodes = bytecodes
         @bytecodeIndex = bytecodeIndex
         @opcode = opcode
         @description = description
-        @topCounts = []
+        @topCounts = [] # "source" counts
+        @bottomCounts = {} # "machine" counts, maps compilations to counts
+        @machineInlinees = {} # maps my compilation to a set of inlinees
     end
     
     def shouldHaveCounts?
@@ -46,7 +50,17 @@
         @topCounts << count
     end
     
-    def totalExecutionCount
+    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 |
@@ -55,7 +69,7 @@
         sum
     end
     
-    def executionCount(engine)
+    def topExecutionCount(engine)
         sum = 0
         @topCounts.each {
             | value |
@@ -65,21 +79,51 @@
         }
         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
 end
 
 class Bytecodes
-    attr_accessor :hash, :source
-    include Enumerable
+    attr_accessor :codeHash, :source, :machineInlineSites
     
     def initialize(json)
-        @hash = json["hash"].to_s
+        @codeHash = json["hash"].to_s
         @source = json["sourceCode"].to_s
         @bytecode = {}
         json["bytecode"].each {
             | subJson |
             index = subJson["bytecodeIndex"].to_i
-            @bytecode[index] = Bytecode.new(index, subJson["opcode"].to_s, subJson["description"].to_s)
+            @bytecode[index] = Bytecode.new(self, index, subJson["opcode"].to_s, subJson["description"].to_s)
         }
+        @machineInlineSites = {} # maps compilation to a set of origins
     end
     
     def each
@@ -93,23 +137,64 @@
         @bytecode[bytecodeIndex]
     end
     
-    def totalMaxExecutionCount
+    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.totalExecutionCount].max
+            max = [max, bytecode.totalTopExecutionCount].max
         }
         max
     end
     
-    def maxExecutionCount(engine)
+    def maxTopExecutionCount(engine)
         max = 0
         @bytecode.each_value {
             | bytecode |
-            max = [max, bytecode.executionCount(engine)].max
+            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
 end
 
 def originStackFromJSON(json)
@@ -148,6 +233,15 @@
             | 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 |
@@ -155,8 +249,13 @@
             counter = ExecutionCounter.new(origin, @engine, subJson["executionCount"].to_i)
             @counters[origin] = counter
             origin[-1].addTopCount(counter)
+            origin[0].addBottomCountForCompilation(counter, self)
         }
     end
+    
+    def counter(origin)
+        @counters[origin]
+    end
 end
 
 $json = JSON::parse(IO::read(ARGV[0]))
@@ -207,13 +306,75 @@
     end
 end
 
-def sourceOnOneLine(source)
-    source = source.gsub(/\s+/, ' ')
-    if source.size > 80
-        source[0..77] + "..."
+def sourceOnOneLine(source, limit)
+    source.gsub(/\s+/, ' ')[0...limit]
+end
+
+def screenWidth
+    HighLine::SystemExtensions.terminal_size[0]
+end
+
+def summary(mode)
+    remaining = screenWidth
+    
+    hashCols = 11
+    remaining -= hashCols + 1
+    
+    countCols = 10 * $engines.size
+    remaining -= countCols + 1
+    
+    if mode == :full
+        machineCountCols = 10 * $engines.size
+        remaining -= machineCountCols + 1
+        
+        inlinesCols = 9
+        remaining -= inlinesCols + 1
+    end
+    
+    if remaining > 0
+        sourceCols = remaining
     else
-        source
+        sourceCols = nil
     end
+    
+    print(center("CodeBlock", hashCols) + " " + center("Source Counts", countCols))
+    if mode == :full
+        print(" " + center("Machine Counts", machineCountCols))
+        print(" " + center("Inlines", inlinesCols))
+    end
+    if sourceCols
+        print(" " + center("Source", sourceCols))
+    end
+    puts
+    
+    print(center("", hashCols) + " " + center("Base/DFG", countCols))
+    if mode == :full
+        print(" " + center("Base/DFG", machineCountCols))
+        print(" " + center("Src/Total", inlinesCols))
+    end
+    puts
+    $bytecodes.sort {
+        | a, b |
+        b.totalMaxTopExecutionCount <=> a.totalMaxTopExecutionCount
+    }.each {
+        | bytecode |
+        print(center("#" + bytecode.codeHash, hashCols) + " " +
+              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.sourceMachineInlineSites.to_s + "/" + bytecode.totalMachineInlineSites.to_s, inlinesCols))
+        end
+        if sourceCols
+            print(" " + sourceOnOneLine(bytecode.source, sourceCols))
+        end
+        puts
+    }
 end
 
 def executeCommand(*commandArray)
@@ -222,42 +383,73 @@
     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 "display (d)     Display details for a code block."
         puts "help (h)        Print this message."
         puts "quit (q)        Quit."
     when "quit", "q", "exit"
         exit 0
     when "summary", "s"
-        hashCols = 14
+        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.codeHash == 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
         
-        puts(rpad("CodeBlock", hashCols) + " " + rpad($engines.join("/") + " Counts", countCols) + " Source")
-        $bytecodes.sort {
-            | a, b |
-            b.totalMaxExecutionCount <=> a.totalMaxExecutionCount
-        }.each {
-            | bytecode |
-            puts(center("#" + bytecode.hash, hashCols) + " " +
-                 center($engines.map {
-                            | engine |
-                            bytecode.maxExecutionCount(engine).to_s
-                        }.join("/"), countCols) + " " +
-                 sourceOnOneLine(bytecode.source))
+        puts(center("Source Counts", countCols) + " " + center("Machine Counts", machineCols) +
+             (" " * pad) + center("Bytecode for #{hash}", screenWidth - pad - countCols - 1 - machineCols))
+        puts(center("Base/DFG", countCols) + " " + center("Base/DFG", countCols))
+        $bytecodes.each {
+            | bytecodes |
+            next if bytecodes.codeHash != hash
+            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)
+            }
         }
     when "display", "d"
         case args.length
         when 1
             hash = args[0]
-            engine = "Baseline"
-            $compilations.each {
-                | compilation |
-                next if compilation.bytecode.hash != hash
-                
-                if compilation.engine == "DFG"
-                    engine = "DFG"
-                    break
-                end
-            }
+            engine = nil
         when 2
             if mayBeHash(args[0])
                 hash = args[0]
@@ -271,34 +463,63 @@
             return
         end
         
-        unless $engines.index(engine)
-            puts "#{engine} is not a valid engine, try #{$engines.join(' or ')}."
-            return
+        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
         
-        countCols = 10 * $engines.size
+        actualCountCols = 13
+        sourceCountCols = 10 * $engines.size
+        
+        first = true
+        count = 0
         $compilations.each {
             | compilation |
-            next if compilation.bytecode.hash != hash
-            next if compilation.engine != engine
+            next if compilation.bytecode.codeHash != hash
+
+            count += 1
+
+            next if engine and compilation.engine != engine
             
-            puts(rpad($engines.join("/") + " Counts", countCols) + " Disassembly for #{hash} in #{engine}")
+            if first
+                first = false
+            else
+                puts
+            end
+            
+            puts("Compilation \##{count}:")
+            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))
             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.
-                next if description.description =~ /CountExecution\(/ and engine == "DFG"
-                if description.origin.empty? or not description.origin[-1].shouldHaveCounts? or (engine == "Baseline" and description.description =~ /^\s*\(S\)/)
-                    countsString = ""
+                next if description.description =~ /CountExecution\(/ and compilation.engine == "DFG"
+                if description.origin.empty? or not description.origin[-1].shouldHaveCounts? or (compilation.engine == "Baseline" and description.description =~ /^\s*\(S\)/)
+                    actualCountsString = ""
+                    sourceCountsString = ""
                 else
-                    countsString = $engines.map {
+                    actualCountsString = compilation.counter(description.origin).count.to_s
+                    sourceCountsString = $engines.map {
                         | myEngine |
-                        description.origin[-1].executionCount(myEngine)
+                        description.origin[-1].topExecutionCount(myEngine)
                     }.join("/")
                 end
                 description.description.split("\n").each {
                     | line |
-                    puts(center(countsString, countCols) + " " + line.chomp)
+                    puts(center(actualCountsString, actualCountCols) + " " + center(sourceCountsString, sourceCountCols) + " " + line.chomp)
                 }
             }
         }
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to