class RubyProf::GraphHtmlPrinter

  1. lib/ruby-prof/graph_html_printer.rb
Parent: RubyProf

Generates graph profile reports as html. To use the graph 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.

Included modules

  1. ERB::Util

Constants

CALL_WIDTH = 20  
PERCENTAGE_WIDTH = 8  
TIME_WIDTH = 10  

Public Class methods

new (result)

Create a GraphPrinter. Result is a RubyProf::Result object generated from a profiling run.

[show source]
# File lib/ruby-prof/graph_html_printer.rb, line 32
def initialize(result)
  super(result)
  @thread_times = Hash.new
  calculate_thread_times
end

Public Instance methods

calculate_thread_times ()

These methods should be private but then ERB doesn't work. Turn off RDOC though

[show source]
# File lib/ruby-prof/graph_html_printer.rb, line 76
def calculate_thread_times
  # Cache thread times since this is an expensive
  # operation with the required sorting
  @overall_threads_time = 0.0
  @thread_times = Hash.new
  @result.threads.each do |thread_id, methods|
    roots = methods.select{|m| m.root?}
    thread_total_time = sum(roots.map{|r| self.total_time(r.call_infos)})
    @overall_threads_time += thread_total_time
    @thread_times[thread_id] = thread_total_time
  end
end
method_href (thread_id, method)
[show source]
# File lib/ruby-prof/graph_html_printer.rb, line 117
def method_href(thread_id, method)
  h(method.full_name.gsub(%r[><#\.\?=:]/,"_") + "_" + thread_id.to_s)
end
print (output = STDOUT, options = {})

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.

unique options are:

:filename    - specify a file to use that contains the ERB
               template to use, instead of the built-in self.template
:template    - specify an ERB template to use, instead of the
               built-in self.template
[show source]
# File lib/ruby-prof/graph_html_printer.rb, line 53
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
self_percent (method)
[show source]
# File lib/ruby-prof/graph_html_printer.rb, line 98
def self_percent(method)
  overall_time = self.thread_time(method.thread_id)
  (method.self_time/overall_time) * 100
end
sum (a)
[show source]
# File lib/ruby-prof/graph_html_printer.rb, line 69
def sum(a)
  a.inject(0.0){|s,t| s+=t}
end
template ()
[show source]
# File lib/ruby-prof/graph_html_printer.rb, line 134
    def template
'
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
  <style media="all" type="text/css">
    table {
      border-collapse: collapse;
      border: 1px solid #CCC;
      font-family: Verdana, Arial, Helvetica, sans-serif;
      font-size: 9pt;
      line-height: normal;
      width: 100%;
    }

    th {
      text-align: center;
      border-top: 1px solid #FB7A31;
      border-bottom: 1px solid #FB7A31;
      background: #FFC;
      padding: 0.3em;
      border-left: 1px solid silver;
    }

    tr.break td {
      border: 0;
      border-top: 1px solid #FB7A31;
      padding: 0;
      margin: 0;
    }

    tr.method td {
      font-weight: bold;
    }

    td {
      padding: 0.3em;
    }

    td:first-child {
      width: 190px;
      }

    td {
      border-left: 1px solid #CCC;
      text-align: center;
    }

    .method_name {
      text-align: left;
    }
  </style>
  </head>
  <body>
    <h1>Profile Report</h1>
    <!-- Threads Table -->
    <table>
      <tr>
        <th>Thread ID</th>
        <th>Total Time</th>
      </tr>
      <% for thread_id in @result.threads.keys.sort %>
      <tr>
        <td><a href="#<%= thread_id %>"><%= thread_id %></a></td>
        <td><%= thread_time(thread_id) %></td>
      </tr>
      <% end %>
    </table>

    <!-- Methods Tables -->
    <% for thread_id in @result.threads.keys.sort
         methods = @result.threads[thread_id]
         total_time = thread_time(thread_id) %>
      <h2><a name="<%= thread_id %>">Thread <%= thread_id %></a></h2>

      <table>
        <tr>
          <th><%= sprintf("%#{PERCENTAGE_WIDTH}s", "%Total") %></th>
          <th><%= sprintf("%#{PERCENTAGE_WIDTH}s", "%Self") %></th>
          <th><%= sprintf("%#{TIME_WIDTH}s", "Total") %></th>
          <th><%= sprintf("%#{TIME_WIDTH}s", "Self") %></th>
          <th><%= sprintf("%#{TIME_WIDTH}s", "Wait") %></th>
          <th><%= sprintf("%#{TIME_WIDTH+2}s", "Child") %></th>
          <th><%= sprintf("%#{CALL_WIDTH}s", "Calls") %></th>
          <th class="method_name">Name</th>
          <th>Line</th>
        </tr>

        <% min_time = @options[:min_time] || (@options[:nonzero] ? 0.005 : nil)
           methods.sort_by(&sort_method).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 %>

            <!-- Parents -->
            <% for caller in method.aggregate_parents.sort_by(&:total_time)
                 next unless caller.parent
                 next if min_time && caller.total_time < min_time  %>
              <tr>
                <td>&nbsp;</td>
                <td>&nbsp;</td>
                <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.total_time) %></td>
                <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.self_time) %></td>
                <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.wait_time) %></td>
                <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.children_time) %></td>
                <% called = "#{caller.called}/#{method.called}" %>
                <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
                <td class="method_name"><%= create_link(thread_id, caller.parent.target) %></td>
                <td><%= file_link(caller.parent.target.source_file, caller.line) %></td>
              </tr>
            <% end %>

            <tr class="method">
              <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", total_percentage) %></td>
              <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", self_percentage) %></td>
              <td><%= sprintf("%#{TIME_WIDTH}.2f", method.total_time) %></td>
              <td><%= sprintf("%#{TIME_WIDTH}.2f", method.self_time) %></td>
              <td><%= sprintf("%#{TIME_WIDTH}.2f", method.wait_time) %></td>
              <td><%= sprintf("%#{TIME_WIDTH}.2f", method.children_time) %></td>
              <td><%= sprintf("%#{CALL_WIDTH}i", method.called) %></td>
              <td class="method_name"><a name="<%= method_href(thread_id, method) %>"><%= h method.full_name %></a></td>
              <td><%= file_link(method.source_file, method.line) %></td>
            </tr>

            <!-- Children -->
            <% for callee in method.aggregate_children.sort_by(&:total_time).reverse %>
            <%   next if min_time && callee.total_time < min_time  %>
              <tr>
                <td>&nbsp;</td>
                <td>&nbsp;</td>
                <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.total_time) %></td>
                <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.self_time) %></td>
                <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.wait_time) %></td>
                <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.children_time) %></td>
                <% called = "#{callee.called}/#{callee.target.called}" %>
                <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
                <td class="method_name"><%= create_link(thread_id, callee.target) %></td>
                <td><%= file_link(method.source_file, callee.line) %></td>
              </tr>
            <% end %>
            <!-- Create divider row -->
            <tr class="break"><td colspan="9"></td></tr>
        <% end %>
      </table>
    <% end %>
  </body>
</html>'
    end
thread_time (thread_id)
[show source]
# File lib/ruby-prof/graph_html_printer.rb, line 89
def thread_time(thread_id)
  @thread_times[thread_id]
end
total_percent (thread_id, method)
[show source]
# File lib/ruby-prof/graph_html_printer.rb, line 93
def total_percent(thread_id, method)
  overall_time = self.thread_time(thread_id)
  (method.total_time/overall_time) * 100
end
total_time (call_infos)
[show source]
# File lib/ruby-prof/graph_html_printer.rb, line 65
def total_time(call_infos)
  sum(call_infos.map{|ci| ci.total_time})
end