skaes-ruby-prof 0.7.3

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 (67) hide show
  1. data/CHANGES +202 -0
  2. data/LICENSE +23 -0
  3. data/README +436 -0
  4. data/Rakefile +129 -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/extconf.rb +34 -0
  10. data/ext/measure_allocations.h +58 -0
  11. data/ext/measure_cpu_time.h +152 -0
  12. data/ext/measure_gc_runs.h +76 -0
  13. data/ext/measure_gc_time.h +57 -0
  14. data/ext/measure_memory.h +101 -0
  15. data/ext/measure_process_time.h +52 -0
  16. data/ext/measure_wall_time.h +53 -0
  17. data/ext/mingw/Rakefile +23 -0
  18. data/ext/mingw/build.rake +38 -0
  19. data/ext/ruby_prof.c +1747 -0
  20. data/ext/ruby_prof.h +185 -0
  21. data/ext/vc/ruby_prof.sln +20 -0
  22. data/ext/vc/ruby_prof.vcproj +241 -0
  23. data/ext/version.h +4 -0
  24. data/lib/ruby-prof.rb +51 -0
  25. data/lib/ruby-prof/abstract_printer.rb +41 -0
  26. data/lib/ruby-prof/aggregate_call_info.rb +68 -0
  27. data/lib/ruby-prof/call_info.rb +112 -0
  28. data/lib/ruby-prof/call_stack_printer.rb +746 -0
  29. data/lib/ruby-prof/call_tree_printer.rb +84 -0
  30. data/lib/ruby-prof/empty.png +0 -0
  31. data/lib/ruby-prof/flat_printer.rb +79 -0
  32. data/lib/ruby-prof/graph_html_printer.rb +272 -0
  33. data/lib/ruby-prof/graph_printer.rb +164 -0
  34. data/lib/ruby-prof/method_info.rb +131 -0
  35. data/lib/ruby-prof/minus.png +0 -0
  36. data/lib/ruby-prof/multi_printer.rb +55 -0
  37. data/lib/ruby-prof/plus.png +0 -0
  38. data/lib/ruby-prof/result.rb +70 -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 +136 -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/method_elimination_test.rb +74 -0
  53. data/test/module_test.rb +54 -0
  54. data/test/multi_printer_test.rb +81 -0
  55. data/test/no_method_class_test.rb +13 -0
  56. data/test/prime.rb +58 -0
  57. data/test/prime_test.rb +13 -0
  58. data/test/printers_test.rb +73 -0
  59. data/test/recursive_test.rb +215 -0
  60. data/test/singleton_test.rb +38 -0
  61. data/test/stack_printer_test.rb +74 -0
  62. data/test/stack_test.rb +138 -0
  63. data/test/start_stop_test.rb +95 -0
  64. data/test/test_suite.rb +26 -0
  65. data/test/thread_test.rb +159 -0
  66. data/test/unique_call_path_test.rb +206 -0
  67. metadata +128 -0
@@ -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
Binary file
@@ -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,272 @@
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
+ def total_time(call_infos)
59
+ sum(call_infos.map{|ci| ci.total_time})
60
+ end
61
+
62
+ def sum(a)
63
+ a.inject(0.0){|s,t| s+=t}
64
+ end
65
+
66
+ # These methods should be private but then ERB doesn't
67
+ # work. Turn off RDOC though
68
+ #--
69
+ def calculate_thread_times
70
+ @overall_threads_time = 0.0
71
+ @thread_times = Hash.new
72
+ @result.threads.each do |thread_id, methods|
73
+ roots = methods.select{|m| m.root?}
74
+ thread_total_time = sum(roots.map{|r| self.total_time(r.call_infos)})
75
+ @overall_threads_time += thread_total_time
76
+ @thread_times[thread_id] = thread_total_time
77
+ end
78
+ end
79
+
80
+ def thread_time(thread_id)
81
+ @thread_times[thread_id]
82
+ end
83
+
84
+ def total_percent(thread_id, method)
85
+ overall_time = self.thread_time(thread_id)
86
+ (method.total_time/overall_time) * 100
87
+ end
88
+
89
+ def self_percent(method)
90
+ overall_time = self.thread_time(method.thread_id)
91
+ (method.self_time/overall_time) * 100
92
+ end
93
+
94
+ # Creates a link to a method. Note that we do not create
95
+ # links to methods which are under the min_perecent
96
+ # specified by the user, since they will not be
97
+ # printed out.
98
+ def create_link(thread_id, method)
99
+ if self.total_percent(thread_id, method) < min_percent
100
+ # Just return name
101
+ h method.full_name
102
+ else
103
+ href = '#' + method_href(thread_id, method)
104
+ "<a href=\"#{href}\">#{h method.full_name}</a>"
105
+ end
106
+ end
107
+
108
+ def method_href(thread_id, method)
109
+ h(method.full_name.gsub(/[><#\.\?=:]/,"_") + "_" + thread_id.to_s)
110
+ end
111
+
112
+ def file_link(path, linenum)
113
+ srcfile = File.expand_path(path)
114
+ if srcfile =~ /\/ruby_runtime$/
115
+ ""
116
+ else
117
+ "<a href=\"txmt://open?url=file://#{h srcfile}&line=#{linenum}\" title=\"#{h srcfile}:#{linenum}\">#{linenum}</a>"
118
+ end
119
+ end
120
+
121
+ def template
122
+ '
123
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
124
+ <html>
125
+ <head>
126
+ <style media="all" type="text/css">
127
+ table {
128
+ border-collapse: collapse;
129
+ border: 1px solid #CCC;
130
+ font-family: Verdana, Arial, Helvetica, sans-serif;
131
+ font-size: 9pt;
132
+ line-height: normal;
133
+ width: 100%;
134
+ }
135
+
136
+ th {
137
+ text-align: center;
138
+ border-top: 1px solid #FB7A31;
139
+ border-bottom: 1px solid #FB7A31;
140
+ background: #FFC;
141
+ padding: 0.3em;
142
+ border-left: 1px solid silver;
143
+ }
144
+
145
+ tr.break td {
146
+ border: 0;
147
+ border-top: 1px solid #FB7A31;
148
+ padding: 0;
149
+ margin: 0;
150
+ }
151
+
152
+ tr.method td {
153
+ font-weight: bold;
154
+ }
155
+
156
+ td {
157
+ padding: 0.3em;
158
+ }
159
+
160
+ td:first-child {
161
+ width: 190px;
162
+ }
163
+
164
+ td {
165
+ border-left: 1px solid #CCC;
166
+ text-align: center;
167
+ }
168
+
169
+ .method_name {
170
+ text-align: left;
171
+ }
172
+ </style>
173
+ </head>
174
+ <body>
175
+ <h1>Profile Report</h1>
176
+ <!-- Threads Table -->
177
+ <table>
178
+ <tr>
179
+ <th>Thread ID</th>
180
+ <th>Total Time</th>
181
+ </tr>
182
+ <% for thread_id in @result.threads.keys.sort %>
183
+ <tr>
184
+ <td><a href="#<%= thread_id %>"><%= thread_id %></a></td>
185
+ <td><%= thread_time(thread_id) %></td>
186
+ </tr>
187
+ <% end %>
188
+ </table>
189
+
190
+ <!-- Methods Tables -->
191
+ <% for thread_id in @result.threads.keys.sort
192
+ methods = @result.threads[thread_id]
193
+ total_time = thread_time(thread_id) %>
194
+ <h2><a name="<%= thread_id %>">Thread <%= thread_id %></a></h2>
195
+
196
+ <table>
197
+ <tr>
198
+ <th><%= sprintf("%#{PERCENTAGE_WIDTH}s", "%Total") %></th>
199
+ <th><%= sprintf("%#{PERCENTAGE_WIDTH}s", "%Self") %></th>
200
+ <th><%= sprintf("%#{TIME_WIDTH}s", "Total") %></th>
201
+ <th><%= sprintf("%#{TIME_WIDTH}s", "Self") %></th>
202
+ <th><%= sprintf("%#{TIME_WIDTH}s", "Wait") %></th>
203
+ <th><%= sprintf("%#{TIME_WIDTH+2}s", "Child") %></th>
204
+ <th><%= sprintf("%#{CALL_WIDTH}s", "Calls") %></th>
205
+ <th class="method_name">Name</th>
206
+ <th>Line</th>
207
+ </tr>
208
+
209
+ <% min_time = @options[:min_time] || (@options[:nonzero] ? 0.005 : nil)
210
+ methods.sort.reverse_each do |method|
211
+ total_percentage = (method.total_time/total_time) * 100
212
+ next if total_percentage < min_percent
213
+ next if min_time && method.total_time < min_time
214
+ self_percentage = (method.self_time/total_time) * 100 %>
215
+
216
+ <!-- Parents -->
217
+ <% for caller in method.aggregate_parents
218
+ next unless caller.parent
219
+ next if min_time && caller.total_time < min_time %>
220
+ <tr>
221
+ <td>&nbsp;</td>
222
+ <td>&nbsp;</td>
223
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.total_time) %></td>
224
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.self_time) %></td>
225
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.wait_time) %></td>
226
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", caller.children_time) %></td>
227
+ <% called = "#{caller.called}/#{method.called}" %>
228
+ <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
229
+ <td class="method_name"><%= create_link(thread_id, caller.parent.target) %></td>
230
+ <td><%= file_link(caller.parent.target.source_file, caller.line) %></td>
231
+ </tr>
232
+ <% end %>
233
+
234
+ <tr class="method">
235
+ <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", total_percentage) %></td>
236
+ <td><%= sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", self_percentage) %></td>
237
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.total_time) %></td>
238
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.self_time) %></td>
239
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.wait_time) %></td>
240
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", method.children_time) %></td>
241
+ <td><%= sprintf("%#{CALL_WIDTH}i", method.called) %></td>
242
+ <td class="method_name"><a name="<%= method_href(thread_id, method) %>"><%= h method.full_name %></a></td>
243
+ <td><%= file_link(method.source_file, method.line) %></td>
244
+ </tr>
245
+
246
+ <!-- Children -->
247
+ <% for callee in method.aggregate_children %>
248
+ <% next if min_time && callee.total_time < min_time %>
249
+ <tr>
250
+ <td>&nbsp;</td>
251
+ <td>&nbsp;</td>
252
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.total_time) %></td>
253
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.self_time) %></td>
254
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.wait_time) %></td>
255
+ <td><%= sprintf("%#{TIME_WIDTH}.2f", callee.children_time) %></td>
256
+ <% called = "#{callee.called}/#{callee.target.called}" %>
257
+ <td><%= sprintf("%#{CALL_WIDTH}s", called) %></td>
258
+ <td class="method_name"><%= create_link(thread_id, callee.target) %></td>
259
+ <td><%= file_link(method.source_file, callee.line) %></td>
260
+ </tr>
261
+ <% end %>
262
+ <!-- Create divider row -->
263
+ <tr class="break"><td colspan="9"></td></tr>
264
+ <% end %>
265
+ </table>
266
+ <% end %>
267
+ </body>
268
+ </html>'
269
+ end
270
+ end
271
+ end
272
+
@@ -0,0 +1,164 @@
1
+ require 'ruby-prof/abstract_printer'
2
+
3
+ module RubyProf
4
+ # Generates graph[link:files/examples/graph_txt.html] profile reports as text.
5
+ # To use the graph printer:
6
+ #
7
+ # result = RubyProf.profile do
8
+ # [code to profile]
9
+ # end
10
+ #
11
+ # printer = RubyProf::GraphPrinter.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 GraphPrinter < AbstractPrinter
23
+ PERCENTAGE_WIDTH = 8
24
+ TIME_WIDTH = 10
25
+ CALL_WIDTH = 17
26
+
27
+ # Create a GraphPrinter. Result is a RubyProf::Result
28
+ # object generated from a profiling run.
29
+ def initialize(result)
30
+ super(result)
31
+ @thread_times = Hash.new
32
+ calculate_thread_times
33
+ end
34
+
35
+ def calculate_thread_times
36
+ # Cache thread times since this is an expensive
37
+ # operation with the required sorting
38
+ @result.threads.each do |thread_id, methods|
39
+ top = methods.sort.last
40
+
41
+ thread_time = 0.01
42
+ thread_time = top.total_time if top.total_time > 0
43
+
44
+ @thread_times[thread_id] = thread_time
45
+ end
46
+ end
47
+
48
+ # Print a graph report to the provided output.
49
+ #
50
+ # output - Any IO oject, including STDOUT or a file.
51
+ # The default value is STDOUT.
52
+ #
53
+ # options - Hash of print options. See #setup_options
54
+ # for more information.
55
+ #
56
+ def print(output = STDOUT, options = {})
57
+ @output = output
58
+ setup_options(options)
59
+ print_threads
60
+ end
61
+
62
+ private
63
+ def print_threads
64
+ # sort assumes that spawned threads have higher object_ids
65
+ @result.threads.sort.each do |thread_id, methods|
66
+ print_methods(thread_id, methods)
67
+ @output << "\n" * 2
68
+ end
69
+ end
70
+
71
+ def print_methods(thread_id, methods)
72
+ # Sort methods from longest to shortest total time
73
+ methods = methods.sort
74
+
75
+ toplevel = methods.last
76
+ total_time = toplevel.total_time
77
+ if total_time == 0
78
+ total_time = 0.01
79
+ end
80
+
81
+ print_heading(thread_id)
82
+
83
+ # Print each method in total time order
84
+ methods.reverse_each do |method|
85
+ total_percentage = (method.total_time/total_time) * 100
86
+ self_percentage = (method.self_time/total_time) * 100
87
+
88
+ next if total_percentage < min_percent
89
+
90
+ @output << "-" * 80 << "\n"
91
+
92
+ print_parents(thread_id, method)
93
+
94
+ # 1 is for % sign
95
+ @output << sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", total_percentage)
96
+ @output << sprintf("%#{PERCENTAGE_WIDTH-1}.2f\%", self_percentage)
97
+ @output << sprintf("%#{TIME_WIDTH}.2f", method.total_time)
98
+ @output << sprintf("%#{TIME_WIDTH}.2f", method.self_time)
99
+ @output << sprintf("%#{TIME_WIDTH}.2f", method.wait_time)
100
+ @output << sprintf("%#{TIME_WIDTH}.2f", method.children_time)
101
+ @output << sprintf("%#{CALL_WIDTH}i", method.called)
102
+ @output << sprintf(" %s", method_name(method))
103
+ if print_file
104
+ @output << sprintf(" %s:%s", method.source_file, method.line)
105
+ end
106
+ @output << "\n"
107
+
108
+ print_children(method)
109
+ end
110
+ end
111
+
112
+ def print_heading(thread_id)
113
+ @output << "Thread ID: #{thread_id}\n"
114
+ @output << "Total Time: #{@thread_times[thread_id]}\n"
115
+ @output << "\n"
116
+
117
+ # 1 is for % sign
118
+ @output << sprintf("%#{PERCENTAGE_WIDTH}s", "%total")
119
+ @output << sprintf("%#{PERCENTAGE_WIDTH}s", "%self")
120
+ @output << sprintf("%#{TIME_WIDTH}s", "total")
121
+ @output << sprintf("%#{TIME_WIDTH}s", "self")
122
+ @output << sprintf("%#{TIME_WIDTH}s", "wait")
123
+ @output << sprintf("%#{TIME_WIDTH}s", "child")
124
+ @output << sprintf("%#{CALL_WIDTH}s", "calls")
125
+ @output << " Name"
126
+ @output << "\n"
127
+ end
128
+
129
+ def print_parents(thread_id, method)
130
+ method.aggregate_parents.each do |caller|
131
+ next unless caller.parent
132
+ @output << " " * 2 * PERCENTAGE_WIDTH
133
+ @output << sprintf("%#{TIME_WIDTH}.2f", caller.total_time)
134
+ @output << sprintf("%#{TIME_WIDTH}.2f", caller.self_time)
135
+ @output << sprintf("%#{TIME_WIDTH}.2f", caller.wait_time)
136
+ @output << sprintf("%#{TIME_WIDTH}.2f", caller.children_time)
137
+
138
+ call_called = "#{caller.called}/#{method.called}"
139
+ @output << sprintf("%#{CALL_WIDTH}s", call_called)
140
+ @output << sprintf(" %s", caller.parent.target.full_name)
141
+ @output << "\n"
142
+ end
143
+ end
144
+
145
+ def print_children(method)
146
+ method.aggregate_children.each do |child|
147
+ # Get children method
148
+
149
+ @output << " " * 2 * PERCENTAGE_WIDTH
150
+
151
+ @output << sprintf("%#{TIME_WIDTH}.2f", child.total_time)
152
+ @output << sprintf("%#{TIME_WIDTH}.2f", child.self_time)
153
+ @output << sprintf("%#{TIME_WIDTH}.2f", child.wait_time)
154
+ @output << sprintf("%#{TIME_WIDTH}.2f", child.children_time)
155
+
156
+ call_called = "#{child.called}/#{child.target.called}"
157
+ @output << sprintf("%#{CALL_WIDTH}s", call_called)
158
+ @output << sprintf(" %s", child.target.full_name)
159
+ @output << "\n"
160
+ end
161
+ end
162
+ end
163
+ end
164
+