ruby-prof 0.4.1-mswin32 → 0.5.0-mswin32

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 (58) hide show
  1. data/CHANGES +30 -0
  2. data/README +65 -25
  3. data/Rakefile +33 -32
  4. data/bin/ruby-prof +100 -83
  5. data/examples/graph.html +65 -69
  6. data/ext/measure_allocations.h +43 -0
  7. data/ext/measure_cpu_time.h +138 -0
  8. data/ext/measure_process_time.h +41 -0
  9. data/ext/measure_wall_time.h +42 -0
  10. data/ext/ruby_prof.c +737 -653
  11. data/lib/ruby-prof.rb +41 -38
  12. data/lib/ruby-prof/abstract_printer.rb +42 -0
  13. data/lib/ruby-prof/call_tree_printer.rb +69 -0
  14. data/lib/ruby-prof/flat_printer.rb +78 -75
  15. data/lib/ruby-prof/graph_html_printer.rb +241 -228
  16. data/lib/ruby-prof/graph_printer.rb +160 -141
  17. data/lib/ruby-prof/profile_test_case.rb +80 -0
  18. data/lib/ruby-prof/rails_plugin/ruby-prof/init.rb +6 -0
  19. data/lib/ruby-prof/rails_plugin/ruby-prof/lib/profiling.rb +52 -0
  20. data/lib/ruby-prof/task.rb +147 -0
  21. data/lib/ruby_prof.so +0 -0
  22. data/test/basic_test.rb +65 -35
  23. data/test/duplicate_names_test.rb +20 -24
  24. data/test/gc.log +5 -0
  25. data/test/measure_mode_test.rb +79 -0
  26. data/test/module_test.rb +31 -18
  27. data/test/no_method_class_test.rb +14 -0
  28. data/test/prime1.rb +17 -0
  29. data/test/prime2.rb +26 -0
  30. data/test/prime3.rb +17 -0
  31. data/test/prime_test.rb +10 -10
  32. data/test/printers_test.rb +14 -12
  33. data/test/profile_unit_test.rb +24 -0
  34. data/test/recursive_test.rb +105 -17
  35. data/test/singleton_test.rb +38 -0
  36. data/test/start_test.rb +24 -0
  37. data/test/test_helper.rb +33 -29
  38. data/test/test_suite.rb +10 -2
  39. data/test/thread_test.rb +123 -17
  40. data/test/timing_test.rb +70 -29
  41. metadata +28 -30
  42. data/doc/created.rid +0 -1
  43. data/doc/files/LICENSE.html +0 -0
  44. data/doc/files/README.html +0 -376
  45. data/doc/files/bin/ruby-prof.html +0 -143
  46. data/doc/files/examples/flat_txt.html +0 -179
  47. data/doc/files/examples/graph_html.html +0 -948
  48. data/doc/files/examples/graph_txt.html +0 -297
  49. data/doc/files/ext/ruby_prof_c.html +0 -101
  50. data/doc/files/lib/ruby-prof/flat_printer_rb.html +0 -101
  51. data/doc/files/lib/ruby-prof/graph_html_printer_rb.html +0 -108
  52. data/doc/files/lib/ruby-prof/graph_printer_rb.html +0 -101
  53. data/doc/files/lib/ruby-prof/profiletask_rb.html +0 -109
  54. data/doc/files/lib/ruby-prof_rb.html +0 -111
  55. data/doc/files/lib/unprof_rb.html +0 -108
  56. data/doc/rdoc-style.css +0 -208
  57. data/lib/ruby-prof/profiletask.rb +0 -150
  58. data/test/clock_mode_test.rb +0 -73
data/lib/ruby-prof.rb CHANGED
@@ -1,38 +1,41 @@
1
- require "ruby_prof.so"
2
-
3
- require "ruby-prof/flat_printer"
4
- require "ruby-prof/graph_printer"
5
- require "ruby-prof/graph_html_printer"
6
-
7
- module RubyProf
8
- # See if the user specified the clock mode via
9
- # the RUBY_PROF_CLOCK_MODE environment variable
10
- def self.figure_clock_mode
11
- case ENV["RUBY_PROF_CLOCK_MODE"]
12
- when "wall" || "wall_time"
13
- RubyProf.clock_mode = RubyProf::WALL_TIME
14
- when "cpu" || "cpu_time"
15
- if ENV.key?("RUBY_PROF_CPU_FREQUENCY")
16
- RubyProf.cpu_frequency = ENV["RUBY_PROF_CPU_FREQUENCY"].to_f
17
- else
18
- begin
19
- open("/proc/cpuinfo") do |f|
20
- f.each_line do |line|
21
- s = line.slice(/cpu MHz\s*:\s*(.*)/, 1)
22
- if s
23
- RubyProf.cpu_frequency = s.to_f * 1000000
24
- break
25
- end
26
- end
27
- end
28
- rescue Errno::ENOENT
29
- end
30
- end
31
- RubyProf.clock_mode = RubyProf::CPU_TIME
32
- else
33
- RubyProf.clock_mode = RubyProf::PROCESS_TIME
34
- end
35
- end
36
- end
37
-
38
- RubyProf::figure_clock_mode
1
+ require "ruby_prof.so"
2
+
3
+ require "ruby-prof/flat_printer"
4
+ require "ruby-prof/graph_printer"
5
+ require "ruby-prof/graph_html_printer"
6
+ require "ruby-prof/call_tree_printer"
7
+
8
+ module RubyProf
9
+ # See if the user specified the clock mode via
10
+ # the RUBY_PROF_MEASURE_MODE environment variable
11
+ def self.figure_measure_mode
12
+ case ENV["RUBY_PROF_MEASURE_MODE"]
13
+ when "wall" || "wall_time"
14
+ RubyProf.measure_mode = RubyProf::WALL_TIME
15
+ when "cpu" || "cpu_time"
16
+ if ENV.key?("RUBY_PROF_CPU_FREQUENCY")
17
+ RubyProf.cpu_frequency = ENV["RUBY_PROF_CPU_FREQUENCY"].to_f
18
+ else
19
+ begin
20
+ open("/proc/cpuinfo") do |f|
21
+ f.each_line do |line|
22
+ s = line.slice(/cpu MHz\s*:\s*(.*)/, 1)
23
+ if s
24
+ RubyProf.cpu_frequency = s.to_f * 1000000
25
+ break
26
+ end
27
+ end
28
+ end
29
+ rescue Errno::ENOENT
30
+ end
31
+ end
32
+ RubyProf.measure_mode = RubyProf::CPU_TIME
33
+ when "allocations"
34
+ RubyProf.measure_mode = RubyProf::ALLOCATIONS
35
+ else
36
+ RubyProf.measure_mode = RubyProf::PROCESS_TIME
37
+ end
38
+ end
39
+ end
40
+
41
+ RubyProf::figure_measure_mode
@@ -0,0 +1,42 @@
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
+
41
+ end
42
+ end
@@ -0,0 +1,69 @@
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
+ @output << 'process_time'
17
+ when RubyProf::WALL_TIME
18
+ @output << 'wall_time'
19
+ when RubyProf::CPU_TIME
20
+ @output << 'cpu_time'
21
+ when RubyProf::ALLOCATIONS
22
+ @output << 'allocations'
23
+ end
24
+ @output << "\n\n"
25
+
26
+ print_threads
27
+ end
28
+
29
+ def print_threads
30
+ @result.threads.each do |thread_id, methods|
31
+ print_methods(thread_id ,methods)
32
+ end
33
+ end
34
+
35
+ def convert(value)
36
+ (value * 1000).round
37
+ end
38
+
39
+ def file(method)
40
+ File.expand_path(method.source_file)
41
+ end
42
+
43
+ def name(method)
44
+ "#{method.klass_name}::#{method.method_name}"
45
+ end
46
+
47
+ def print_methods(thread_id, methods)
48
+ methods.reverse_each do |method|
49
+ # Print out the file and method name
50
+ @output << "fl=#{file(method)}\n"
51
+ @output << "fn=#{name(method)}\n"
52
+
53
+ # Now print out the function line number and its self time
54
+ @output << "#{method.line} #{convert(method.self_time)}\n"
55
+
56
+ # Now print out all the children methods
57
+ method.children.each do |callee|
58
+ @output << "cfl=#{file(callee.target)}\n"
59
+ @output << "cfn=#{name(callee.target)}\n"
60
+ @output << "calls=#{callee.called} #{callee.line}\n"
61
+
62
+ # Print out total times here!
63
+ @output << "#{callee.line} #{convert(callee.total_time)}\n"
64
+ end
65
+ @output << "\n"
66
+ end
67
+ end #end print_methods
68
+ end # end class
69
+ end # end packages
@@ -1,75 +1,78 @@
1
- module RubyProf
2
- # Generates flat[link:files/examples/flat_txt.html] profile reports as text.
3
- # To use the flat printer:
4
- #
5
- # result = RubyProf.profile do
6
- # [code to profile]
7
- # end
8
- #
9
- # printer = RubyProf::FlatPrinter.new(result)
10
- # printer.print(STDOUT, 0)
11
- #
12
- class FlatPrinter
13
- # Create a FlatPrinter. Result is a RubyProf::Result
14
- # object generated from a profiling run.
15
- def initialize(result)
16
- @result = result
17
- end
18
-
19
- # Print a flat profile report to the provided output.
20
- #
21
- # output - Any IO oject, including STDOUT or a file.
22
- # The default value is STDOUT.
23
- #
24
- # min_percent - The minimum %self (the methods
25
- # self time divided by the overall total time) that
26
- # a method must take for it to be printed out in
27
- # the report. Default value is 0.
28
- def print(output = STDOUT, min_percent = 0)
29
- @min_percent = min_percent
30
- @output = output
31
- print_threads
32
- end
33
-
34
- private
35
-
36
- def print_threads
37
- # sort assumes that spawned threads have higher object_ids
38
- @result.threads.sort.each do |thread_id, methods|
39
- print_methods(thread_id, methods)
40
- @output << "\n" * 2
41
- end
42
- end
43
-
44
- def print_methods(thread_id, methods)
45
- toplevel = @result.toplevel(thread_id)
46
- total_time = toplevel.total_time
47
-
48
-
49
- sum = 0
50
- @output << "Thread ID: " << thread_id << "\n"
51
- @output << " %self cumulative total self children calls self/call total/call name\n"
52
-
53
- methods.sort.reverse.each do |pair|
54
- method_name = pair[0]
55
- method = pair[1]
56
-
57
- self_percent = (method.self_time / total_time) * 100
58
- next if self_percent < @min_percent
59
-
60
- sum += method.self_time
61
- @output.printf("%6.2f %8.2f %8.2f %8.2f %8.2f %8d %8.2f %8.2f %s\n",
62
- method.self_time / total_time * 100, # %self
63
- sum, # cumulative
64
- method.total_time, # total
65
- method.self_time, # self
66
- method.children_time, # children
67
- method.called, # calls
68
- method.self_time / method.called, # self/call
69
- method.total_time / method.called, # total/call
70
- method_name) # name
71
- end
72
- end
73
- end
74
- end
75
-
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: " << thread_id << "\n"
53
+ @output << "Total: " << total_time << "\n"
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.printf("%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
+ end
75
+ end
76
+ end
77
+ end
78
+
@@ -1,228 +1,241 @@
1
- require "erb"
2
-
3
- module RubyProf
4
- # Generates graph[link:files/examples/graph_html.html] profile reports as html.
5
- # To use the grap html printer:
6
- #
7
- # result = RubyProf.profile do
8
- # [code to profile]
9
- # end
10
- #
11
- # printer = RubyProf::GraphHtmlPrinter.new(result, 5)
12
- # printer.print(STDOUT, 0)
13
- #
14
- # The constructor takes two arguments. The first is
15
- # a RubyProf::Result object generated from a profiling
16
- # run. The second is the minimum %total (the methods
17
- # total time divided by the overall total time) that
18
- # a method must take for it to be printed out in
19
- # the report. Use this parameter to eliminate methods
20
- # that are not important to the overall profiling results.
21
-
22
- class GraphHtmlPrinter
23
- PERCENTAGE_WIDTH = 8
24
- TIME_WIDTH = 10
25
- CALL_WIDTH = 20
26
-
27
- # Create a GraphPrinter. Result is a RubyProf::Result
28
- # object generated from a profiling run.
29
- def initialize(result)
30
- @result = result
31
- end
32
-
33
- # Print a graph html report to the provided output.
34
- #
35
- # output - Any IO oject, including STDOUT or a file.
36
- # The default value is STDOUT.
37
- #
38
- # min_percent - The minimum %total (the methods
39
- # total time divided by the overall total time) that
40
- # a method must take for it to be printed out in
41
- # the report. Default value is 0.
42
- def print(output = STDOUT, min_percent = 0)
43
- @output = output
44
- @min_percent = min_percent
45
-
46
- _erbout = @output
47
- erb = ERB.new(template, nil, nil)
48
- @output << erb.result(binding)
49
- end
50
-
51
- # These methods should be private but then ERB doesn't
52
- # work. Turn off RDOC though
53
- #--
54
- def total_time(thread_id)
55
- toplevel = @result.toplevel(thread_id)
56
- total_time = toplevel.total_time
57
- total_time = 0.01 if total_time == 0
58
- return total_time
59
- end
60
-
61
- def total_percent(method)
62
- overall_time = self.total_time(method.thread_id)
63
- (method.total_time/overall_time) * 100
64
- end
65
-
66
- def self_percent(method)
67
- overall_time = self.total_time(method.thread_id)
68
- (method.self_time/overall_time) * 100
69
- end
70
-
71
- # Creates a link to a method. Note that we do not create
72
- # links to methods which are under the min_perecent
73
- # specified by the user, since they will not be
74
- # printed out.
75
- def create_link(thread_id, name)
76
- # Get method
77
- method = @result.threads[thread_id][name]
78
-
79
- if self.total_percent(method) < @min_percent
80
- # Just return name
81
- name
82
- else
83
- # Create link
84
- "<a href=\"##{link_name(thread_id, name)}\">#{name}</a>"
85
- end
86
- end
87
-
88
- def link_name(thread_id, name)\
89
- name.gsub(/[><#\.\?=:]/,"_") + "_" + thread_id.to_s
90
- end
91
-
92
- def template
93
- '
94
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
95
- <html>
96
- <head>
97
- <style media="all" type="text/css">
98
- table {
99
- border-collapse: collapse;
100
- border: 1px solid #CCC;
101
- font-family: Verdana, Arial, Helvetica, sans-serif;
102
- font-size: 9pt;
103
- line-height: normal;
104
- }
105
-
106
- th {
107
- text-align: center;
108
- border-top: 1px solid #FB7A31;
109
- border-bottom: 1px solid #FB7A31;
110
- background: #FFC;
111
- padding: 0.3em;
112
- border-left: 1px solid silver;
113
- }
114
-
115
- tr.break td {
116
- border: 0;
117
- border-top: 1px solid #FB7A31;
118
- padding: 0;
119
- margin: 0;
120
- }
121
-
122
- tr.method td {
123
- font-weight: bold;
124
- }
125
-
126
- td {
127
- padding: 0.3em;
128
- }
129
-
130
- td:first-child {
131
- width: 190px;
132
- }
133
-
134
- td {
135
- border-left: 1px solid #CCC;
136
- text-align: center;
137
- }
138
- </style>
139
- </head>
140
- <body>
141
- <h1>Profile Report</h1>
142
- <!-- Threads Table -->
143
- <table>
144
- <tr>
145
- <th>Thread ID</th>
146
- <th>Total Time</th>
147
- </tr>
148
- <% for thread_id, methods in @result.threads %>
149
- <tr>
150
- <td><a href="#<%= thread_id %>"><%= thread_id %></a></td>
151
- <td><%= @result.toplevel(thread_id).total_time %></td>
152
- </tr>
153
- <% end %>
154
- </table>
155
-
156
- <!-- Methods Tables -->
157
- <% for thread_id, methods in @result.threads %>
158
- <h2><a name="<%= thread_id %>">Thread <%= thread_id %></a></h2>
159
-
160
- <table>
161
- <tr>
162
- <th><%= sprintf("%#{PERCENTAGE_WIDTH}s", "%Total") %></th>
163
- <th><%= sprintf("%#{PERCENTAGE_WIDTH}s", "%Self") %></th>
164
- <th><%= sprintf("%#{TIME_WIDTH}s", "Total") %></th>
165
- <th><%= sprintf("%#{TIME_WIDTH}s", "Self") %></th>
166
- <th><%= sprintf("%#{TIME_WIDTH+2}s", "Children") %></th>
167
- <th><%= sprintf("%#{CALL_WIDTH}s", "Calls") %></th>
168
- <th>Name</th>
169
- </tr>
170
-
171
- <% methods.sort.reverse.each do |pair| %>
172
- <% name = pair[0] %>
173
- <% method = pair[1] %>
174
- <% method_total_percent = self.total_percent(method) %>
175
- <% next if method_total_percent < @min_percent %>
176
- <% method_self_percent = self.self_percent(method) %>
177
-
178
- <!-- Parents -->
179
- <% for name, call_info in method.parents %>
180
- <tr>
181
- <td>&nbsp;</td>
182
- <td>&nbsp;</td>
183
- <td><%= sprintf("%#{TIME_WIDTH}.2f", call_info.total_time) %></td>
184
- <td><%= sprintf("%#{TIME_WIDTH}.2f", call_info.self_time) %></td>
185
- <td><%= sprintf("%#{TIME_WIDTH}.2f", call_info.children_time) %></td>
186
- <% called = "#{call_info.called}/#{method.called}" %>
187
- <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
188
- <td><%= create_link(thread_id, name) %></td>
189
- </tr>
190
- <% end %>
191
-
192
- <tr class="method">
193
- <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", method_total_percent) %></td>
194
- <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", method_self_percent) %></td>
195
- <td><%= sprintf("%#{TIME_WIDTH}.2f", method.total_time) %></td>
196
- <td><%= sprintf("%#{TIME_WIDTH}.2f", method.self_time) %></td>
197
- <td><%= sprintf("%#{TIME_WIDTH}.2f", method.children_time) %></td>
198
- <td><%= sprintf("%#{CALL_WIDTH}i", method.called) %></td>
199
- <td><a name="<%= link_name(thread_id, method.name) %>"><%= method.name %></a></td>
200
- </tr>
201
-
202
- <!-- Children -->
203
- <% for name, call_info in method.children %>
204
- <% methods = @result.threads[thread_id] %>
205
- <% child = methods[name] %>
206
-
207
- <tr>
208
- <td>&nbsp;</td>
209
- <td>&nbsp;</td>
210
- <td><%= sprintf("%#{TIME_WIDTH}.2f", call_info.total_time) %></td>
211
- <td><%= sprintf("%#{TIME_WIDTH}.2f", call_info.self_time) %></td>
212
- <td><%= sprintf("%#{TIME_WIDTH}.2f", call_info.children_time) %></td>
213
- <% called = "#{call_info.called}/#{child.called}" %>
214
- <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
215
- <td><%= create_link(thread_id, name) %></td>
216
- </tr>
217
- <% end %>
218
- <!-- Create divider row -->
219
- <tr class="break"><td colspan="7"></td></tr>
220
- <% end %>
221
- </table>
222
- <% end %>
223
- </body>
224
- </html>'
225
- end
226
- end
227
- end
228
-
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, 5)
13
+ # printer.print(STDOUT, 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
+ PERCENTAGE_WIDTH = 8
25
+ TIME_WIDTH = 10
26
+ CALL_WIDTH = 20
27
+
28
+ # Create a GraphPrinter. Result is a RubyProf::Result
29
+ # object generated from a profiling run.
30
+ def initialize(result)
31
+ super(result)
32
+ @thread_times = Hash.new
33
+ calculate_thread_times
34
+ end
35
+
36
+ # Print a graph html report to the provided output.
37
+ #
38
+ # output - Any IO oject, including STDOUT or a file.
39
+ # The default value is STDOUT.
40
+ #
41
+ # options - Hash of print options. See #setup_options
42
+ # for more information.
43
+ #
44
+ def print(output = STDOUT, options = {})
45
+ @output = output
46
+ setup_options(options)
47
+
48
+ _erbout = @output
49
+ erb = ERB.new(template, nil, nil)
50
+ @output << erb.result(binding)
51
+ end
52
+
53
+ # These methods should be private but then ERB doesn't
54
+ # work. Turn off RDOC though
55
+ #--
56
+ def calculate_thread_times
57
+ # Cache thread times since this is an expensive
58
+ # operation with the required sorting
59
+ @result.threads.each do |thread_id, methods|
60
+ top = methods.sort.last
61
+
62
+ thread_time = 0.01
63
+ thread_time = top.total_time if top.total_time > 0
64
+
65
+ @thread_times[thread_id] = thread_time
66
+ end
67
+ end
68
+
69
+ def thread_time(thread_id)
70
+ @thread_times[thread_id]
71
+ end
72
+
73
+ def total_percent(thread_id, method)
74
+ overall_time = self.thread_time(thread_id)
75
+ (method.total_time/overall_time) * 100
76
+ end
77
+
78
+ def self_percent(method)
79
+ overall_time = self.thread_time(method.thread_id)
80
+ (method.self_time/overall_time) * 100
81
+ end
82
+
83
+ # Creates a link to a method. Note that we do not create
84
+ # links to methods which are under the min_perecent
85
+ # specified by the user, since they will not be
86
+ # printed out.
87
+ def create_link(thread_id, method)
88
+ if self.total_percent(thread_id, method) < min_percent
89
+ # Just return name
90
+ method.full_name
91
+ else
92
+ href = '#' + method_href(thread_id, method)
93
+ "<a href=\"#{href}\">#{method.full_name}</a>"
94
+ end
95
+ end
96
+
97
+ def method_href(thread_id, method)
98
+ method.full_name.gsub(/[><#\.\?=:]/,"_") + "_" + thread_id.to_s
99
+ end
100
+
101
+ def template
102
+ '
103
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
104
+ <html>
105
+ <head>
106
+ <style media="all" type="text/css">
107
+ table {
108
+ border-collapse: collapse;
109
+ border: 1px solid #CCC;
110
+ font-family: Verdana, Arial, Helvetica, sans-serif;
111
+ font-size: 9pt;
112
+ line-height: normal;
113
+ }
114
+
115
+ th {
116
+ text-align: center;
117
+ border-top: 1px solid #FB7A31;
118
+ border-bottom: 1px solid #FB7A31;
119
+ background: #FFC;
120
+ padding: 0.3em;
121
+ border-left: 1px solid silver;
122
+ }
123
+
124
+ tr.break td {
125
+ border: 0;
126
+ border-top: 1px solid #FB7A31;
127
+ padding: 0;
128
+ margin: 0;
129
+ }
130
+
131
+ tr.method td {
132
+ font-weight: bold;
133
+ }
134
+
135
+ td {
136
+ padding: 0.3em;
137
+ }
138
+
139
+ td:first-child {
140
+ width: 190px;
141
+ }
142
+
143
+ td {
144
+ border-left: 1px solid #CCC;
145
+ text-align: center;
146
+ }
147
+ </style>
148
+ </head>
149
+ <body>
150
+ <h1>Profile Report</h1>
151
+ <!-- Threads Table -->
152
+ <table>
153
+ <tr>
154
+ <th>Thread ID</th>
155
+ <th>Total Time</th>
156
+ </tr>
157
+ <% for thread_id, methods in @result.threads %>
158
+ <tr>
159
+ <td><a href="#<%= thread_id %>"><%= thread_id %></a></td>
160
+ <td><%= thread_time(thread_id) %></td>
161
+ </tr>
162
+ <% end %>
163
+ </table>
164
+
165
+ <!-- Methods Tables -->
166
+ <% for thread_id, methods in @result.threads
167
+ total_time = thread_time(thread_id) %>
168
+ <h2><a name="<%= thread_id %>">Thread <%= thread_id %></a></h2>
169
+
170
+ <table>
171
+ <tr>
172
+ <th><%= sprintf("%#{PERCENTAGE_WIDTH}s", "%Total") %></th>
173
+ <th><%= sprintf("%#{PERCENTAGE_WIDTH}s", "%Self") %></th>
174
+ <th><%= sprintf("%#{TIME_WIDTH}s", "Total") %></th>
175
+ <th><%= sprintf("%#{TIME_WIDTH}s", "Self") %></th>
176
+ <th><%= sprintf("%#{TIME_WIDTH}s", "Wait") %></th>
177
+ <th><%= sprintf("%#{TIME_WIDTH+2}s", "Child") %></th>
178
+ <th><%= sprintf("%#{CALL_WIDTH}s", "Calls") %></th>
179
+ <th>Name</th>
180
+ <th>Line</th>
181
+ </tr>
182
+
183
+ <% methods.sort.reverse_each do |method|
184
+ total_percentage = (method.total_time/total_time) * 100
185
+ next if total_percentage < min_percent
186
+ self_percentage = (method.self_time/total_time) * 100 %>
187
+
188
+ <!-- Parents -->
189
+ <% for caller in method.parents %>
190
+ <tr>
191
+ <td>&nbsp;</td>
192
+ <td>&nbsp;</td>
193
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.total_time) %></td>
194
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.self_time) %></td>
195
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.wait_time) %></td>
196
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.children_time) %></td>
197
+ <% called = "#{caller.called}/#{method.called}" %>
198
+ <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
199
+ <td><%= create_link(thread_id, caller.target) %></td>
200
+ <td><a href="file://<%= File.expand_path(caller.target.source_file) %>#line=<%= caller.line %>"><%= caller.line %></a></td>
201
+ </tr>
202
+ <% end %>
203
+
204
+ <tr class="method">
205
+ <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", total_percentage) %></td>
206
+ <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", self_percentage) %></td>
207
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.total_time) %></td>
208
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.self_time) %></td>
209
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.wait_time) %></td>
210
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.children_time) %></td>
211
+ <td><%= sprintf("%#{CALL_WIDTH}i", method.called) %></td>
212
+ <td><a name="<%= method_href(thread_id, method) %>"><%= method.full_name %></a></td>
213
+ <td><a href="file://<%= File.expand_path(method.source_file) %>#line=<%= method.line %>"><%= method.line %></a></td>
214
+ </tr>
215
+
216
+ <!-- Children -->
217
+ <% for callee in method.children %>
218
+ <tr>
219
+ <td>&nbsp;</td>
220
+ <td>&nbsp;</td>
221
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.total_time) %></td>
222
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.self_time) %></td>
223
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.wait_time) %></td>
224
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.children_time) %></td>
225
+ <% called = "#{callee.called}/#{callee.target.called}" %>
226
+ <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
227
+ <td><%= create_link(thread_id, callee.target) %></td>
228
+ <td><a href="file://<%= File.expand_path(method.source_file) %>#line=<%= callee.line %>"><%= callee.line %></a></td>
229
+ </tr>
230
+ <% end %>
231
+ <!-- Create divider row -->
232
+ <tr class="break"><td colspan="8"></td></tr>
233
+ <% end %>
234
+ </table>
235
+ <% end %>
236
+ </body>
237
+ </html>'
238
+ end
239
+ end
240
+ end
241
+