ruby-prof 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/CHANGES +17 -0
  2. data/LICENSE +23 -0
  3. data/README +220 -0
  4. data/Rakefile +141 -0
  5. data/bin/ruby-prof +154 -0
  6. data/doc/classes/RubyProf.html +563 -0
  7. data/doc/classes/RubyProf/CallInfo.html +274 -0
  8. data/doc/classes/RubyProf/FlatPrinter.html +207 -0
  9. data/doc/classes/RubyProf/GraphHtmlPrinter.html +538 -0
  10. data/doc/classes/RubyProf/GraphPrinter.html +240 -0
  11. data/doc/classes/RubyProf/MethodInfo.html +556 -0
  12. data/doc/classes/RubyProf/ProfileTask.html +395 -0
  13. data/doc/classes/RubyProf/Result.html +234 -0
  14. data/doc/created.rid +1 -0
  15. data/doc/files/LICENSE.html +142 -0
  16. data/doc/files/README.html +376 -0
  17. data/doc/files/bin/ruby-prof.html +143 -0
  18. data/doc/files/examples/flat_txt.html +187 -0
  19. data/doc/files/examples/graph_html.html +948 -0
  20. data/doc/files/examples/graph_txt.html +305 -0
  21. data/doc/files/ext/ruby_prof_c.html +101 -0
  22. data/doc/files/lib/ruby-prof/flat_printer_rb.html +101 -0
  23. data/doc/files/lib/ruby-prof/graph_html_printer_rb.html +108 -0
  24. data/doc/files/lib/ruby-prof/graph_printer_rb.html +101 -0
  25. data/doc/files/lib/ruby-prof/profiletask_rb.html +109 -0
  26. data/doc/files/lib/ruby-prof_rb.html +111 -0
  27. data/doc/files/lib/unprof_rb.html +108 -0
  28. data/doc/fr_class_index.html +34 -0
  29. data/doc/fr_file_index.html +39 -0
  30. data/doc/fr_method_index.html +67 -0
  31. data/doc/index.html +24 -0
  32. data/doc/rdoc-style.css +208 -0
  33. data/examples/flat.txt +57 -0
  34. data/examples/graph.html +827 -0
  35. data/examples/graph.txt +171 -0
  36. data/ext/extconf.rb +19 -0
  37. data/ext/ruby_prof.c +1433 -0
  38. data/lib/ruby-prof.rb +38 -0
  39. data/lib/ruby-prof/flat_printer.rb +76 -0
  40. data/lib/ruby-prof/graph_html_printer.rb +227 -0
  41. data/lib/ruby-prof/graph_printer.rb +142 -0
  42. data/lib/ruby-prof/profiletask.rb +150 -0
  43. data/lib/unprof.rb +8 -0
  44. data/test/basic_test.rb +141 -0
  45. data/test/clock_mode_test.rb +73 -0
  46. data/test/module_test.rb +45 -0
  47. data/test/prime.rb +58 -0
  48. data/test/prime_test.rb +24 -0
  49. data/test/printers_test.rb +28 -0
  50. data/test/recursive_test.rb +55 -0
  51. data/test/test.rb +3 -0
  52. data/test/test_helper.rb +45 -0
  53. data/test/test_suite.rb +9 -0
  54. data/test/thread_test.rb +32 -0
  55. data/test/timing_test.rb +90 -0
  56. metadata +121 -0
data/lib/ruby-prof.rb ADDED
@@ -0,0 +1,38 @@
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
@@ -0,0 +1,76 @@
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
+ methods = methods.values.sort do |method1, method2|
49
+ method1.self_time <=> method2.self_time
50
+ end
51
+ methods.reverse!
52
+
53
+ sum = 0
54
+ @output << "Thread ID: " << thread_id << "\n"
55
+ @output << " %self cumulative total self children calls self/call total/call name\n"
56
+
57
+ for method in methods
58
+ self_percent = (method.self_time / total_time) * 100
59
+ next if self_percent < @min_percent
60
+
61
+ sum += method.self_time
62
+ @output.printf("%6.2f %8.2f %8.2f %8.2f %8.2f %8d %8.2f %8.2f %s\n",
63
+ method.self_time / total_time * 100, # %self
64
+ sum, # cumulative
65
+ method.total_time, # total
66
+ method.self_time, # self
67
+ method.children_time, # children
68
+ method.called, # calls
69
+ method.self_time / method.called, # self/call
70
+ method.total_time / method.called, # total/call
71
+ method.name) # name
72
+ end
73
+ end
74
+ end
75
+ end
76
+
@@ -0,0 +1,227 @@
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 = methods.values.sort.reverse %>
172
+ <% for method in methods %>
173
+ <% method_total_percent = self.total_percent(method) %>
174
+ <% next if method_total_percent < @min_percent %>
175
+ <% method_self_percent = self.self_percent(method) %>
176
+
177
+ <!-- Parents -->
178
+ <% for name, call_info in method.parents %>
179
+ <tr>
180
+ <td>&nbsp;</td>
181
+ <td>&nbsp;</td>
182
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", call_info.total_time) %></td>
183
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", call_info.self_time) %></td>
184
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", call_info.children_time) %></td>
185
+ <% called = "#{call_info.called}/#{method.called}" %>
186
+ <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
187
+ <td><%= create_link(thread_id, name) %></td>
188
+ </tr>
189
+ <% end %>
190
+
191
+ <tr class="method">
192
+ <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", method_total_percent) %></td>
193
+ <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", method_self_percent) %></td>
194
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.total_time) %></td>
195
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.self_time) %></td>
196
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.children_time) %></td>
197
+ <td><%= sprintf("%#{CALL_WIDTH}i", method.called) %></td>
198
+ <td><a name="<%= link_name(thread_id, method.name) %>"><%= method.name %></a></td>
199
+ </tr>
200
+
201
+ <!-- Children -->
202
+ <% for name, call_info in method.children %>
203
+ <% methods = @result.threads[thread_id] %>
204
+ <% child = methods[name] %>
205
+
206
+ <tr>
207
+ <td>&nbsp;</td>
208
+ <td>&nbsp;</td>
209
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", call_info.total_time) %></td>
210
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", call_info.self_time) %></td>
211
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", call_info.children_time) %></td>
212
+ <% called = "#{call_info.called}/#{child.called}" %>
213
+ <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
214
+ <td><%= create_link(thread_id, name) %></td>
215
+ </tr>
216
+ <% end %>
217
+ <!-- Create divider row -->
218
+ <tr class="break"><td colspan="7"></td></tr>
219
+ <% end %>
220
+ </table>
221
+ <% end %>
222
+ </body>
223
+ </html>'
224
+ end
225
+ end
226
+ end
227
+
@@ -0,0 +1,142 @@
1
+ module RubyProf
2
+ # Generates graph[link:files/examples/graph_txt.html] profile reports as text.
3
+ # To use the graph printer:
4
+ #
5
+ # result = RubyProf.profile do
6
+ # [code to profile]
7
+ # end
8
+ #
9
+ # printer = RubyProf::GraphPrinter.new(result, 5)
10
+ # printer.print(STDOUT, 0)
11
+ #
12
+ # The constructor takes two arguments. The first is
13
+ # a RubyProf::Result object generated from a profiling
14
+ # run. The second is the minimum %total (the methods
15
+ # total time divided by the overall total time) that
16
+ # a method must take for it to be printed out in
17
+ # the report. Use this parameter to eliminate methods
18
+ # that are not important to the overall profiling results.
19
+
20
+ class GraphPrinter
21
+ PERCENTAGE_WIDTH = 8
22
+ TIME_WIDTH = 10
23
+ CALL_WIDTH = 20
24
+
25
+ # Create a GraphPrinter. Result is a RubyProf::Result
26
+ # object generated from a profiling run.
27
+ def initialize(result, min_percent = 0)
28
+ @result = result
29
+ @min_percent = min_percent
30
+ end
31
+
32
+ # Print a graph report to the provided output.
33
+ #
34
+ # output - Any IO oject, including STDOUT or a file.
35
+ # The default value is STDOUT.
36
+ #
37
+ # min_percent - The minimum %total (the methods
38
+ # total time divided by the overall total time) that
39
+ # a method must take for it to be printed out in
40
+ # the report. Default value is 0.
41
+ def print(output = STDOUT, min_percent = 0)
42
+ @output = output
43
+ @min_percent = min_percent
44
+
45
+ print_threads
46
+ end
47
+
48
+ private
49
+ def print_threads
50
+ # sort assumes that spawned threads have higher object_ids
51
+ @result.threads.sort.each do |thread_id, methods|
52
+ print_methods(thread_id, methods)
53
+ @output << "\n" * 2
54
+ end
55
+ end
56
+
57
+ def print_methods(thread_id, methods)
58
+ toplevel = @result.toplevel(thread_id)
59
+ total_time = toplevel.total_time
60
+ if total_time == 0
61
+ total_time = 0.01
62
+ end
63
+
64
+ print_heading(thread_id)
65
+
66
+ # Get methods and sort by time
67
+ methods = methods.values.sort.reverse
68
+
69
+ # Print each method
70
+ methods.each do |method|
71
+ total_percentage = (method.total_time/total_time) * 100
72
+ self_percentage = (method.self_time/total_time) * 100
73
+
74
+ next if total_percentage < @min_percent
75
+
76
+ @output << "-" * 80 << "\n"
77
+
78
+ print_parents(thread_id, method)
79
+
80
+ # 1 is for % sign
81
+ @output << sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", total_percentage)
82
+ @output << sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", self_percentage)
83
+ @output << sprintf("%#{TIME_WIDTH}.2f", method.total_time)
84
+ @output << sprintf("%#{TIME_WIDTH}.2f", method.self_time)
85
+ @output << sprintf("%#{TIME_WIDTH}.2f", method.children_time)
86
+ @output << sprintf("%#{CALL_WIDTH}i", method.called)
87
+ @output << sprintf(" %s", method.name)
88
+ @output << "\n"
89
+
90
+ print_children(thread_id, method)
91
+ end
92
+ end
93
+
94
+ def print_heading(thread_id)
95
+ @output << "Thread ID: #{thread_id}\n"
96
+ # 1 is for % sign
97
+ @output << sprintf("%#{PERCENTAGE_WIDTH}s", "%total")
98
+ @output << sprintf("%#{PERCENTAGE_WIDTH}s", "%self")
99
+ @output << sprintf("%#{TIME_WIDTH}s", "total")
100
+ @output << sprintf("%#{TIME_WIDTH}s", "self")
101
+ @output << sprintf("%#{TIME_WIDTH+2}s", "children")
102
+ @output << sprintf("%#{CALL_WIDTH-2}s", "calls")
103
+ @output << " Name"
104
+ @output << "\n"
105
+ end
106
+
107
+ def print_parents(thread_id, method)
108
+ method.parents.each do |name, call_info|
109
+ @output << " " * 2 * PERCENTAGE_WIDTH
110
+ @output << sprintf("%#{TIME_WIDTH}.2f", call_info.total_time)
111
+ @output << sprintf("%#{TIME_WIDTH}.2f", call_info.self_time)
112
+ @output << sprintf("%#{TIME_WIDTH}.2f", call_info.children_time)
113
+
114
+ call_called = "#{call_info.called}/#{method.called}"
115
+ @output << sprintf("%#{CALL_WIDTH}s", call_called)
116
+ @output << sprintf(" %s", name)
117
+ @output << "\n"
118
+ end
119
+ end
120
+
121
+ def print_children(thread_id, method)
122
+ a = method.children
123
+ method.children.each do |name, call_info|
124
+ # Get children method
125
+ methods = @result.threads[thread_id]
126
+ children = methods[name]
127
+
128
+ @output << " " * 2 * PERCENTAGE_WIDTH
129
+
130
+ @output << sprintf("%#{TIME_WIDTH}.2f", call_info.total_time)
131
+ @output << sprintf("%#{TIME_WIDTH}.2f", call_info.self_time)
132
+ @output << sprintf("%#{TIME_WIDTH}.2f", call_info.children_time)
133
+
134
+ call_called = "#{call_info.called}/#{children.called}"
135
+ @output << sprintf("%#{CALL_WIDTH}s", call_called)
136
+ @output << sprintf(" %s", name)
137
+ @output << "\n"
138
+ end
139
+ end
140
+ end
141
+ end
142
+