rdp-ruby-prof 0.7.4

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 (64) hide show
  1. data/CHANGES +202 -0
  2. data/LICENSE +23 -0
  3. data/README +445 -0
  4. data/Rakefile +123 -0
  5. data/bin/ruby-prof +207 -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.c# +1679 -0
  10. data/ext/Makefile +180 -0
  11. data/ext/extconf.rb +40 -0
  12. data/ext/measure_allocations.h +58 -0
  13. data/ext/measure_cpu_time.h +152 -0
  14. data/ext/measure_gc_runs.h +76 -0
  15. data/ext/measure_gc_time.h +57 -0
  16. data/ext/measure_memory.h +101 -0
  17. data/ext/measure_process_time.h +52 -0
  18. data/ext/measure_wall_time.h +53 -0
  19. data/ext/mingw/Rakefile +23 -0
  20. data/ext/mingw/build.rake +38 -0
  21. data/ext/ruby_prof.c +1707 -0
  22. data/ext/ruby_prof.e +19984 -0
  23. data/ext/ruby_prof.h +188 -0
  24. data/ext/vc/ruby_prof.sln +20 -0
  25. data/ext/vc/ruby_prof.vcproj +241 -0
  26. data/ext/version.h +4 -0
  27. data/lib/ruby-prof.rb +48 -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_printer.rb +84 -0
  32. data/lib/ruby-prof/flat_printer.rb +79 -0
  33. data/lib/ruby-prof/graph_html_printer.rb +256 -0
  34. data/lib/ruby-prof/graph_html_printer.rb.orig +256 -0
  35. data/lib/ruby-prof/graph_html_printer.rb.rej +34 -0
  36. data/lib/ruby-prof/graph_printer.rb +164 -0
  37. data/lib/ruby-prof/graph_printer.rb.orig +164 -0
  38. data/lib/ruby-prof/method_info.rb +111 -0
  39. data/lib/ruby-prof/task.rb +146 -0
  40. data/lib/ruby-prof/test.rb +148 -0
  41. data/lib/unprof.rb +8 -0
  42. data/rails/environment/profile.rb +24 -0
  43. data/rails/example/example_test.rb +9 -0
  44. data/rails/profile_test_helper.rb +21 -0
  45. data/test/aggregate_test.rb +121 -0
  46. data/test/basic_test.rb +283 -0
  47. data/test/duplicate_names_test.rb +32 -0
  48. data/test/exceptions_test.rb +15 -0
  49. data/test/exclude_threads_test.rb +54 -0
  50. data/test/line_number_test.rb +73 -0
  51. data/test/measurement_test.rb +121 -0
  52. data/test/module_test.rb +54 -0
  53. data/test/no_method_class_test.rb +13 -0
  54. data/test/prime.rb +58 -0
  55. data/test/prime_test.rb +13 -0
  56. data/test/printers_test.rb +71 -0
  57. data/test/recursive_test.rb +254 -0
  58. data/test/singleton_test.rb +37 -0
  59. data/test/stack_test.rb +138 -0
  60. data/test/start_stop_test.rb +95 -0
  61. data/test/test_suite.rb +23 -0
  62. data/test/thread_test.rb +159 -0
  63. data/test/unique_call_path_test.rb +206 -0
  64. metadata +124 -0
@@ -0,0 +1,4 @@
1
+ #define RUBY_PROF_VERSION "0.7.4"
2
+ #define RUBY_PROF_VERSION_MAJ 0
3
+ #define RUBY_PROF_VERSION_MIN 7
4
+ #define RUBY_PROF_VERSION_MIC 4
@@ -0,0 +1,48 @@
1
+ require "ruby_prof.so"
2
+
3
+ require "ruby-prof/method_info"
4
+ require "ruby-prof/call_info"
5
+ require "ruby-prof/aggregate_call_info"
6
+ require "ruby-prof/flat_printer"
7
+ require "ruby-prof/graph_printer"
8
+ require "ruby-prof/graph_html_printer"
9
+ require "ruby-prof/call_tree_printer"
10
+
11
+ require "ruby-prof/test"
12
+
13
+ module RubyProf
14
+ # See if the user specified the clock mode via
15
+ # the RUBY_PROF_MEASURE_MODE environment variable
16
+ def self.figure_measure_mode
17
+ case ENV["RUBY_PROF_MEASURE_MODE"]
18
+ when "wall" || "wall_time"
19
+ RubyProf.measure_mode = RubyProf::WALL_TIME
20
+ when "cpu" || "cpu_time"
21
+ if ENV.key?("RUBY_PROF_CPU_FREQUENCY")
22
+ RubyProf.cpu_frequency = ENV["RUBY_PROF_CPU_FREQUENCY"].to_f
23
+ else
24
+ begin
25
+ open("/proc/cpuinfo") do |f|
26
+ f.each_line do |line|
27
+ s = line.slice(/cpu MHz\s*:\s*(.*)/, 1)
28
+ if s
29
+ RubyProf.cpu_frequency = s.to_f * 1000000
30
+ break
31
+ end
32
+ end
33
+ end
34
+ rescue Errno::ENOENT
35
+ end
36
+ end
37
+ RubyProf.measure_mode = RubyProf::CPU_TIME
38
+ when "allocations"
39
+ RubyProf.measure_mode = RubyProf::ALLOCATIONS
40
+ when "memory"
41
+ RubyProf.measure_mode = RubyProf::MEMORY
42
+ else
43
+ RubyProf.measure_mode = RubyProf::PROCESS_TIME
44
+ end
45
+ end
46
+ end
47
+
48
+ RubyProf::figure_measure_mode
@@ -0,0 +1,41 @@
1
+ module RubyProf
2
+ class AbstractPrinter
3
+ def initialize(result)
4
+ @result = result
5
+ @output = nil
6
+ @options = {}
7
+ end
8
+
9
+ # Specify print options.
10
+ #
11
+ # options - Hash table
12
+ # :min_percent - Number 0 to 100 that specifes the minimum
13
+ # %self (the methods self time divided by the
14
+ # overall total time) that a method must take
15
+ # for it to be printed out in the report.
16
+ # Default value is 0.
17
+ #
18
+ # :print_file - True or false. Specifies if a method's source
19
+ # file should be printed. Default value if false.
20
+ #
21
+ def setup_options(options = {})
22
+ @options = options
23
+ end
24
+
25
+ def min_percent
26
+ @options[:min_percent] || 0
27
+ end
28
+
29
+ def print_file
30
+ @options[:print_file] || false
31
+ end
32
+
33
+ def method_name(method)
34
+ name = method.full_name
35
+ if print_file
36
+ name += " (#{method.source_file}:#{method.line}}"
37
+ end
38
+ name
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,62 @@
1
+ module RubyProf
2
+ class AggregateCallInfo
3
+ attr_reader :call_infos
4
+ def initialize(call_infos)
5
+ if call_infos.length == 0
6
+ raise(ArgumentError, "Must specify at least one call info.")
7
+ end
8
+ @call_infos = call_infos
9
+ end
10
+
11
+ def target
12
+ call_infos.first.target
13
+ end
14
+
15
+ def parent
16
+ call_infos.first.parent
17
+ end
18
+
19
+ def line
20
+ call_infos.first.line
21
+ end
22
+
23
+ def children
24
+ call_infos.inject(Array.new) do |result, call_info|
25
+ result.concat(call_info.children)
26
+ end
27
+ end
28
+
29
+ def total_time
30
+ aggregate(:total_time)
31
+ end
32
+
33
+ def self_time
34
+ aggregate(:self_time)
35
+ end
36
+
37
+ def wait_time
38
+ aggregate(:wait_time)
39
+ end
40
+
41
+ def children_time
42
+ aggregate(:children_time)
43
+ end
44
+
45
+ def called
46
+ aggregate(:called)
47
+ end
48
+
49
+ def to_s
50
+ "#{call_infos.first.full_name}"
51
+ end
52
+
53
+ private
54
+
55
+ def aggregate(method_name)
56
+ self.call_infos.inject(0) do |sum, call_info|
57
+ sum += call_info.send(method_name)
58
+ sum
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,47 @@
1
+ module RubyProf
2
+ class CallInfo
3
+ def depth
4
+ result = 0
5
+ call_info = self.parent
6
+
7
+ while call_info
8
+ result += 1
9
+ call_info = call_info.parent
10
+ end
11
+ result
12
+ end
13
+
14
+ def children_time
15
+ children.inject(0) do |sum, call_info|
16
+ sum += call_info.total_time
17
+ end
18
+ end
19
+
20
+ def stack
21
+ @stack ||= begin
22
+ methods = Array.new
23
+ call_info = self
24
+
25
+ while call_info
26
+ methods << call_info.target
27
+ call_info = call_info.parent
28
+ end
29
+ methods.reverse
30
+ end
31
+ end
32
+
33
+ def call_sequence
34
+ @call_sequence ||= begin
35
+ stack.map {|method| method.full_name}.join('->')
36
+ end
37
+ end
38
+
39
+ def root?
40
+ self.parent.nil?
41
+ end
42
+
43
+ def to_s
44
+ "#{call_sequence}"
45
+ end
46
+ end
47
+ 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,79 @@
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.sort.last
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
79
+
@@ -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.sort.last
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
+