airbnb-ruby-prof 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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