require 'ruby-prof/abstract_printer' require 'erb' module RubyProf # Generates graph[link:files/examples/graph_html.html] profile reports as html. # To use the grap html printer: # # result = RubyProf.profile do # [code to profile] # end # # printer = RubyProf::GraphHtmlPrinter.new(result) # printer.print(STDOUT, :min_percent=>0) # # The constructor takes two arguments. The first is # a RubyProf::Result object generated from a profiling # run. The second is the minimum %total (the methods # total time divided by the overall total time) that # a method must take for it to be printed out in # the report. Use this parameter to eliminate methods # that are not important to the overall profiling results. class GraphHtmlPrinter < AbstractPrinter include ERB::Util PERCENTAGE_WIDTH = 8 TIME_WIDTH = 10 CALL_WIDTH = 20 # Create a GraphPrinter. Result is a RubyProf::Result # object generated from a profiling run. def initialize(result) super(result) @thread_times = Hash.new calculate_thread_times end # Print a graph html report to the provided output. # # output - Any IO oject, including STDOUT or a file. # The default value is STDOUT. # # options - Hash of print options. See #setup_options # for more information. # def print(output = STDOUT, options = {}) @output = output setup_options(options) filename = options[:filename] template = filename ? File.read(filename).untaint : (options[:template] || self.template) _erbout = @output erb = ERB.new(template, nil, nil) erb.filename = filename @output << erb.result(binding) end # These methods should be private but then ERB doesn't # work. Turn off RDOC though #-- def calculate_thread_times # Cache thread times since this is an expensive # operation with the required sorting @result.threads.each do |thread_id, methods| top = methods.sort.last thread_time = 0.01 thread_time = top.total_time if top.total_time > 0 @thread_times[thread_id] = thread_time end end def thread_time(thread_id) @thread_times[thread_id] end def total_percent(thread_id, method) overall_time = self.thread_time(thread_id) (method.total_time/overall_time) * 100 end def self_percent(method) overall_time = self.thread_time(method.thread_id) (method.self_time/overall_time) * 100 end # Creates a link to a method. Note that we do not create # links to methods which are under the min_perecent # specified by the user, since they will not be # printed out. def create_link(thread_id, method) if self.total_percent(thread_id, method) < min_percent # Just return name h method.full_name else href = '#' + method_href(thread_id, method) "#{h method.full_name}" end end def method_href(thread_id, method) h(method.full_name.gsub(/[><#\.\?=:]/,"_") + "_" + thread_id.to_s) end def template '

Profile Report

<% for thread_id, methods in @result.threads %> <% end %>
Thread ID Total Time
<%= thread_id %> <%= thread_time(thread_id) %>
<% for thread_id, methods in @result.threads total_time = thread_time(thread_id) %>

Thread <%= thread_id %>

<% min_time = @options[:min_time] || (@options[:nonzero] ? 0.005 : nil) methods.sort.reverse_each do |method| total_percentage = (method.total_time/total_time) * 100 next if total_percentage < min_percent next if min_time && method.total_time < min_time self_percentage = (method.self_time/total_time) * 100 %> <% for caller in method.parents %> <% next if min_time && caller.total_time < min_time %> <% called = "#{caller.called}/#{method.called}" %> <% end %> <% for callee in method.children %> <% next if min_time && callee.total_time < min_time %> <% called = "#{callee.called}/#{callee.target.called}" %> <% end %> <% end %>
<%= sprintf("%#{PERCENTAGE_WIDTH}s", "%Total") %> <%= sprintf("%#{PERCENTAGE_WIDTH}s", "%Self") %> <%= sprintf("%#{TIME_WIDTH}s", "Total") %> <%= sprintf("%#{TIME_WIDTH}s", "Self") %> <%= sprintf("%#{TIME_WIDTH}s", "Wait") %> <%= sprintf("%#{TIME_WIDTH+2}s", "Child") %> <%= sprintf("%#{CALL_WIDTH}s", "Calls") %> Name Line
    <%= sprintf("%#{TIME_WIDTH}.2f", caller.total_time) %> <%= sprintf("%#{TIME_WIDTH}.2f", caller.self_time) %> <%= sprintf("%#{TIME_WIDTH}.2f", caller.wait_time) %> <%= sprintf("%#{TIME_WIDTH}.2f", caller.children_time) %><%= sprintf("%#{CALL_WIDTH}s", called) %> <%= create_link(thread_id, caller.target) %> <%= caller.line %>
<%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", total_percentage) %> <%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", self_percentage) %> <%= sprintf("%#{TIME_WIDTH}.2f", method.total_time) %> <%= sprintf("%#{TIME_WIDTH}.2f", method.self_time) %> <%= sprintf("%#{TIME_WIDTH}.2f", method.wait_time) %> <%= sprintf("%#{TIME_WIDTH}.2f", method.children_time) %> <%= sprintf("%#{CALL_WIDTH}i", method.called) %> <%= h method.full_name %> <%= method.line %>
    <%= sprintf("%#{TIME_WIDTH}.2f", callee.total_time) %> <%= sprintf("%#{TIME_WIDTH}.2f", callee.self_time) %> <%= sprintf("%#{TIME_WIDTH}.2f", callee.wait_time) %> <%= sprintf("%#{TIME_WIDTH}.2f", callee.children_time) %><%= sprintf("%#{CALL_WIDTH}s", called) %> <%= create_link(thread_id, callee.target) %> <%= callee.line %>
<% end %> ' end end end