ruby-prof 0.11.0.rc1 → 0.11.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/CHANGES +20 -5
  2. data/README.rdoc +10 -3
  3. data/ext/ruby_prof/rp_call_info.c +108 -79
  4. data/ext/ruby_prof/rp_call_info.h +1 -0
  5. data/ext/ruby_prof/rp_measure_cpu_time.c +111 -111
  6. data/ext/ruby_prof/rp_measure_gc_runs.c +1 -1
  7. data/ext/ruby_prof/rp_measure_memory.c +1 -1
  8. data/ext/ruby_prof/rp_measure_process_time.c +71 -71
  9. data/ext/ruby_prof/rp_measure_wall_time.c +1 -1
  10. data/ext/ruby_prof/rp_method.c +143 -73
  11. data/ext/ruby_prof/rp_method.h +7 -4
  12. data/ext/ruby_prof/rp_stack.c +16 -1
  13. data/ext/ruby_prof/rp_stack.h +4 -1
  14. data/ext/ruby_prof/rp_thread.c +165 -35
  15. data/ext/ruby_prof/rp_thread.h +8 -2
  16. data/ext/ruby_prof/ruby_prof.c +164 -171
  17. data/ext/ruby_prof/ruby_prof.h +53 -54
  18. data/ext/ruby_prof/vc/ruby_prof.sln +26 -0
  19. data/ext/ruby_prof/vc/ruby_prof.vcxproj +109 -0
  20. data/ext/ruby_prof/vc/ruby_prof_18.vcxproj +105 -0
  21. data/lib/ruby-prof/aggregate_call_info.rb +9 -7
  22. data/lib/ruby-prof/call_info.rb +2 -27
  23. data/lib/ruby-prof/call_info_visitor.rb +42 -0
  24. data/lib/ruby-prof/{empty.png → images/empty.png} +0 -0
  25. data/lib/ruby-prof/{minus.png → images/minus.png} +0 -0
  26. data/lib/ruby-prof/{plus.png → images/plus.png} +0 -0
  27. data/lib/ruby-prof/method_info.rb +13 -15
  28. data/lib/ruby-prof/{abstract_printer.rb → printers/abstract_printer.rb} +36 -2
  29. data/lib/ruby-prof/printers/call_info_printer.rb +40 -0
  30. data/lib/ruby-prof/{call_stack_printer.rb → printers/call_stack_printer.rb} +11 -16
  31. data/lib/ruby-prof/{call_tree_printer.rb → printers/call_tree_printer.rb} +4 -4
  32. data/lib/ruby-prof/{dot_printer.rb → printers/dot_printer.rb} +11 -31
  33. data/lib/ruby-prof/{flat_printer.rb → printers/flat_printer.rb} +26 -35
  34. data/lib/ruby-prof/{flat_printer_with_line_numbers.rb → printers/flat_printer_with_line_numbers.rb} +14 -25
  35. data/lib/ruby-prof/printers/graph_html_printer.rb +248 -0
  36. data/lib/ruby-prof/{graph_printer.rb → printers/graph_printer.rb} +31 -73
  37. data/lib/ruby-prof/{multi_printer.rb → printers/multi_printer.rb} +0 -0
  38. data/lib/ruby-prof/profile.rb +27 -22
  39. data/lib/ruby-prof/rack.rb +22 -12
  40. data/ruby-prof.gemspec +58 -0
  41. data/test/aggregate_test.rb +6 -6
  42. data/test/call_info_visitor_test.rb +31 -0
  43. data/test/duplicate_names_test.rb +1 -1
  44. data/test/dynamic_method_test.rb +1 -1
  45. data/test/enumerable_test.rb +1 -1
  46. data/test/exclude_threads_test.rb +2 -2
  47. data/test/gc_test.rb +35 -0
  48. data/test/line_number_test.rb +2 -2
  49. data/test/measure_cpu_time_test.rb +5 -5
  50. data/test/measure_process_time_test.rb +5 -5
  51. data/test/measure_wall_time_test.rb +5 -5
  52. data/test/method_elimination_test.rb +3 -3
  53. data/test/module_test.rb +1 -1
  54. data/test/no_method_class_test.rb +1 -1
  55. data/test/printers_test.rb +16 -8
  56. data/test/recursive_test.rb +115 -91
  57. data/test/stack_test.rb +1 -1
  58. data/test/start_stop_test.rb +13 -13
  59. data/test/summarize_test.rb +48 -0
  60. data/test/test_suite.rb +1 -0
  61. data/test/thread_test.rb +16 -12
  62. data/test/unique_call_path_test.rb +10 -10
  63. metadata +64 -85
  64. data/lib/1.8/ruby_prof.so +0 -0
  65. data/lib/1.9/ruby_prof.exp +0 -0
  66. data/lib/1.9/ruby_prof.ilk +0 -0
  67. data/lib/1.9/ruby_prof.lib +0 -0
  68. data/lib/1.9/ruby_prof.pdb +0 -0
  69. data/lib/1.9/ruby_prof.so +0 -0
  70. data/lib/ruby-prof.rb +0 -70
  71. data/lib/ruby-prof/graph_html_printer.rb +0 -286
  72. data/lib/ruby-prof/symbol_to_proc.rb +0 -10
  73. data/lib/ruby_prof.exp +0 -0
  74. data/lib/ruby_prof.ilk +0 -0
  75. data/lib/ruby_prof.lib +0 -0
  76. data/lib/ruby_prof.pdb +0 -0
  77. data/lib/ruby_prof.so +0 -0
  78. data/lib/unprof.rb +0 -10
  79. data/test/do_nothing.rb +0 -0
@@ -12,21 +12,10 @@ module RubyProf
12
12
  # printer.print(STDOUT, {})
13
13
  #
14
14
  class FlatPrinterWithLineNumbers < FlatPrinter
15
+ def print_methods(thread)
16
+ total_time = thread.top_method.total_time
15
17
 
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
- methods = methods.sort_by(&sort_method).reverse
25
-
26
- @output << "Thread ID: %d\n" % thread_id
27
- @output << "Total: %0.6f\n" % total_time
28
- @output << "\n"
29
- @output << " %self total self wait child calls name\n"
18
+ methods = thread.methods.sort_by(&sort_method).reverse
30
19
  sum = 0
31
20
  methods.each do |method|
32
21
  self_percent = (method.self_time / total_time) * 100
@@ -36,15 +25,16 @@ module RubyProf
36
25
  #self_time_called = method.called > 0 ? method.self_time/method.called : 0
37
26
  #total_time_called = method.called > 0? method.total_time/method.called : 0
38
27
 
39
- @output << "%6.2f %8.2f %8.2f %8.2f %8.2f %8d %s " % [
40
- method.self_time / total_time * 100, # %self
41
- method.total_time, # total
42
- method.self_time, # self
43
- method.wait_time, # wait
44
- method.children_time, # children
45
- method.called, # calls
46
- method_name(method), # name
47
- ]
28
+ @output << "%6.2f %8.2f %8.2f %8.2f %8.2f %8d %s%s \n" % [
29
+ method.self_time / total_time * 100, # %self
30
+ method.total_time, # total
31
+ method.self_time, # self
32
+ method.wait_time, # wait
33
+ method.children_time, # children
34
+ method.called, # calls
35
+ method.recursive? ? "*" : " ", # cycle
36
+ method_name(method) # name
37
+ ]
48
38
  if method.source_file != 'ruby_runtime'
49
39
  @output << " %s:%s" % [File.expand_path(method.source_file), method.line]
50
40
  end
@@ -64,5 +54,4 @@ module RubyProf
64
54
  end
65
55
  end
66
56
  end
67
- end
68
-
57
+ end
@@ -0,0 +1,248 @@
1
+ # encoding: utf-8
2
+
3
+ require 'erb'
4
+
5
+ module RubyProf
6
+ # Generates graph[link:files/examples/graph_html.html] profile reports as html.
7
+ # To use the graph html printer:
8
+ #
9
+ # result = RubyProf.profile do
10
+ # [code to profile]
11
+ # end
12
+ #
13
+ # printer = RubyProf::GraphHtmlPrinter.new(result)
14
+ # printer.print(STDOUT, :min_percent=>0)
15
+ #
16
+ # The Graph printer takes the following options in its print methods:
17
+ # :filename - specify a file to use that contains the ERB
18
+ # template to use, instead of the built-in self.template
19
+ #
20
+ # :template - specify an ERB template to use, instead of the
21
+ # built-in self.template
22
+ #
23
+
24
+ class GraphHtmlPrinter < AbstractPrinter
25
+ include ERB::Util
26
+
27
+ PERCENTAGE_WIDTH = 8
28
+ TIME_WIDTH = 10
29
+ CALL_WIDTH = 20
30
+
31
+ def setup_options(options)
32
+ super(options)
33
+
34
+ filename = options[:filename]
35
+ template = filename ? File.read(filename).untaint : (options[:template] || self.template)
36
+ @erb = ERB.new(template, nil, nil)
37
+ @erb.filename = filename
38
+ end
39
+
40
+ def print(output = STDOUT, options = {})
41
+ @output = output
42
+ setup_options(options)
43
+ _erbout = @output
44
+ @output << @erb.result(binding)
45
+ end
46
+
47
+ # Creates a link to a method. Note that we do not create
48
+ # links to methods which are under the min_perecent
49
+ # specified by the user, since they will not be
50
+ # printed out.
51
+ def create_link(thread, method)
52
+ overall_time = thread.top_method.total_time
53
+ total_percent = (method.total_time/overall_time) * 100
54
+ if total_percent < min_percent
55
+ # Just return name
56
+ h method.full_name
57
+ else
58
+ href = '#' + method_href(thread, method)
59
+ "<a href=\"#{href}\">#{h method.full_name}</a>"
60
+ end
61
+ end
62
+
63
+ def method_href(thread, method)
64
+ h(method.full_name.gsub(/[><#\.\?=:]/,"_") + "_" + thread.id.to_s)
65
+ end
66
+
67
+ def file_link(path, linenum)
68
+ srcfile = File.expand_path(path)
69
+ if srcfile =~ /\/ruby_runtime$/
70
+ ""
71
+ else
72
+ if RUBY_PLATFORM =~ /darwin/
73
+ "<a href=\"txmt://open?url=file://#{h srcfile}&line=#{linenum}\" title=\"#{h srcfile}:#{linenum}\">#{linenum}</a>"
74
+ else
75
+ "<a href=\"file://#{h srcfile}##{linenum}\" title=\"#{h srcfile}:#{linenum}\">#{linenum}</a>"
76
+ end
77
+ end
78
+ end
79
+
80
+ def template
81
+ '
82
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
83
+ <html>
84
+ <head>
85
+ <style media="all" type="text/css">
86
+ table {
87
+ border-collapse: collapse;
88
+ border: 1px solid #CCC;
89
+ font-family: Verdana, Arial, Helvetica, sans-serif;
90
+ font-size: 9pt;
91
+ line-height: normal;
92
+ width: 100%;
93
+ }
94
+
95
+ th {
96
+ text-align: center;
97
+ border-top: 1px solid #FB7A31;
98
+ border-bottom: 1px solid #FB7A31;
99
+ background: #FFC;
100
+ padding: 0.3em;
101
+ border-left: 1px solid silver;
102
+ }
103
+
104
+ tr.break td {
105
+ border: 0;
106
+ border-top: 1px solid #FB7A31;
107
+ padding: 0;
108
+ margin: 0;
109
+ }
110
+
111
+ tr.method td {
112
+ font-weight: bold;
113
+ }
114
+
115
+ td {
116
+ padding: 0.3em;
117
+ }
118
+
119
+ td:first-child {
120
+ width: 190px;
121
+ }
122
+
123
+ td {
124
+ border-left: 1px solid #CCC;
125
+ text-align: center;
126
+ }
127
+
128
+ .method_name {
129
+ text-align: left;
130
+ }
131
+
132
+ tfoot td {
133
+ text-align: left;
134
+ }
135
+ </style>
136
+ </head>
137
+ <body>
138
+ <h1>Profile Report</h1>
139
+ <!-- Threads Table -->
140
+ <table>
141
+ <tr>
142
+ <th>Thread ID</th>
143
+ <th>Total Time</th>
144
+ </tr>
145
+ <% for thread in @result.threads %>
146
+ <tr>
147
+ <td><a href="#<%= thread.id %>"><%= thread.id %></a></td>
148
+ <td><%= thread.top_method.total_time %></td>
149
+ </tr>
150
+ <% end %>
151
+ </table>
152
+
153
+ <!-- Methods Tables -->
154
+ <% for thread in @result.threads
155
+ methods = thread.methods
156
+ total_time = thread.top_method.total_time %>
157
+ <h2><a name="<%= thread.id %>">Thread <%= thread.id %></a></h2>
158
+
159
+ <table>
160
+ <thead>
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}s", "Wait") %></th>
167
+ <th><%= sprintf("%#{TIME_WIDTH+2}s", "Child") %></th>
168
+ <th><%= sprintf("%#{CALL_WIDTH}s", "Calls") %></th>
169
+ <th class="method_name">Name</th>
170
+ <th>Line</th>
171
+ </tr>
172
+ </thead>
173
+
174
+ <tbody>
175
+ <% min_time = @options[:min_time] || (@options[:nonzero] ? 0.005 : nil)
176
+ methods.sort_by(&sort_method).reverse_each do |method|
177
+ total_percentage = (method.total_time/total_time) * 100
178
+ next if total_percentage < min_percent
179
+ next if min_time && method.total_time < min_time
180
+ self_percentage = (method.self_time/total_time) * 100 %>
181
+
182
+ <!-- Parents -->
183
+ <% for caller in method.aggregate_parents.sort_by(&:total_time)
184
+ next unless caller.parent
185
+ next if min_time && caller.total_time < min_time %>
186
+ <tr>
187
+ <td>&nbsp;</td>
188
+ <td>&nbsp;</td>
189
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.total_time) %></td>
190
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.self_time) %></td>
191
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.wait_time) %></td>
192
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.children_time) %></td>
193
+ <% called = "#{caller.called}/#{method.called}" %>
194
+ <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
195
+ <td class="method_name"><%= create_link(thread, caller.parent.target) %></td>
196
+ <td><%= file_link(caller.parent.target.source_file, caller.line) %></td>
197
+ </tr>
198
+ <% end %>
199
+
200
+ <tr class="method">
201
+ <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", total_percentage) %></td>
202
+ <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", self_percentage) %></td>
203
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.total_time) %></td>
204
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.self_time) %></td>
205
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.wait_time) %></td>
206
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.children_time) %></td>
207
+ <td><%= sprintf("%#{CALL_WIDTH}i", method.called) %></td>
208
+ <td class="method_name">
209
+ <a name="<%= method_href(thread, method) %>">
210
+ <%= method.recursive? ? "*" : " "%><%= h method.full_name %>
211
+ </a>
212
+ </td>
213
+ <td><%= file_link(method.source_file, method.line) %></td>
214
+ </tr>
215
+
216
+ <!-- Children -->
217
+ <% for callee in method.aggregate_children.sort_by(&:total_time).reverse %>
218
+ <% next if min_time && callee.total_time < min_time %>
219
+ <tr>
220
+ <td>&nbsp;</td>
221
+ <td>&nbsp;</td>
222
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.total_time) %></td>
223
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.self_time) %></td>
224
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.wait_time) %></td>
225
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.children_time) %></td>
226
+ <% called = "#{callee.called}/#{callee.target.called}" %>
227
+ <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
228
+ <td class="method_name"><%= create_link(thread, callee.target) %></td>
229
+ <td><%= file_link(method.source_file, callee.line) %></td>
230
+ </tr>
231
+ <% end %>
232
+ <!-- Create divider row -->
233
+ <tr class="break"><td colspan="9"></td></tr>
234
+ <% end %>
235
+ </tbody>
236
+ <tfoot>
237
+ <tr>
238
+ <td colspan="9">* in front of method name means it is recursively called</td>
239
+ </tr>
240
+ </tfoot>
241
+ </table>
242
+ <% end %>
243
+ </body>
244
+ </html>'
245
+ end
246
+ end
247
+ end
248
+
@@ -18,60 +18,30 @@ module RubyProf
18
18
  TIME_WIDTH = 10
19
19
  CALL_WIDTH = 17
20
20
 
21
- # Create a GraphPrinter. Result is a RubyProf::Result
22
- # object generated from a profiling run.
23
- def initialize(result)
24
- super(result)
25
- @thread_times = Hash.new
26
- calculate_thread_times
27
- end
28
-
29
- def calculate_thread_times
30
- # Cache thread times since this is an expensive
31
- # operation with the required sorting
32
- @result.threads.each do |thread_id, methods|
33
- top = methods.max
34
-
35
- thread_time = [top.total_time, 0.01].max
36
-
37
- @thread_times[thread_id] = thread_time
38
- end
39
- end
21
+ private
40
22
 
41
- # Print a graph report to the provided output.
42
- #
43
- # output - Any IO oject, including STDOUT or a file.
44
- # The default value is STDOUT.
45
- #
46
- # options - Hash of print options. See #setup_options
47
- # for more information.
48
- #
49
- def print(output = STDOUT, options = {})
50
- @output = output
51
- setup_options(options)
52
- print_threads
53
- end
23
+ def print_header(thread)
24
+ @output << "Thread ID: #{thread.id}\n"
25
+ @output << "Total Time: #{thread.top_method.total_time}\n"
26
+ @output << "Sort by: #{sort_method}\n"
27
+ @output << "\n"
54
28
 
55
- private
56
- def print_threads
57
- # sort assumes that spawned threads have higher object_ids
58
- @result.threads.sort.each do |thread_id, methods|
59
- print_methods(thread_id, methods)
60
- @output << "\n" * 2
61
- end
29
+ # 1 is for % sign
30
+ @output << sprintf("%#{PERCENTAGE_WIDTH}s", "%total")
31
+ @output << sprintf("%#{PERCENTAGE_WIDTH}s", "%self")
32
+ @output << sprintf("%#{TIME_WIDTH}s", "total")
33
+ @output << sprintf("%#{TIME_WIDTH}s", "self")
34
+ @output << sprintf("%#{TIME_WIDTH}s", "wait")
35
+ @output << sprintf("%#{TIME_WIDTH}s", "child")
36
+ @output << sprintf("%#{CALL_WIDTH}s", "calls")
37
+ @output << " Name"
38
+ @output << "\n"
62
39
  end
63
40
 
64
- def print_methods(thread_id, methods)
41
+ def print_methods(thread)
42
+ total_time = thread.top_method.total_time
65
43
  # Sort methods from longest to shortest total time
66
- methods = methods.sort_by(&sort_method)
67
-
68
- toplevel = methods.last
69
- total_time = toplevel.total_time
70
- if total_time == 0
71
- total_time = 0.01
72
- end
73
-
74
- print_heading(thread_id)
44
+ methods = thread.methods.sort_by(&sort_method)
75
45
 
76
46
  # Print each method in total time order
77
47
  methods.reverse_each do |method|
@@ -82,7 +52,7 @@ module RubyProf
82
52
 
83
53
  @output << "-" * 80 << "\n"
84
54
 
85
- print_parents(thread_id, method)
55
+ print_parents(thread, method)
86
56
 
87
57
  # 1 is for % sign
88
58
  @output << sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", total_percentage)
@@ -92,7 +62,8 @@ module RubyProf
92
62
  @output << sprintf("%#{TIME_WIDTH}.2f", method.wait_time)
93
63
  @output << sprintf("%#{TIME_WIDTH}.2f", method.children_time)
94
64
  @output << sprintf("%#{CALL_WIDTH}i", method.called)
95
- @output << sprintf(" %s", method_name(method))
65
+ @output << sprintf(" %s", method.recursive? ? "*" : " ")
66
+ @output << sprintf("%s", method_name(method))
96
67
  if print_file
97
68
  @output << sprintf(" %s:%s", method.source_file, method.line)
98
69
  end
@@ -102,24 +73,7 @@ module RubyProf
102
73
  end
103
74
  end
104
75
 
105
- def print_heading(thread_id)
106
- @output << "Thread ID: #{thread_id}\n"
107
- @output << "Total Time: #{@thread_times[thread_id]}\n"
108
- @output << "\n"
109
-
110
- # 1 is for % sign
111
- @output << sprintf("%#{PERCENTAGE_WIDTH}s", "%total")
112
- @output << sprintf("%#{PERCENTAGE_WIDTH}s", "%self")
113
- @output << sprintf("%#{TIME_WIDTH}s", "total")
114
- @output << sprintf("%#{TIME_WIDTH}s", "self")
115
- @output << sprintf("%#{TIME_WIDTH}s", "wait")
116
- @output << sprintf("%#{TIME_WIDTH}s", "child")
117
- @output << sprintf("%#{CALL_WIDTH}s", "calls")
118
- @output << " Name"
119
- @output << "\n"
120
- end
121
-
122
- def print_parents(thread_id, method)
76
+ def print_parents(thread, method)
123
77
  method.aggregate_parents.sort_by(&:total_time).each do |caller|
124
78
  next unless caller.parent
125
79
  @output << " " * 2 * PERCENTAGE_WIDTH
@@ -130,7 +84,7 @@ module RubyProf
130
84
 
131
85
  call_called = "#{caller.called}/#{method.called}"
132
86
  @output << sprintf("%#{CALL_WIDTH}s", call_called)
133
- @output << sprintf(" %s", caller.parent.target.full_name)
87
+ @output << sprintf(" %s", caller.parent.target.full_name)
134
88
  @output << "\n"
135
89
  end
136
90
  end
@@ -148,10 +102,14 @@ module RubyProf
148
102
 
149
103
  call_called = "#{child.called}/#{child.target.called}"
150
104
  @output << sprintf("%#{CALL_WIDTH}s", call_called)
151
- @output << sprintf(" %s", child.target.full_name)
105
+ @output << sprintf(" %s", child.target.full_name)
152
106
  @output << "\n"
153
107
  end
154
108
  end
155
- end
156
- end
157
109
 
110
+ def print_footer(thread)
111
+ @output << "\n"
112
+ @output << "* in front of method name means it is recursively called\n"
113
+ end
114
+ end
115
+ end