ruby-prof 0.6.0-x86-mswin32-60

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 (50) hide show
  1. data/CHANGES +116 -0
  2. data/LICENSE +23 -0
  3. data/README +307 -0
  4. data/Rakefile +141 -0
  5. data/bin/ruby-prof +192 -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/extconf.rb +21 -0
  10. data/ext/extconf.rb.rej +13 -0
  11. data/ext/measure_allocations.h +43 -0
  12. data/ext/measure_cpu_time.h +138 -0
  13. data/ext/measure_memory.h +42 -0
  14. data/ext/measure_process_time.h +41 -0
  15. data/ext/measure_wall_time.h +42 -0
  16. data/ext/ruby_prof.c +1628 -0
  17. data/lib/ruby-prof.rb +43 -0
  18. data/lib/ruby-prof/abstract_printer.rb +42 -0
  19. data/lib/ruby-prof/call_tree_printer.rb +76 -0
  20. data/lib/ruby-prof/call_tree_printer.rb.rej +27 -0
  21. data/lib/ruby-prof/flat_printer.rb +79 -0
  22. data/lib/ruby-prof/graph_html_printer.rb +255 -0
  23. data/lib/ruby-prof/graph_printer.rb +163 -0
  24. data/lib/ruby-prof/profile_test_case.rb +80 -0
  25. data/lib/ruby-prof/task.rb +147 -0
  26. data/lib/ruby_prof.so +0 -0
  27. data/lib/unprof.rb +8 -0
  28. data/rails_plugin/ruby-prof/init.rb +8 -0
  29. data/rails_plugin/ruby-prof/lib/profiling.rb +57 -0
  30. data/test/basic_test.rb +190 -0
  31. data/test/duplicate_names_test.rb +33 -0
  32. data/test/line_number_test.rb +69 -0
  33. data/test/measure_mode_test.rb +79 -0
  34. data/test/module_test.rb +57 -0
  35. data/test/no_method_class_test.rb +14 -0
  36. data/test/prime.rb +60 -0
  37. data/test/prime1.rb +17 -0
  38. data/test/prime2.rb +26 -0
  39. data/test/prime3.rb +17 -0
  40. data/test/prime_test.rb +24 -0
  41. data/test/printers_test.rb +74 -0
  42. data/test/profile_unit_test.rb +24 -0
  43. data/test/recursive_test.rb +144 -0
  44. data/test/singleton_test.rb +38 -0
  45. data/test/start_test.rb +24 -0
  46. data/test/test_helper.rb +55 -0
  47. data/test/test_suite.rb +19 -0
  48. data/test/thread_test.rb +135 -0
  49. data/test/timing_test.rb +133 -0
  50. metadata +116 -0
data/lib/ruby-prof.rb ADDED
@@ -0,0 +1,43 @@
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
+ when "memory"
36
+ RubyProf.measure_mode = RubyProf::MEMORY
37
+ else
38
+ RubyProf.measure_mode = RubyProf::PROCESS_TIME
39
+ end
40
+ end
41
+ end
42
+
43
+ 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,76 @@
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
+ end
31
+ @output << "\n\n"
32
+
33
+ print_threads
34
+ end
35
+
36
+ def print_threads
37
+ @result.threads.each do |thread_id, methods|
38
+ print_methods(thread_id, methods)
39
+ end
40
+ end
41
+
42
+ def convert(value)
43
+ (value * @value_scale).round
44
+ end
45
+
46
+ def file(method)
47
+ File.expand_path(method.source_file)
48
+ end
49
+
50
+ def name(method)
51
+ "#{method.klass_name}::#{method.method_name}"
52
+ end
53
+
54
+ def print_methods(thread_id, methods)
55
+ methods.reverse_each do |method|
56
+ # Print out the file and method name
57
+ @output << "fl=#{file(method)}\n"
58
+ @output << "fn=#{name(method)}\n"
59
+
60
+ # Now print out the function line number and its self time
61
+ @output << "#{method.line} #{convert(method.self_time)}\n"
62
+
63
+ # Now print out all the children methods
64
+ method.children.each do |callee|
65
+ @output << "cfl=#{file(callee.target)}\n"
66
+ @output << "cfn=#{name(callee.target)}\n"
67
+ @output << "calls=#{callee.called} #{callee.line}\n"
68
+
69
+ # Print out total times here!
70
+ @output << "#{callee.line} #{convert(callee.total_time)}\n"
71
+ end
72
+ @output << "\n"
73
+ end
74
+ end #end print_methods
75
+ end # end class
76
+ end # end packages
@@ -0,0 +1,27 @@
1
+ ***************
2
+ *** 28,39 ****
3
+
4
+ def print_threads
5
+ @result.threads.each do |thread_id, methods|
6
+ - print_methods(thread_id ,methods)
7
+ end
8
+ end
9
+
10
+ def convert(value)
11
+ - (value * 1000).round
12
+ end
13
+
14
+ def file(method)
15
+ --- 32,43 ----
16
+
17
+ def print_threads
18
+ @result.threads.each do |thread_id, methods|
19
+ + print_methods(thread_id ,methods)
20
+ end
21
+ end
22
+
23
+ def convert(value)
24
+ + (value * @value_scale).round
25
+ end
26
+
27
+ def file(method)
@@ -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,255 @@
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
+ }
119
+
120
+ th {
121
+ text-align: center;
122
+ border-top: 1px solid #FB7A31;
123
+ border-bottom: 1px solid #FB7A31;
124
+ background: #FFC;
125
+ padding: 0.3em;
126
+ border-left: 1px solid silver;
127
+ }
128
+
129
+ tr.break td {
130
+ border: 0;
131
+ border-top: 1px solid #FB7A31;
132
+ padding: 0;
133
+ margin: 0;
134
+ }
135
+
136
+ tr.method td {
137
+ font-weight: bold;
138
+ }
139
+
140
+ td {
141
+ padding: 0.3em;
142
+ }
143
+
144
+ td:first-child {
145
+ width: 190px;
146
+ }
147
+
148
+ td {
149
+ border-left: 1px solid #CCC;
150
+ text-align: center;
151
+ }
152
+
153
+ .method_name {
154
+ text-align: left;
155
+ max-width: 25em;
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.parents %>
202
+ <% next if min_time && caller.total_time < min_time %>
203
+ <tr>
204
+ <td>&nbsp;</td>
205
+ <td>&nbsp;</td>
206
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.total_time) %></td>
207
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.self_time) %></td>
208
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.wait_time) %></td>
209
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.children_time) %></td>
210
+ <% called = "#{caller.called}/#{method.called}" %>
211
+ <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
212
+ <td class="method_name"><%= create_link(thread_id, caller.target) %></td>
213
+ <td><a href="file://<%=h srcfile=File.expand_path(caller.target.source_file) %>#line=<%= linenum=caller.line %>" title="<%=h srcfile %>:<%= linenum %>"><%= caller.line %></a></td>
214
+ </tr>
215
+ <% end %>
216
+
217
+ <tr class="method">
218
+ <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", total_percentage) %></td>
219
+ <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", self_percentage) %></td>
220
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.total_time) %></td>
221
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.self_time) %></td>
222
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.wait_time) %></td>
223
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.children_time) %></td>
224
+ <td><%= sprintf("%#{CALL_WIDTH}i", method.called) %></td>
225
+ <td class="method_name"><a name="<%= method_href(thread_id, method) %>"><%= h method.full_name %></a></td>
226
+ <td><a href="file://<%=h srcfile=File.expand_path(method.source_file) %>#line=<%= linenum=method.line %>" title="<%=h srcfile %>:<%= linenum %>"><%= method.line %></a></td>
227
+ </tr>
228
+
229
+ <!-- Children -->
230
+ <% for callee in method.children %>
231
+ <% next if min_time && callee.total_time < min_time %>
232
+ <tr>
233
+ <td>&nbsp;</td>
234
+ <td>&nbsp;</td>
235
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.total_time) %></td>
236
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.self_time) %></td>
237
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.wait_time) %></td>
238
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.children_time) %></td>
239
+ <% called = "#{callee.called}/#{callee.target.called}" %>
240
+ <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
241
+ <td class="method_name"><%= create_link(thread_id, callee.target) %></td>
242
+ <td><a href="file://<%=h srcfile=File.expand_path(method.source_file) %>#line=<%= linenum=callee.line %>" title="<%=h srcfile %>:<%= linenum %>"><%= callee.line %></a></td>
243
+ </tr>
244
+ <% end %>
245
+ <!-- Create divider row -->
246
+ <tr class="break"><td colspan="9"></td></tr>
247
+ <% end %>
248
+ </table>
249
+ <% end %>
250
+ </body>
251
+ </html>'
252
+ end
253
+ end
254
+ end
255
+