ruby-prof-danielhoey 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. data/CHANGES +221 -0
  2. data/LICENSE +23 -0
  3. data/README +432 -0
  4. data/Rakefile +158 -0
  5. data/bin/ruby-prof +224 -0
  6. data/examples/flat.txt +55 -0
  7. data/examples/graph.html +823 -0
  8. data/examples/graph.txt +170 -0
  9. data/ext/ruby_prof/call_tree.c +392 -0
  10. data/ext/ruby_prof/call_tree.h +32 -0
  11. data/ext/ruby_prof/extconf.rb +40 -0
  12. data/ext/ruby_prof/list.c +66 -0
  13. data/ext/ruby_prof/list.h +10 -0
  14. data/ext/ruby_prof/measure_allocations.h +58 -0
  15. data/ext/ruby_prof/measure_cpu_time.h +152 -0
  16. data/ext/ruby_prof/measure_gc_runs.h +76 -0
  17. data/ext/ruby_prof/measure_gc_time.h +57 -0
  18. data/ext/ruby_prof/measure_memory.h +101 -0
  19. data/ext/ruby_prof/measure_process_time.h +52 -0
  20. data/ext/ruby_prof/measure_wall_time.h +53 -0
  21. data/ext/ruby_prof/measurement.h +13 -0
  22. data/ext/ruby_prof/mingw/Rakefile +23 -0
  23. data/ext/ruby_prof/mingw/build.rake +38 -0
  24. data/ext/ruby_prof/ruby_prof.c +1943 -0
  25. data/ext/ruby_prof/ruby_prof.h +183 -0
  26. data/ext/ruby_prof/version.h +4 -0
  27. data/lib/ruby-prof.rb +59 -0
  28. data/lib/ruby-prof/abstract_printer.rb +41 -0
  29. data/lib/ruby-prof/aggregate_call_info.rb +62 -0
  30. data/lib/ruby-prof/call_info.rb +47 -0
  31. data/lib/ruby-prof/call_tree/abstract_printer.rb +24 -0
  32. data/lib/ruby-prof/call_tree/html_printer.rb +89 -0
  33. data/lib/ruby-prof/call_tree/html_printer_output.html.erb +99 -0
  34. data/lib/ruby-prof/call_tree/text_printer.rb +28 -0
  35. data/lib/ruby-prof/call_tree_printer.rb +84 -0
  36. data/lib/ruby-prof/flat_printer.rb +78 -0
  37. data/lib/ruby-prof/flat_printer_with_line_numbers.rb +72 -0
  38. data/lib/ruby-prof/graph_html_printer.rb +256 -0
  39. data/lib/ruby-prof/graph_printer.rb +157 -0
  40. data/lib/ruby-prof/method_info.rb +111 -0
  41. data/lib/ruby-prof/symbol_to_proc.rb +8 -0
  42. data/lib/ruby-prof/task.rb +146 -0
  43. data/lib/ruby-prof/test.rb +148 -0
  44. data/lib/unprof.rb +8 -0
  45. data/rails/environment/profile.rb +24 -0
  46. data/rails/example/example_test.rb +9 -0
  47. data/rails/profile_test_helper.rb +21 -0
  48. data/test/aggregate_test.rb +121 -0
  49. data/test/basic_test.rb +290 -0
  50. data/test/current_failures_windows +8 -0
  51. data/test/do_nothing.rb +0 -0
  52. data/test/duplicate_names_test.rb +32 -0
  53. data/test/enumerable_test.rb +16 -0
  54. data/test/exceptions_test.rb +15 -0
  55. data/test/exclude_threads_test.rb +54 -0
  56. data/test/exec_test.rb +14 -0
  57. data/test/line_number_test.rb +73 -0
  58. data/test/measurement_test.rb +121 -0
  59. data/test/module_test.rb +54 -0
  60. data/test/no_method_class_test.rb +14 -0
  61. data/test/prime.rb +58 -0
  62. data/test/prime_test.rb +13 -0
  63. data/test/printers_test.rb +130 -0
  64. data/test/recursive_test.rb +275 -0
  65. data/test/ruby-prof-bin +20 -0
  66. data/test/singleton_test.rb +37 -0
  67. data/test/stack_test.rb +138 -0
  68. data/test/start_stop_test.rb +95 -0
  69. data/test/test_suite.rb +23 -0
  70. data/test/thread_test.rb +173 -0
  71. data/test/unique_call_path_test.rb +225 -0
  72. metadata +163 -0
@@ -0,0 +1,99 @@
1
+ <html>
2
+ <head>
3
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
4
+ <title>Ruby-Prof Results</title>
5
+ <style type="text/css" media="screen">
6
+ .call_tree_node {
7
+ margin-left: 10px;
8
+ padding-top: 2px;
9
+ padding-bottom: 1px;
10
+ }
11
+
12
+ .leaf {
13
+ color: #333333;
14
+ }
15
+
16
+
17
+ .extra_info {
18
+ font-size: smaller;
19
+ color: #666666;
20
+ padding-left: 10px;
21
+ }
22
+
23
+ .hide_child_nodes > .call_tree_node {
24
+ display: none;
25
+ }
26
+
27
+ .nodes_not_shown {
28
+ visibility: hidden;
29
+ }
30
+
31
+ .hide_child_nodes > .nodes_not_shown {
32
+ visibility: visible;
33
+ }
34
+
35
+ .insignificant_calls > .nodes_not_shown {
36
+ visibility: visible;
37
+ }
38
+
39
+ .show_insignificant_calls > .nodes_not_shown {
40
+ visibility: hidden;
41
+ }
42
+
43
+ .filter {
44
+ display: none;
45
+ }
46
+ </style>
47
+ <script type="text/javascript" src="http://code.jquery.com/jquery-1.4.2.min.js"> </script>
48
+
49
+ <script type="text/javascript">
50
+ //<![CDATA[
51
+ CallTree = function(){
52
+ var callTree = {
53
+ click: function(node, event) {
54
+ $(node).toggleClass('hide_child_nodes');
55
+ event.stopPropagation();
56
+ return false;
57
+ $(node).children().toggleClass('red');
58
+
59
+ $(node).slideToggle();
60
+ },
61
+
62
+ filter: function(time) {
63
+ for (i=1; i<=time; i++) {
64
+ $.each(times[i], function(){ $(this).addClass('filter') });
65
+ }
66
+
67
+ for (i=time+1; i<=filterValue; i++) {
68
+ $.each(times[i], function(){ $(this).removeClass('filter') });
69
+ }
70
+
71
+ filterValue = time;
72
+ }
73
+ };
74
+
75
+ var filterValue = 0;
76
+
77
+ return callTree;
78
+ }();
79
+
80
+ times = {};
81
+
82
+ $(document).ready(function(){
83
+ $('.call_tree_node').each(function(){
84
+ time = this.attributes['time'].nodeValue;
85
+ if (!times[time]) { times[time] = []; }
86
+ times[time].push(this);
87
+ });
88
+ });
89
+ //]]>
90
+ </script>
91
+ </head>
92
+
93
+ <body>
94
+ <div id="controls">
95
+ <span>Time: <input type="text" maxlength="3" onchange="CallTree.filter(parseInt(this.value))"/></span>
96
+ </div>
97
+ <%= @result %>
98
+ </body>
99
+ </html>
@@ -0,0 +1,28 @@
1
+
2
+ module RubyProf
3
+ class CallTreeTextPrinter < CallTreeAbstractPrinter
4
+ def print(io)
5
+ io << "<main>\n"
6
+ super(io)
7
+ end
8
+
9
+ def print_methods(io, methods, parent_time=nil)
10
+ super(IndentedIo.new(io, 2), methods, parent_time)
11
+ end
12
+
13
+ def format_method(method)
14
+ "#{method.klass}::#{method.method}, #{method.time}, #{method.call_count}\n"
15
+ end
16
+
17
+ class IndentedIo
18
+ def initialize(io, indent)
19
+ @io = io
20
+ @indent = Array.new(indent, ' ').join
21
+ end
22
+
23
+ def <<(text)
24
+ @io << "#{@indent}#{text}"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,84 @@
1
+ require 'ruby-prof/abstract_printer'
2
+
3
+ module RubyProf
4
+ # Generate profiling information in calltree format
5
+ # for use by kcachegrind and similar tools.
6
+
7
+ class CallTreePrinter < AbstractPrinter
8
+ def print(output = STDOUT, options = {})
9
+ @output = output
10
+ setup_options(options)
11
+
12
+ # add a header - this information is somewhat arbitrary
13
+ @output << "events: "
14
+ case RubyProf.measure_mode
15
+ when RubyProf::PROCESS_TIME
16
+ @value_scale = RubyProf::CLOCKS_PER_SEC;
17
+ @output << 'process_time'
18
+ when RubyProf::WALL_TIME
19
+ @value_scale = 1_000_000
20
+ @output << 'wall_time'
21
+ when RubyProf.const_defined?(:CPU_TIME) && RubyProf::CPU_TIME
22
+ @value_scale = RubyProf.cpu_frequency
23
+ @output << 'cpu_time'
24
+ when RubyProf.const_defined?(:ALLOCATIONS) && RubyProf::ALLOCATIONS
25
+ @value_scale = 1
26
+ @output << 'allocations'
27
+ when RubyProf.const_defined?(:MEMORY) && RubyProf::MEMORY
28
+ @value_scale = 1
29
+ @output << 'memory'
30
+ when RubyProf.const_defined?(:GC_RUNS) && RubyProf::GC_RUNS
31
+ @value_scale = 1
32
+ @output << 'gc_runs'
33
+ when RubyProf.const_defined?(:GC_TIME) && RubyProf::GC_TIME
34
+ @value_scale = 1000000
35
+ @output << 'gc_time'
36
+ else
37
+ raise "Unknown measure mode: #{RubyProf.measure_mode}"
38
+ end
39
+ @output << "\n\n"
40
+
41
+ print_threads
42
+ end
43
+
44
+ def print_threads
45
+ @result.threads.each do |thread_id, methods|
46
+ print_methods(thread_id, methods)
47
+ end
48
+ end
49
+
50
+ def convert(value)
51
+ (value * @value_scale).round
52
+ end
53
+
54
+ def file(method)
55
+ File.expand_path(method.source_file)
56
+ end
57
+
58
+ def name(method)
59
+ "#{method.klass_name}::#{method.method_name}"
60
+ end
61
+
62
+ def print_methods(thread_id, methods)
63
+ methods.reverse_each do |method|
64
+ # Print out the file and method name
65
+ @output << "fl=#{file(method)}\n"
66
+ @output << "fn=#{name(method)}\n"
67
+
68
+ # Now print out the function line number and its self time
69
+ @output << "#{method.line} #{convert(method.self_time)}\n"
70
+
71
+ # Now print out all the children methods
72
+ method.children.each do |callee|
73
+ @output << "cfl=#{file(callee.target)}\n"
74
+ @output << "cfn=#{name(callee.target)}\n"
75
+ @output << "calls=#{callee.called} #{callee.line}\n"
76
+
77
+ # Print out total times here!
78
+ @output << "#{callee.line} #{convert(callee.total_time)}\n"
79
+ end
80
+ @output << "\n"
81
+ end
82
+ end #end print_methods
83
+ end # end class
84
+ end # end packages
@@ -0,0 +1,78 @@
1
+ require 'ruby-prof/abstract_printer'
2
+
3
+ module RubyProf
4
+ # Generates flat[link:files/examples/flat_txt.html] profile reports as text.
5
+ # To use the flat printer:
6
+ #
7
+ # result = RubyProf.profile do
8
+ # [code to profile]
9
+ # end
10
+ #
11
+ # printer = RubyProf::FlatPrinter.new(result)
12
+ # printer.print(STDOUT, 0)
13
+ #
14
+ class FlatPrinter < AbstractPrinter
15
+ # Print a flat profile report to the provided output.
16
+ #
17
+ # output - Any IO oject, including STDOUT or a file.
18
+ # The default value is STDOUT.
19
+ #
20
+ # options - Hash of print options. See #setup_options
21
+ # for more information.
22
+ #
23
+ def print(output = STDOUT, options = {})
24
+ @output = output
25
+ setup_options(options)
26
+ print_threads
27
+ end
28
+
29
+ private
30
+
31
+ def print_threads
32
+ @result.threads.each do |thread_id, methods|
33
+ print_methods(thread_id, methods)
34
+ @output << "\n" * 2
35
+ end
36
+ end
37
+
38
+ def print_methods(thread_id, methods)
39
+ # Get total time
40
+ toplevel = methods.max
41
+ total_time = toplevel.total_time
42
+ if total_time == 0
43
+ total_time = 0.01
44
+ end
45
+
46
+ # Now sort methods by largest self time,
47
+ # not total time like in other printouts
48
+ methods = methods.sort do |m1, m2|
49
+ m1.self_time <=> m2.self_time
50
+ end.reverse
51
+
52
+ @output << "Thread ID: %d\n" % thread_id
53
+ @output << "Total: %0.6f\n" % total_time
54
+ @output << "\n"
55
+ @output << " %self total self wait child calls name\n"
56
+
57
+ sum = 0
58
+ methods.each do |method|
59
+ self_percent = (method.self_time / total_time) * 100
60
+ next if self_percent < min_percent
61
+
62
+ sum += method.self_time
63
+ #self_time_called = method.called > 0 ? method.self_time/method.called : 0
64
+ #total_time_called = method.called > 0? method.total_time/method.called : 0
65
+
66
+ @output << "%6.2f %8.2f %8.2f %8.2f %8.2f %8d %s\n" % [
67
+ method.self_time / total_time * 100, # %self
68
+ method.total_time, # total
69
+ method.self_time, # self
70
+ method.wait_time, # wait
71
+ method.children_time, # children
72
+ method.called, # calls
73
+ method_name(method) # name
74
+ ]
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,72 @@
1
+ require 'ruby-prof/abstract_printer'
2
+ require 'pathname'
3
+ module RubyProf
4
+ # Generates flat[link:files/examples/flat_txt.html] profile reports as text.
5
+ # To use the flat printer with line numbers:
6
+ #
7
+ # result = RubyProf.profile do
8
+ # [code to profile]
9
+ # end
10
+ #
11
+ # printer = RubyProf::FlatPrinterWithLineNumbers.new(result)
12
+ # printer.print(STDOUT, 0)
13
+ #
14
+ class FlatPrinterWithLineNumbers < FlatPrinter
15
+
16
+ def print_methods(thread_id, methods)
17
+ # Get total time
18
+ toplevel = methods.max
19
+ total_time = toplevel.total_time
20
+ if total_time == 0
21
+ total_time = 0.01
22
+ end
23
+
24
+ # Now sort methods by largest self time,
25
+ # not total time like in other printouts
26
+ methods = methods.sort do |m1, m2|
27
+ m1.self_time <=> m2.self_time
28
+ end.reverse
29
+
30
+ @output << "Thread ID: %d\n" % thread_id
31
+ @output << "Total: %0.6f\n" % total_time
32
+ @output << "\n"
33
+ @output << " %self total self wait child calls name\n"
34
+ sum = 0
35
+ methods.each do |method|
36
+ self_percent = (method.self_time / total_time) * 100
37
+ next if self_percent < min_percent
38
+
39
+ sum += method.self_time
40
+ #self_time_called = method.called > 0 ? method.self_time/method.called : 0
41
+ #total_time_called = method.called > 0? method.total_time/method.called : 0
42
+
43
+ @output << "%6.2f %8.2f %8.2f %8.2f %8.2f %8d %s " % [
44
+ method.self_time / total_time * 100, # %self
45
+ method.total_time, # total
46
+ method.self_time, # self
47
+ method.wait_time, # wait
48
+ method.children_time, # children
49
+ method.called, # calls
50
+ method_name(method), # name
51
+ ]
52
+ if method.source_file != 'ruby_runtime'
53
+ @output << " %s:%s" % [File.expand_path(method.source_file), method.line]
54
+ end
55
+ @output << "\n\tcalled from: "
56
+
57
+ # make sure they're unique
58
+ method.call_infos.map{|ci|
59
+ if ci.parent && ci.parent.target.source_file != 'ruby_runtime'
60
+ [method_name(ci.parent.target), File.expand_path(ci.parent.target.source_file), ci.parent.target.line]
61
+ else
62
+ nil
63
+ end
64
+ }.compact.uniq.each{|args|
65
+ @output << " %s (%s:%s) " % args
66
+ }
67
+ @output << "\n\n"
68
+ end
69
+ end
70
+ end
71
+ end
72
+
@@ -0,0 +1,256 @@
1
+ require 'ruby-prof/abstract_printer'
2
+ require 'erb'
3
+
4
+ module RubyProf
5
+ # Generates graph[link:files/examples/graph_html.html] profile reports as html.
6
+ # To use the grap html printer:
7
+ #
8
+ # result = RubyProf.profile do
9
+ # [code to profile]
10
+ # end
11
+ #
12
+ # printer = RubyProf::GraphHtmlPrinter.new(result)
13
+ # printer.print(STDOUT, :min_percent=>0)
14
+ #
15
+ # The constructor takes two arguments. The first is
16
+ # a RubyProf::Result object generated from a profiling
17
+ # run. The second is the minimum %total (the methods
18
+ # total time divided by the overall total time) that
19
+ # a method must take for it to be printed out in
20
+ # the report. Use this parameter to eliminate methods
21
+ # that are not important to the overall profiling results.
22
+
23
+ class GraphHtmlPrinter < AbstractPrinter
24
+ include ERB::Util
25
+
26
+ PERCENTAGE_WIDTH = 8
27
+ TIME_WIDTH = 10
28
+ CALL_WIDTH = 20
29
+
30
+ # Create a GraphPrinter. Result is a RubyProf::Result
31
+ # object generated from a profiling run.
32
+ def initialize(result)
33
+ super(result)
34
+ @thread_times = Hash.new
35
+ calculate_thread_times
36
+ end
37
+
38
+ # Print a graph html report to the provided output.
39
+ #
40
+ # output - Any IO oject, including STDOUT or a file.
41
+ # The default value is STDOUT.
42
+ #
43
+ # options - Hash of print options. See #setup_options
44
+ # for more information.
45
+ #
46
+ def print(output = STDOUT, options = {})
47
+ @output = output
48
+ setup_options(options)
49
+
50
+ filename = options[:filename]
51
+ template = filename ? File.read(filename).untaint : (options[:template] || self.template)
52
+ _erbout = @output
53
+ erb = ERB.new(template, nil, nil)
54
+ erb.filename = filename
55
+ @output << erb.result(binding)
56
+ end
57
+
58
+ # These methods should be private but then ERB doesn't
59
+ # work. Turn off RDOC though
60
+ #--
61
+ def calculate_thread_times
62
+ # Cache thread times since this is an expensive
63
+ # operation with the required sorting
64
+ @result.threads.each do |thread_id, methods|
65
+ top = methods.max
66
+
67
+ thread_time = 0.01
68
+ thread_time = top.total_time if top.total_time > 0
69
+
70
+ @thread_times[thread_id] = thread_time
71
+ end
72
+ end
73
+
74
+ def thread_time(thread_id)
75
+ @thread_times[thread_id]
76
+ end
77
+
78
+ def total_percent(thread_id, method)
79
+ overall_time = self.thread_time(thread_id)
80
+ (method.total_time/overall_time) * 100
81
+ end
82
+
83
+ def self_percent(method)
84
+ overall_time = self.thread_time(method.thread_id)
85
+ (method.self_time/overall_time) * 100
86
+ end
87
+
88
+ # Creates a link to a method. Note that we do not create
89
+ # links to methods which are under the min_perecent
90
+ # specified by the user, since they will not be
91
+ # printed out.
92
+ def create_link(thread_id, method)
93
+ if self.total_percent(thread_id, method) < min_percent
94
+ # Just return name
95
+ h method.full_name
96
+ else
97
+ href = '#' + method_href(thread_id, method)
98
+ "<a href=\"#{href}\">#{h method.full_name}</a>"
99
+ end
100
+ end
101
+
102
+ def method_href(thread_id, method)
103
+ h(method.full_name.gsub(/[><#\.\?=:]/,"_") + "_" + thread_id.to_s)
104
+ end
105
+
106
+ def template
107
+ '
108
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
109
+ <html>
110
+ <head>
111
+ <style media="all" type="text/css">
112
+ table {
113
+ border-collapse: collapse;
114
+ border: 1px solid #CCC;
115
+ font-family: Verdana, Arial, Helvetica, sans-serif;
116
+ font-size: 9pt;
117
+ line-height: normal;
118
+ width: 100%;
119
+ }
120
+
121
+ th {
122
+ text-align: center;
123
+ border-top: 1px solid #FB7A31;
124
+ border-bottom: 1px solid #FB7A31;
125
+ background: #FFC;
126
+ padding: 0.3em;
127
+ border-left: 1px solid silver;
128
+ }
129
+
130
+ tr.break td {
131
+ border: 0;
132
+ border-top: 1px solid #FB7A31;
133
+ padding: 0;
134
+ margin: 0;
135
+ }
136
+
137
+ tr.method td {
138
+ font-weight: bold;
139
+ }
140
+
141
+ td {
142
+ padding: 0.3em;
143
+ }
144
+
145
+ td:first-child {
146
+ width: 190px;
147
+ }
148
+
149
+ td {
150
+ border-left: 1px solid #CCC;
151
+ text-align: center;
152
+ }
153
+
154
+ .method_name {
155
+ text-align: left;
156
+ }
157
+ </style>
158
+ </head>
159
+ <body>
160
+ <h1>Profile Report</h1>
161
+ <!-- Threads Table -->
162
+ <table>
163
+ <tr>
164
+ <th>Thread ID</th>
165
+ <th>Total Time</th>
166
+ </tr>
167
+ <% for thread_id, methods in @result.threads %>
168
+ <tr>
169
+ <td><a href="#<%= thread_id %>"><%= thread_id %></a></td>
170
+ <td><%= thread_time(thread_id) %></td>
171
+ </tr>
172
+ <% end %>
173
+ </table>
174
+
175
+ <!-- Methods Tables -->
176
+ <% for thread_id, methods in @result.threads
177
+ total_time = thread_time(thread_id) %>
178
+ <h2><a name="<%= thread_id %>">Thread <%= thread_id %></a></h2>
179
+
180
+ <table>
181
+ <tr>
182
+ <th><%= sprintf("%#{PERCENTAGE_WIDTH}s", "%Total") %></th>
183
+ <th><%= sprintf("%#{PERCENTAGE_WIDTH}s", "%Self") %></th>
184
+ <th><%= sprintf("%#{TIME_WIDTH}s", "Total") %></th>
185
+ <th><%= sprintf("%#{TIME_WIDTH}s", "Self") %></th>
186
+ <th><%= sprintf("%#{TIME_WIDTH}s", "Wait") %></th>
187
+ <th><%= sprintf("%#{TIME_WIDTH+2}s", "Child") %></th>
188
+ <th><%= sprintf("%#{CALL_WIDTH}s", "Calls") %></th>
189
+ <th class="method_name">Name</th>
190
+ <th>Line</th>
191
+ </tr>
192
+
193
+ <% min_time = @options[:min_time] || (@options[:nonzero] ? 0.005 : nil)
194
+ methods.sort.reverse_each do |method|
195
+ total_percentage = (method.total_time/total_time) * 100
196
+ next if total_percentage < min_percent
197
+ next if min_time && method.total_time < min_time
198
+ self_percentage = (method.self_time/total_time) * 100 %>
199
+
200
+ <!-- Parents -->
201
+ <% for caller in method.aggregate_parents.sort_by(&:total_time)
202
+ next unless caller.parent
203
+ next if min_time && caller.total_time < min_time %>
204
+ <tr>
205
+ <td>&nbsp;</td>
206
+ <td>&nbsp;</td>
207
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.total_time) %></td>
208
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.self_time) %></td>
209
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.wait_time) %></td>
210
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.children_time) %></td>
211
+ <% called = "#{caller.called}/#{method.called}" %>
212
+ <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
213
+ <td class="method_name"><%= create_link(thread_id, caller.parent.target) %></td>
214
+ <td><a href="file://<%=h srcfile=File.expand_path(caller.parent.target.source_file) %>#line=<%= linenum=caller.line %>" title="<%=h srcfile %>:<%= linenum %>"><%= caller.line %></a></td>
215
+ </tr>
216
+ <% end %>
217
+
218
+ <tr class="method">
219
+ <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", total_percentage) %></td>
220
+ <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", self_percentage) %></td>
221
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.total_time) %></td>
222
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.self_time) %></td>
223
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.wait_time) %></td>
224
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.children_time) %></td>
225
+ <td><%= sprintf("%#{CALL_WIDTH}i", method.called) %></td>
226
+ <td class="method_name"><a name="<%= method_href(thread_id, method) %>"><%= h method.full_name %></a></td>
227
+ <td><a href="file://<%=h srcfile=File.expand_path(method.source_file) %>#line=<%= linenum=method.line %>" title="<%=h srcfile %>:<%= linenum %>"><%= method.line %></a></td>
228
+ </tr>
229
+
230
+ <!-- Children -->
231
+ <% for callee in method.aggregate_children.sort_by(&:total_time).reverse %>
232
+ <% next if min_time && callee.total_time < min_time %>
233
+ <tr>
234
+ <td>&nbsp;</td>
235
+ <td>&nbsp;</td>
236
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.total_time) %></td>
237
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.self_time) %></td>
238
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.wait_time) %></td>
239
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.children_time) %></td>
240
+ <% called = "#{callee.called}/#{callee.target.called}" %>
241
+ <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
242
+ <td class="method_name"><%= create_link(thread_id, callee.target) %></td>
243
+ <td><a href="file://<%=h srcfile=File.expand_path(method.source_file) %>#line=<%= linenum=callee.line %>" title="<%=h srcfile %>:<%= linenum %>"><%= callee.line %></a></td>
244
+ </tr>
245
+ <% end %>
246
+ <!-- Create divider row -->
247
+ <tr class="break"><td colspan="9"></td></tr>
248
+ <% end %>
249
+ </table>
250
+ <% end %>
251
+ </body>
252
+ </html>'
253
+ end
254
+ end
255
+ end
256
+