airbnb-ruby-prof 0.0.1

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 (116) hide show
  1. data/CHANGES +483 -0
  2. data/LICENSE +25 -0
  3. data/README.rdoc +426 -0
  4. data/Rakefile +51 -0
  5. data/bin/ruby-prof +279 -0
  6. data/bin/ruby-prof-check-trace +45 -0
  7. data/examples/flat.txt +50 -0
  8. data/examples/graph.dot +84 -0
  9. data/examples/graph.html +823 -0
  10. data/examples/graph.txt +139 -0
  11. data/examples/multi.flat.txt +23 -0
  12. data/examples/multi.graph.html +760 -0
  13. data/examples/multi.grind.dat +114 -0
  14. data/examples/multi.stack.html +547 -0
  15. data/examples/stack.html +547 -0
  16. data/ext/ruby_prof/extconf.rb +67 -0
  17. data/ext/ruby_prof/rp_call_info.c +374 -0
  18. data/ext/ruby_prof/rp_call_info.h +59 -0
  19. data/ext/ruby_prof/rp_fast_call_tree_printer.c +247 -0
  20. data/ext/ruby_prof/rp_fast_call_tree_printer.h +10 -0
  21. data/ext/ruby_prof/rp_measure.c +71 -0
  22. data/ext/ruby_prof/rp_measure.h +56 -0
  23. data/ext/ruby_prof/rp_measure_allocations.c +74 -0
  24. data/ext/ruby_prof/rp_measure_cpu_time.c +134 -0
  25. data/ext/ruby_prof/rp_measure_gc_runs.c +71 -0
  26. data/ext/ruby_prof/rp_measure_gc_time.c +58 -0
  27. data/ext/ruby_prof/rp_measure_memory.c +75 -0
  28. data/ext/ruby_prof/rp_measure_process_time.c +69 -0
  29. data/ext/ruby_prof/rp_measure_wall_time.c +43 -0
  30. data/ext/ruby_prof/rp_method.c +717 -0
  31. data/ext/ruby_prof/rp_method.h +79 -0
  32. data/ext/ruby_prof/rp_stack.c +221 -0
  33. data/ext/ruby_prof/rp_stack.h +81 -0
  34. data/ext/ruby_prof/rp_thread.c +312 -0
  35. data/ext/ruby_prof/rp_thread.h +36 -0
  36. data/ext/ruby_prof/ruby_prof.c +800 -0
  37. data/ext/ruby_prof/ruby_prof.h +64 -0
  38. data/ext/ruby_prof/vc/ruby_prof.sln +32 -0
  39. data/ext/ruby_prof/vc/ruby_prof_18.vcxproj +108 -0
  40. data/ext/ruby_prof/vc/ruby_prof_19.vcxproj +110 -0
  41. data/ext/ruby_prof/vc/ruby_prof_20.vcxproj +110 -0
  42. data/lib/ruby-prof.rb +63 -0
  43. data/lib/ruby-prof/aggregate_call_info.rb +76 -0
  44. data/lib/ruby-prof/assets/call_stack_printer.css.html +117 -0
  45. data/lib/ruby-prof/assets/call_stack_printer.js.html +385 -0
  46. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  47. data/lib/ruby-prof/assets/flame_graph_printer.lib.css.html +149 -0
  48. data/lib/ruby-prof/assets/flame_graph_printer.lib.js.html +707 -0
  49. data/lib/ruby-prof/assets/flame_graph_printer.page.js.html +56 -0
  50. data/lib/ruby-prof/assets/flame_graph_printer.tmpl.html.erb +39 -0
  51. data/lib/ruby-prof/call_info.rb +111 -0
  52. data/lib/ruby-prof/call_info_visitor.rb +40 -0
  53. data/lib/ruby-prof/compatibility.rb +186 -0
  54. data/lib/ruby-prof/method_info.rb +109 -0
  55. data/lib/ruby-prof/printers/abstract_printer.rb +85 -0
  56. data/lib/ruby-prof/printers/call_info_printer.rb +41 -0
  57. data/lib/ruby-prof/printers/call_stack_printer.rb +260 -0
  58. data/lib/ruby-prof/printers/call_tree_printer.rb +130 -0
  59. data/lib/ruby-prof/printers/dot_printer.rb +132 -0
  60. data/lib/ruby-prof/printers/fast_call_tree_printer.rb +87 -0
  61. data/lib/ruby-prof/printers/flame_graph_html_printer.rb +59 -0
  62. data/lib/ruby-prof/printers/flame_graph_json_printer.rb +157 -0
  63. data/lib/ruby-prof/printers/flat_printer.rb +70 -0
  64. data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +64 -0
  65. data/lib/ruby-prof/printers/graph_html_printer.rb +244 -0
  66. data/lib/ruby-prof/printers/graph_printer.rb +116 -0
  67. data/lib/ruby-prof/printers/multi_printer.rb +58 -0
  68. data/lib/ruby-prof/profile.rb +22 -0
  69. data/lib/ruby-prof/profile/exclude_common_methods.rb +201 -0
  70. data/lib/ruby-prof/rack.rb +95 -0
  71. data/lib/ruby-prof/task.rb +147 -0
  72. data/lib/ruby-prof/thread.rb +35 -0
  73. data/lib/ruby-prof/version.rb +4 -0
  74. data/lib/ruby-prof/walker.rb +95 -0
  75. data/lib/unprof.rb +10 -0
  76. data/ruby-prof.gemspec +56 -0
  77. data/test/aggregate_test.rb +136 -0
  78. data/test/basic_test.rb +128 -0
  79. data/test/block_test.rb +74 -0
  80. data/test/call_info_test.rb +78 -0
  81. data/test/call_info_visitor_test.rb +31 -0
  82. data/test/duplicate_names_test.rb +32 -0
  83. data/test/dynamic_method_test.rb +55 -0
  84. data/test/enumerable_test.rb +21 -0
  85. data/test/exceptions_test.rb +16 -0
  86. data/test/exclude_methods_test.rb +146 -0
  87. data/test/exclude_threads_test.rb +53 -0
  88. data/test/fiber_test.rb +79 -0
  89. data/test/issue137_test.rb +63 -0
  90. data/test/line_number_test.rb +71 -0
  91. data/test/measure_allocations_test.rb +26 -0
  92. data/test/measure_cpu_time_test.rb +213 -0
  93. data/test/measure_gc_runs_test.rb +32 -0
  94. data/test/measure_gc_time_test.rb +36 -0
  95. data/test/measure_memory_test.rb +33 -0
  96. data/test/measure_process_time_test.rb +63 -0
  97. data/test/measure_wall_time_test.rb +255 -0
  98. data/test/module_test.rb +45 -0
  99. data/test/multi_measure_test.rb +38 -0
  100. data/test/multi_printer_test.rb +83 -0
  101. data/test/no_method_class_test.rb +15 -0
  102. data/test/pause_resume_test.rb +166 -0
  103. data/test/prime.rb +54 -0
  104. data/test/printers_test.rb +255 -0
  105. data/test/printing_recursive_graph_test.rb +127 -0
  106. data/test/rack_test.rb +93 -0
  107. data/test/recursive_test.rb +212 -0
  108. data/test/singleton_test.rb +38 -0
  109. data/test/stack_printer_test.rb +65 -0
  110. data/test/stack_test.rb +138 -0
  111. data/test/start_stop_test.rb +112 -0
  112. data/test/test_helper.rb +264 -0
  113. data/test/thread_test.rb +187 -0
  114. data/test/unique_call_path_test.rb +202 -0
  115. data/test/yarv_test.rb +55 -0
  116. metadata +211 -0
@@ -0,0 +1,85 @@
1
+ # encoding: utf-8
2
+
3
+ module RubyProf
4
+ class AbstractPrinter
5
+ # Create a new printer.
6
+ #
7
+ # result should be the output generated from a profiling run
8
+ def initialize(result)
9
+ @result = result
10
+ @output = nil
11
+ end
12
+
13
+ # Specify print options.
14
+ #
15
+ # options - Hash table
16
+ # :min_percent - Number 0 to 100 that specifes the minimum
17
+ # %self (the methods self time divided by the
18
+ # overall total time) that a method must take
19
+ # for it to be printed out in the report.
20
+ # Default value is 0.
21
+ #
22
+ # :print_file - True or false. Specifies if a method's source
23
+ # file should be printed. Default value if false.
24
+ #
25
+ # :sort_method - Specifies method used for sorting method infos.
26
+ # Available values are :total_time, :self_time,
27
+ # :wait_time, :children_time
28
+ # Default value is :total_time
29
+ def setup_options(options = {})
30
+ @options = options
31
+ end
32
+
33
+ def min_percent
34
+ @options[:min_percent] || 0
35
+ end
36
+
37
+ def print_file
38
+ @options[:print_file] || false
39
+ end
40
+
41
+ def sort_method
42
+ @options[:sort_method] || :total_time
43
+ end
44
+
45
+ def method_name(method)
46
+ name = method.full_name
47
+ if print_file
48
+ name += " (#{method.source_file}:#{method.line}}"
49
+ end
50
+ name
51
+ end
52
+
53
+ # Print a profiling report to the provided output.
54
+ #
55
+ # output - Any IO object, including STDOUT or a file.
56
+ # The default value is STDOUT.
57
+ #
58
+ # options - Hash of print options. See #setup_options
59
+ # for more information. Note that each printer can
60
+ # define its own set of options.
61
+ def print(output = STDOUT, options = {})
62
+ @output = output
63
+ setup_options(options)
64
+ print_threads
65
+ end
66
+
67
+ def print_threads
68
+ @result.threads.each do |thread|
69
+ print_thread(thread)
70
+ end
71
+ end
72
+
73
+ def print_thread(thread)
74
+ print_header(thread)
75
+ print_methods(thread)
76
+ print_footer(thread)
77
+ end
78
+
79
+ def print_header(thread)
80
+ end
81
+
82
+ def print_footer(thread)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+
3
+ module RubyProf
4
+ # Prints out the call graph based on CallInfo instances. This
5
+ # is mainly for debugging purposes as it provides access into
6
+ # into RubyProf's internals.
7
+
8
+ class CallInfoPrinter < AbstractPrinter
9
+ TIME_WIDTH = 0
10
+
11
+ private
12
+
13
+ def print_header(thread)
14
+ @output << "Thread ID: #{thread.id}\n"
15
+ @output << "Fiber ID: #{thread.fiber_id}\n"
16
+ @output << "Total Time: #{thread.total_time}\n"
17
+ @output << "Sort by: #{sort_method}\n"
18
+ @output << "\n"
19
+ end
20
+
21
+ def print_methods(thread)
22
+ visitor = CallInfoVisitor.new(thread.top_call_infos)
23
+
24
+ visitor.visit do |call_info, event|
25
+ if event == :enter
26
+ @output << " " * call_info.depth
27
+ @output << call_info.target.full_name
28
+ @output << " ("
29
+ @output << "tt:#{sprintf("%#{TIME_WIDTH}.2f", call_info.total_time)}, "
30
+ @output << "st:#{sprintf("%#{TIME_WIDTH}.2f", call_info.self_time)}, "
31
+ @output << "wt:#{sprintf("%#{TIME_WIDTH}.2f", call_info.wait_time)}, "
32
+ @output << "ct:#{sprintf("%#{TIME_WIDTH}.2f", call_info.children_time)}, "
33
+ @output << "call:#{call_info.called}, "
34
+ @output << "rec:#{call_info.recursive?}"
35
+ @output << ")"
36
+ @output << "\n"
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,260 @@
1
+ # encoding: utf-8
2
+
3
+ require 'erb'
4
+ require 'fileutils'
5
+ require 'base64'
6
+
7
+ module RubyProf
8
+ # prints a HTML visualization of the call tree
9
+ class CallStackPrinter < AbstractPrinter
10
+ include ERB::Util
11
+
12
+ # Specify print options.
13
+ #
14
+ # options - Hash table
15
+ # :min_percent - Number 0 to 100 that specifes the minimum
16
+ # %self (the methods self time divided by the
17
+ # overall total time) that a method must take
18
+ # for it to be printed out in the report.
19
+ # Default value is 0.
20
+ #
21
+ # :print_file - True or false. Specifies if a method's source
22
+ # file should be printed. Default value if false.
23
+ #
24
+ # :threshold - a float from 0 to 100 that sets the threshold of
25
+ # results displayed.
26
+ # Default value is 1.0
27
+ #
28
+ # :title - a String to overide the default "ruby-prof call tree"
29
+ # title of the report.
30
+ #
31
+ # :expansion - a float from 0 to 100 that sets the threshold of
32
+ # results that are expanded, if the percent_total
33
+ # exceeds it.
34
+ # Default value is 10.0
35
+ #
36
+ # :application - a String to overide the name of the application,
37
+ # as it appears on the report.
38
+ #
39
+ def print(output = STDOUT, options = {})
40
+ @output = output
41
+ setup_options(options)
42
+ if @graph_html = options.delete(:graph)
43
+ @graph_html = "file://" + @graph_html if @graph_html[0]=="/"
44
+ end
45
+
46
+ print_header
47
+
48
+ @overall_threads_time = @result.threads.inject(0) do |val, thread|
49
+ val += thread.total_time
50
+ end
51
+
52
+ @result.threads.each do |thread|
53
+ @current_thread_id = thread.fiber_id
54
+ @overall_time = thread.total_time
55
+ thread_info = "Thread: #{thread.id}"
56
+ thread_info << ", Fiber: #{thread.fiber_id}" unless thread.id == thread.fiber_id
57
+ thread_info << " (#{"%4.2f%%" % ((@overall_time/@overall_threads_time)*100)} ~ #{@overall_time})"
58
+ @output.print "<div class=\"thread\">#{thread_info}</div>"
59
+ @output.print "<ul name=\"thread\">"
60
+ thread.methods.each do |m|
61
+ # $stderr.print m.dump
62
+ next unless m.root?
63
+ m.call_infos.each do |ci|
64
+ next unless ci.root?
65
+ print_stack ci, thread.total_time
66
+ end
67
+ end
68
+ @output.print "</ul>"
69
+ end
70
+
71
+ print_footer
72
+
73
+ end
74
+
75
+ def print_stack(call_info, parent_time)
76
+ total_time = call_info.total_time
77
+ percent_parent = (total_time/parent_time)*100
78
+ percent_total = (total_time/@overall_time)*100
79
+ return unless percent_total > min_percent
80
+ color = self.color(percent_total)
81
+ kids = call_info.children
82
+ visible = percent_total >= threshold
83
+ expanded = percent_total >= expansion
84
+ display = visible ? "block" : "none"
85
+ @output.print "<li class=\"color#{color}\" style=\"display:#{display}\">"
86
+ if kids.empty?
87
+ @output.print "<a href=\"#\" class=\"toggle empty\" ></a>"
88
+ else
89
+ visible_children = kids.any?{|ci| (ci.total_time/@overall_time)*100 >= threshold}
90
+ image = visible_children ? (expanded ? "minus" : "plus") : "empty"
91
+ @output.print "<a href=\"#\" class=\"toggle #{image}\" ></a>"
92
+ end
93
+ @output.printf "<span> %4.2f%% (%4.2f%%) %s %s</span>\n", percent_total, percent_parent, link(call_info), graph_link(call_info)
94
+ unless kids.empty?
95
+ if expanded
96
+ @output.print "<ul>"
97
+ else
98
+ @output.print '<ul style="display:none">'
99
+ end
100
+ kids.sort_by{|c| -c.total_time}.each do |callinfo|
101
+ print_stack callinfo, total_time
102
+ end
103
+ @output.print "</ul>"
104
+ end
105
+ @output.print "</li>"
106
+ end
107
+
108
+ def name(call_info)
109
+ method = call_info.target
110
+ method.full_name
111
+ end
112
+
113
+ def link(call_info)
114
+ method = call_info.target
115
+ file = File.expand_path(method.source_file)
116
+ if file =~ /\/ruby_runtime$/
117
+ h(name(call_info))
118
+ else
119
+ if RUBY_PLATFORM =~ /darwin/
120
+ "<a href=\"txmt://open?url=file://#{file}&line=#{method.line}\">#{h(name(call_info))}</a>"
121
+ else
122
+ "<a href=\"file://#{file}##{method.line}\">#{h(name(call_info))}</a>"
123
+ end
124
+ end
125
+ end
126
+
127
+ def graph_link(call_info)
128
+ total_calls = call_info.target.call_infos.inject(0){|t, ci| t += ci.called}
129
+ href = "#{@graph_html}##{method_href(call_info.target)}"
130
+ totals = @graph_html ? "<a href='#{href}'>#{total_calls}</a>" : total_calls.to_s
131
+ "[#{call_info.called} calls, #{totals} total]"
132
+ end
133
+
134
+ def method_href(method)
135
+ h(method.full_name.gsub(/[><#\.\?=:]/,"_") + "_" + @current_thread_id.to_s)
136
+ end
137
+
138
+ def total_time(call_infos)
139
+ sum(call_infos.map{|ci| ci.total_time})
140
+ end
141
+
142
+ def sum(a)
143
+ a.inject(0.0){|s,t| s+=t}
144
+ end
145
+
146
+ def dump(ci)
147
+ $stderr.printf "%s/%d t:%f s:%f w:%f \n", ci, ci.object_id, ci.total_time, ci.self_time, ci.wait_time
148
+ end
149
+
150
+ def color(p)
151
+ case i = p.to_i
152
+ when 0..5
153
+ "01"
154
+ when 5..10
155
+ "05"
156
+ when 100
157
+ "9"
158
+ else
159
+ "#{i/10}"
160
+ end
161
+ end
162
+
163
+ def application
164
+ @options[:application] || $PROGRAM_NAME
165
+ end
166
+
167
+ def arguments
168
+ ARGV.join(' ')
169
+ end
170
+
171
+ def title
172
+ @title ||= @options.delete(:title) || "ruby-prof call tree"
173
+ end
174
+
175
+ def threshold
176
+ @options[:threshold] || 1.0
177
+ end
178
+
179
+ def expansion
180
+ @options[:expansion] || 10.0
181
+ end
182
+
183
+ def print_header
184
+ @output.puts "<html><head>"
185
+ @output.puts '<meta http-equiv="content-type" content="text/html; charset=utf-8">'
186
+ @output.puts "<title>#{h title}</title>"
187
+ print_css
188
+ print_java_script
189
+ @output.puts '</head><body><div style="display: inline-block;">'
190
+ print_title_bar
191
+ print_commands
192
+ print_help
193
+ end
194
+
195
+ def print_footer
196
+ @output.puts '<div id="sentinel"></div></div></body></html>'
197
+ end
198
+
199
+ def open_asset(file)
200
+ path = File.join(File.expand_path('../../assets', __FILE__), file)
201
+ File.open(path, 'rb').read
202
+ end
203
+
204
+ def print_css
205
+ html = open_asset('call_stack_printer.css.html')
206
+ @output.puts html.gsub('%s', base64_image)
207
+ end
208
+
209
+ def base64_image
210
+ file = open_asset('call_stack_printer.png')
211
+ Base64.encode64(file).gsub(/\n/, '')
212
+ end
213
+
214
+ def print_java_script
215
+ html = open_asset('call_stack_printer.js.html')
216
+ @output.puts html
217
+ end
218
+
219
+ def print_title_bar
220
+ @output.puts <<-"end_title_bar"
221
+ <div id="titlebar">
222
+ Call tree for application <b>#{h application} #{h arguments}</b><br/>
223
+ Generated on #{Time.now} with options #{h @options.inspect}<br/>
224
+ </div>
225
+ end_title_bar
226
+ end
227
+
228
+ def print_commands
229
+ @output.puts <<-"end_commands"
230
+ <div id=\"commands\">
231
+ <span style=\"font-size: 11pt; font-weight: bold;\">Threshold:</span>
232
+ <input value=\"#{h threshold}\" size=\"3\" id=\"threshold\" type=\"text\">
233
+ <input value=\"Apply\" onclick=\"setThreshold();\" type=\"submit\">
234
+ <input value=\"Expand All\" onclick=\"expandAll(event);\" type=\"submit\">
235
+ <input value=\"Collapse All\" onclick=\"collapseAll(event);\" type=\"submit\">
236
+ <input value=\"Show Help\" onclick=\"toggleHelp(this);\" type=\"submit\">
237
+ </div>
238
+ end_commands
239
+ end
240
+
241
+ def print_help
242
+ @output.puts <<-'end_help'
243
+ <div style="display: none;" id="help">
244
+ &#8226; Enter a decimal value <i>d</i> into the threshold field and click "Apply"
245
+ to hide all nodes marked with time values lower than <i>d</i>.<br>
246
+ &#8226; Click on "Expand All" for full tree expansion.<br>
247
+ &#8226; Click on "Collapse All" to show only top level nodes.<br>
248
+ &#8226; Use a, s, d, w as in Quake or Urban Terror to navigate the tree.<br>
249
+ &#8226; Use f and b to navigate the tree in preorder forward and backwards.<br>
250
+ &#8226; Use x to toggle visibility of a subtree.<br>
251
+ &#8226; Use * to expand/collapse a whole subtree.<br>
252
+ &#8226; Use h to navigate to thread root.<br>
253
+ &#8226; Use n and p to navigate between threads.<br>
254
+ &#8226; Click on background to move focus to a subtree.<br>
255
+ </div>
256
+ end_help
257
+ end
258
+ end
259
+ end
260
+
@@ -0,0 +1,130 @@
1
+ # encoding: utf-8
2
+
3
+ require 'fiber'
4
+ require 'thread'
5
+ require 'fileutils'
6
+
7
+ module RubyProf
8
+ # Generate profiling information in callgrind format for use by
9
+ # kcachegrind and similar tools.
10
+ #
11
+ # Note: when profiling for a callgrind printer, one should use the
12
+ # merge_fibers: true option when creating the profile. Otherwise
13
+ # each fiber would appear as a separate profile.
14
+
15
+ class CallTreePrinter < AbstractPrinter
16
+
17
+ def determine_event_specification_and_value_scale
18
+ @event_specification = "events: "
19
+ case @result.measure_modes.first
20
+ when RubyProf::PROCESS_TIME
21
+ @value_scale = RubyProf::CLOCKS_PER_SEC
22
+ @event_specification << 'process_time'
23
+ when RubyProf::WALL_TIME
24
+ @value_scale = 1_000_000
25
+ @event_specification << 'wall_time'
26
+ when RubyProf.const_defined?(:CPU_TIME) && RubyProf::CPU_TIME
27
+ @value_scale = RubyProf.cpu_frequency
28
+ @event_specification << 'cpu_time'
29
+ when RubyProf.const_defined?(:ALLOCATIONS) && RubyProf::ALLOCATIONS
30
+ @value_scale = 1
31
+ @event_specification << 'allocations'
32
+ when RubyProf.const_defined?(:MEMORY) && RubyProf::MEMORY
33
+ @value_scale = 1
34
+ @event_specification << 'memory'
35
+ when RubyProf.const_defined?(:GC_RUNS) && RubyProf::GC_RUNS
36
+ @value_scale = 1
37
+ @event_specification << 'gc_runs'
38
+ when RubyProf.const_defined?(:GC_TIME) && RubyProf::GC_TIME
39
+ @value_scale = 1000000
40
+ @event_specification << 'gc_time'
41
+ else
42
+ raise "Unknown measure mode: #{RubyProf.measure_mode}"
43
+ end
44
+ end
45
+
46
+ def print(options = {})
47
+ setup_options(options)
48
+ determine_event_specification_and_value_scale
49
+ print_threads
50
+ end
51
+
52
+ def print_threads
53
+ remove_subsidiary_files_from_previous_profile_runs
54
+ # TODO: merge fibers of a given thread here, instead of relying
55
+ # on the profiler to merge fibers.
56
+ @result.threads.each do |thread|
57
+ print_thread(thread)
58
+ end
59
+ end
60
+
61
+ def convert(value)
62
+ (value * @value_scale).round
63
+ end
64
+
65
+ def file(method)
66
+ File.expand_path(method.source_file)
67
+ end
68
+
69
+ def print_thread(thread)
70
+ File.open(file_path_for_thread(thread), "w") do |f|
71
+ print_headers(f, thread)
72
+ thread.methods.reverse_each do |method|
73
+ print_method(f, method)
74
+ end
75
+ end
76
+ end
77
+
78
+ def path
79
+ @options[:path] || "."
80
+ end
81
+
82
+ def base_name
83
+ @options[:profile] || "profile"
84
+ end
85
+
86
+ def remove_subsidiary_files_from_previous_profile_runs
87
+ pattern = [base_name, "callgrind.out", $$, "*"].join(".")
88
+ files = Dir.glob(File.join(path, pattern))
89
+ FileUtils.rm_f(files)
90
+ end
91
+
92
+ def file_name_for_thread(thread)
93
+ if thread.fiber_id == Fiber.current.object_id
94
+ [base_name, "callgrind.out", $$].join(".")
95
+ else
96
+ [base_name, "callgrind.out", $$, thread.fiber_id].join(".")
97
+ end
98
+ end
99
+
100
+ def file_path_for_thread(thread)
101
+ File.join(path, file_name_for_thread(thread))
102
+ end
103
+
104
+ def print_headers(output, thread)
105
+ output << "#{@event_specification}\n\n"
106
+ # this doesn't work. kcachegrind does not fully support the spec.
107
+ # output << "thread: #{thread.id}\n\n"
108
+ end
109
+
110
+ def print_method(output, method)
111
+ # Print out the file and method name
112
+ output << "fl=#{file(method)}\n"
113
+ output << "fn=#{method.calltree_name}\n"
114
+
115
+ # Now print out the function line number and its self time
116
+ output << "#{method.line} #{convert(method.self_time)}\n"
117
+
118
+ # Now print out all the children methods
119
+ method.children.each do |callee|
120
+ output << "cfl=#{file(callee.target)}\n"
121
+ output << "cfn=#{callee.target.calltree_name}\n"
122
+ output << "calls=#{callee.called} #{callee.line}\n"
123
+
124
+ # Print out total times here!
125
+ output << "#{callee.line} #{convert(callee.total_time)}\n"
126
+ end
127
+ output << "\n"
128
+ end
129
+ end
130
+ end