ruby-prof 0.18.0-x64-mingw32

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 (108) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +500 -0
  3. data/LICENSE +25 -0
  4. data/README.rdoc +487 -0
  5. data/Rakefile +113 -0
  6. data/bin/ruby-prof +345 -0
  7. data/bin/ruby-prof-check-trace +45 -0
  8. data/examples/flat.txt +50 -0
  9. data/examples/graph.dot +84 -0
  10. data/examples/graph.html +823 -0
  11. data/examples/graph.txt +139 -0
  12. data/examples/multi.flat.txt +23 -0
  13. data/examples/multi.graph.html +760 -0
  14. data/examples/multi.grind.dat +114 -0
  15. data/examples/multi.stack.html +547 -0
  16. data/examples/stack.html +547 -0
  17. data/ext/ruby_prof/extconf.rb +68 -0
  18. data/ext/ruby_prof/rp_call_info.c +425 -0
  19. data/ext/ruby_prof/rp_call_info.h +53 -0
  20. data/ext/ruby_prof/rp_measure.c +40 -0
  21. data/ext/ruby_prof/rp_measure.h +45 -0
  22. data/ext/ruby_prof/rp_measure_allocations.c +76 -0
  23. data/ext/ruby_prof/rp_measure_cpu_time.c +136 -0
  24. data/ext/ruby_prof/rp_measure_gc_runs.c +73 -0
  25. data/ext/ruby_prof/rp_measure_gc_time.c +60 -0
  26. data/ext/ruby_prof/rp_measure_memory.c +77 -0
  27. data/ext/ruby_prof/rp_measure_process_time.c +71 -0
  28. data/ext/ruby_prof/rp_measure_wall_time.c +45 -0
  29. data/ext/ruby_prof/rp_method.c +630 -0
  30. data/ext/ruby_prof/rp_method.h +75 -0
  31. data/ext/ruby_prof/rp_stack.c +173 -0
  32. data/ext/ruby_prof/rp_stack.h +63 -0
  33. data/ext/ruby_prof/rp_thread.c +277 -0
  34. data/ext/ruby_prof/rp_thread.h +27 -0
  35. data/ext/ruby_prof/ruby_prof.c +794 -0
  36. data/ext/ruby_prof/ruby_prof.h +60 -0
  37. data/ext/ruby_prof/vc/ruby_prof.sln +31 -0
  38. data/ext/ruby_prof/vc/ruby_prof.vcxproj +141 -0
  39. data/lib/2.6.3/ruby_prof.so +0 -0
  40. data/lib/ruby-prof.rb +68 -0
  41. data/lib/ruby-prof/aggregate_call_info.rb +76 -0
  42. data/lib/ruby-prof/assets/call_stack_printer.css.html +117 -0
  43. data/lib/ruby-prof/assets/call_stack_printer.js.html +385 -0
  44. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  45. data/lib/ruby-prof/call_info.rb +115 -0
  46. data/lib/ruby-prof/call_info_visitor.rb +40 -0
  47. data/lib/ruby-prof/compatibility.rb +179 -0
  48. data/lib/ruby-prof/method_info.rb +121 -0
  49. data/lib/ruby-prof/printers/abstract_printer.rb +104 -0
  50. data/lib/ruby-prof/printers/call_info_printer.rb +41 -0
  51. data/lib/ruby-prof/printers/call_stack_printer.rb +265 -0
  52. data/lib/ruby-prof/printers/call_tree_printer.rb +143 -0
  53. data/lib/ruby-prof/printers/dot_printer.rb +132 -0
  54. data/lib/ruby-prof/printers/flat_printer.rb +70 -0
  55. data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +83 -0
  56. data/lib/ruby-prof/printers/graph_html_printer.rb +249 -0
  57. data/lib/ruby-prof/printers/graph_printer.rb +116 -0
  58. data/lib/ruby-prof/printers/multi_printer.rb +84 -0
  59. data/lib/ruby-prof/profile.rb +26 -0
  60. data/lib/ruby-prof/profile/exclude_common_methods.rb +207 -0
  61. data/lib/ruby-prof/profile/legacy_method_elimination.rb +50 -0
  62. data/lib/ruby-prof/rack.rb +174 -0
  63. data/lib/ruby-prof/task.rb +147 -0
  64. data/lib/ruby-prof/thread.rb +35 -0
  65. data/lib/ruby-prof/version.rb +3 -0
  66. data/lib/unprof.rb +10 -0
  67. data/ruby-prof.gemspec +58 -0
  68. data/test/abstract_printer_test.rb +53 -0
  69. data/test/aggregate_test.rb +136 -0
  70. data/test/basic_test.rb +128 -0
  71. data/test/block_test.rb +74 -0
  72. data/test/call_info_test.rb +78 -0
  73. data/test/call_info_visitor_test.rb +31 -0
  74. data/test/duplicate_names_test.rb +32 -0
  75. data/test/dynamic_method_test.rb +55 -0
  76. data/test/enumerable_test.rb +21 -0
  77. data/test/exceptions_test.rb +24 -0
  78. data/test/exclude_methods_test.rb +146 -0
  79. data/test/exclude_threads_test.rb +53 -0
  80. data/test/fiber_test.rb +79 -0
  81. data/test/issue137_test.rb +63 -0
  82. data/test/line_number_test.rb +80 -0
  83. data/test/measure_allocations_test.rb +26 -0
  84. data/test/measure_cpu_time_test.rb +212 -0
  85. data/test/measure_gc_runs_test.rb +32 -0
  86. data/test/measure_gc_time_test.rb +36 -0
  87. data/test/measure_memory_test.rb +33 -0
  88. data/test/measure_process_time_test.rb +61 -0
  89. data/test/measure_wall_time_test.rb +255 -0
  90. data/test/method_elimination_test.rb +84 -0
  91. data/test/module_test.rb +45 -0
  92. data/test/multi_printer_test.rb +104 -0
  93. data/test/no_method_class_test.rb +15 -0
  94. data/test/pause_resume_test.rb +166 -0
  95. data/test/prime.rb +54 -0
  96. data/test/printers_test.rb +275 -0
  97. data/test/printing_recursive_graph_test.rb +127 -0
  98. data/test/rack_test.rb +157 -0
  99. data/test/recursive_test.rb +215 -0
  100. data/test/singleton_test.rb +38 -0
  101. data/test/stack_printer_test.rb +77 -0
  102. data/test/stack_test.rb +138 -0
  103. data/test/start_stop_test.rb +112 -0
  104. data/test/test_helper.rb +267 -0
  105. data/test/thread_test.rb +187 -0
  106. data/test/unique_call_path_test.rb +202 -0
  107. data/test/yarv_test.rb +55 -0
  108. metadata +199 -0
@@ -0,0 +1,265 @@
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
+ # :editor_uri - Specifies editor uri scheme used for opening files
40
+ # e.g. :atm or :mvim. For OS X default is :txmt.
41
+ # Use RUBY_PROF_EDITOR_URI environment variable to overide.
42
+ def print(output = STDOUT, options = {})
43
+ @output = output
44
+ setup_options(options)
45
+ @editor = editor_uri
46
+ if @graph_html = options.delete(:graph)
47
+ @graph_html = "file://" + @graph_html if @graph_html[0]=="/"
48
+ end
49
+
50
+ print_header
51
+
52
+ @overall_threads_time = @result.threads.inject(0) do |val, thread|
53
+ val += thread.total_time
54
+ end
55
+
56
+ @result.threads.each do |thread|
57
+ @current_thread_id = thread.fiber_id
58
+ @overall_time = thread.total_time
59
+ thread_info = String.new("Thread: #{thread.id}")
60
+ thread_info << ", Fiber: #{thread.fiber_id}" unless thread.id == thread.fiber_id
61
+ thread_info << " (#{"%4.2f%%" % ((@overall_time/@overall_threads_time)*100)} ~ #{@overall_time})"
62
+ @output.print "<div class=\"thread\">#{thread_info}</div>"
63
+ @output.print "<ul name=\"thread\">"
64
+ thread.methods.each do |m|
65
+ # $stderr.print m.dump
66
+ next unless m.root?
67
+ m.call_infos.each do |ci|
68
+ next unless ci.root?
69
+ print_stack ci, thread.total_time
70
+ end
71
+ end
72
+ @output.print "</ul>"
73
+ end
74
+
75
+ print_footer
76
+
77
+ end
78
+
79
+ def print_stack(call_info, parent_time)
80
+ total_time = call_info.total_time
81
+ percent_parent = (total_time/parent_time)*100
82
+ percent_total = (total_time/@overall_time)*100
83
+ return unless percent_total > min_percent
84
+ color = self.color(percent_total)
85
+ kids = call_info.children
86
+ visible = percent_total >= threshold
87
+ expanded = percent_total >= expansion
88
+ display = visible ? "block" : "none"
89
+ @output.print "<li class=\"color#{color}\" style=\"display:#{display}\">"
90
+ if kids.empty?
91
+ @output.print "<a href=\"#\" class=\"toggle empty\" ></a>"
92
+ else
93
+ visible_children = kids.any?{|ci| (ci.total_time/@overall_time)*100 >= threshold}
94
+ image = visible_children ? (expanded ? "minus" : "plus") : "empty"
95
+ @output.print "<a href=\"#\" class=\"toggle #{image}\" ></a>"
96
+ end
97
+ @output.printf "<span> %4.2f%% (%4.2f%%) %s %s</span>\n", percent_total, percent_parent, link(call_info), graph_link(call_info)
98
+ unless kids.empty?
99
+ if expanded
100
+ @output.print "<ul>"
101
+ else
102
+ @output.print '<ul style="display:none">'
103
+ end
104
+ kids.sort_by{|c| -c.total_time}.each do |callinfo|
105
+ print_stack callinfo, total_time
106
+ end
107
+ @output.print "</ul>"
108
+ end
109
+ @output.print "</li>"
110
+ end
111
+
112
+ def name(call_info)
113
+ method = call_info.target
114
+ method.full_name
115
+ end
116
+
117
+ def link(call_info)
118
+ method = call_info.target
119
+ file = File.expand_path(method.source_file)
120
+ if file =~ /\/ruby_runtime$/
121
+ h(name(call_info))
122
+ else
123
+ if @editor
124
+ "<a href=\"#{@editor}://" \
125
+ "open?url=file://#{file}&line=#{method.line}\">" \
126
+ "#{h(name(call_info))}</a>"
127
+ else
128
+ "<a href=\"file://#{file}##{method.line}\">#{h(name(call_info))}</a>"
129
+ end
130
+ end
131
+ end
132
+
133
+ def graph_link(call_info)
134
+ total_calls = call_info.target.call_infos.inject(0){|t, ci| t += ci.called}
135
+ href = "#{@graph_html}##{method_href(call_info.target)}"
136
+ totals = @graph_html ? "<a href='#{href}'>#{total_calls}</a>" : total_calls.to_s
137
+ "[#{call_info.called} calls, #{totals} total]"
138
+ end
139
+
140
+ def method_href(method)
141
+ h(method.full_name.gsub(/[><#\.\?=:]/,"_") + "_" + @current_thread_id.to_s)
142
+ end
143
+
144
+ def total_time(call_infos)
145
+ sum(call_infos.map{|ci| ci.total_time})
146
+ end
147
+
148
+ def sum(a)
149
+ a.inject(0.0){|s,t| s+=t}
150
+ end
151
+
152
+ def dump(ci)
153
+ $stderr.printf "%s/%d t:%f s:%f w:%f \n", ci, ci.object_id, ci.total_time, ci.self_time, ci.wait_time
154
+ end
155
+
156
+ def color(p)
157
+ case i = p.to_i
158
+ when 0..5
159
+ "01"
160
+ when 5..10
161
+ "05"
162
+ when 100
163
+ "9"
164
+ else
165
+ "#{i/10}"
166
+ end
167
+ end
168
+
169
+ def application
170
+ @options[:application] || $PROGRAM_NAME
171
+ end
172
+
173
+ def arguments
174
+ ARGV.join(' ')
175
+ end
176
+
177
+ def title
178
+ @title ||= @options.delete(:title) || "ruby-prof call tree"
179
+ end
180
+
181
+ def threshold
182
+ @options[:threshold] || 1.0
183
+ end
184
+
185
+ def expansion
186
+ @options[:expansion] || 10.0
187
+ end
188
+
189
+ def print_header
190
+ @output.puts "<html><head>"
191
+ @output.puts '<meta http-equiv="content-type" content="text/html; charset=utf-8">'
192
+ @output.puts "<title>#{h title}</title>"
193
+ print_css
194
+ print_java_script
195
+ @output.puts '</head><body><div style="display: inline-block;">'
196
+ print_title_bar
197
+ print_commands
198
+ print_help
199
+ end
200
+
201
+ def print_footer
202
+ @output.puts '<div id="sentinel"></div></div></body></html>'
203
+ end
204
+
205
+ def open_asset(file)
206
+ path = File.join(File.expand_path('../../assets', __FILE__), file)
207
+ File.open(path, 'rb').read
208
+ end
209
+
210
+ def print_css
211
+ html = open_asset('call_stack_printer.css.html')
212
+ @output.puts html.gsub('%s', base64_image)
213
+ end
214
+
215
+ def base64_image
216
+ file = open_asset('call_stack_printer.png')
217
+ Base64.encode64(file).gsub(/\n/, '')
218
+ end
219
+
220
+ def print_java_script
221
+ html = open_asset('call_stack_printer.js.html')
222
+ @output.puts html
223
+ end
224
+
225
+ def print_title_bar
226
+ @output.puts <<-"end_title_bar"
227
+ <div id="titlebar">
228
+ Call tree for application <b>#{h application} #{h arguments}</b><br/>
229
+ Generated on #{Time.now} with options #{h @options.inspect}<br/>
230
+ </div>
231
+ end_title_bar
232
+ end
233
+
234
+ def print_commands
235
+ @output.puts <<-"end_commands"
236
+ <div id=\"commands\">
237
+ <span style=\"font-size: 11pt; font-weight: bold;\">Threshold:</span>
238
+ <input value=\"#{h threshold}\" size=\"3\" id=\"threshold\" type=\"text\">
239
+ <input value=\"Apply\" onclick=\"setThreshold();\" type=\"submit\">
240
+ <input value=\"Expand All\" onclick=\"expandAll(event);\" type=\"submit\">
241
+ <input value=\"Collapse All\" onclick=\"collapseAll(event);\" type=\"submit\">
242
+ <input value=\"Show Help\" onclick=\"toggleHelp(this);\" type=\"submit\">
243
+ </div>
244
+ end_commands
245
+ end
246
+
247
+ def print_help
248
+ @output.puts <<-'end_help'
249
+ <div style="display: none;" id="help">
250
+ &#8226; Enter a decimal value <i>d</i> into the threshold field and click "Apply"
251
+ to hide all nodes marked with time values lower than <i>d</i>.<br>
252
+ &#8226; Click on "Expand All" for full tree expansion.<br>
253
+ &#8226; Click on "Collapse All" to show only top level nodes.<br>
254
+ &#8226; Use a, s, d, w as in Quake or Urban Terror to navigate the tree.<br>
255
+ &#8226; Use f and b to navigate the tree in preorder forward and backwards.<br>
256
+ &#8226; Use x to toggle visibility of a subtree.<br>
257
+ &#8226; Use * to expand/collapse a whole subtree.<br>
258
+ &#8226; Use h to navigate to thread root.<br>
259
+ &#8226; Use n and p to navigate between threads.<br>
260
+ &#8226; Click on background to move focus to a subtree.<br>
261
+ </div>
262
+ end_help
263
+ end
264
+ end
265
+ end
@@ -0,0 +1,143 @@
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 RubyProf.measure_mode
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
+ validate_print_params(options)
48
+ setup_options(options)
49
+ determine_event_specification_and_value_scale
50
+ print_threads
51
+ end
52
+
53
+ def validate_print_params(options)
54
+ if options.is_a?(IO)
55
+ raise ArgumentError, "#{self.class.name}#print cannot print to IO objects"
56
+ elsif !options.is_a?(Hash)
57
+ raise ArgumentError, "#{self.class.name}#print requires an options hash"
58
+ end
59
+ end
60
+
61
+ def print_threads
62
+ remove_subsidiary_files_from_previous_profile_runs
63
+ # TODO: merge fibers of a given thread here, instead of relying
64
+ # on the profiler to merge fibers.
65
+ @result.threads.each do |thread|
66
+ print_thread(thread)
67
+ end
68
+ end
69
+
70
+ def convert(value)
71
+ (value * @value_scale).round
72
+ end
73
+
74
+ def file(method)
75
+ File.expand_path(method.source_file)
76
+ end
77
+
78
+ def print_thread(thread)
79
+ File.open(file_path_for_thread(thread), "w") do |f|
80
+ print_headers(f, thread)
81
+ thread.methods.reverse_each do |method|
82
+ print_method(f, method)
83
+ end
84
+ end
85
+ end
86
+
87
+ def path
88
+ @options[:path] || "."
89
+ end
90
+
91
+ def self.needs_dir?
92
+ true
93
+ end
94
+
95
+ def base_name
96
+ @options[:profile] || "profile"
97
+ end
98
+
99
+ def remove_subsidiary_files_from_previous_profile_runs
100
+ pattern = [base_name, "callgrind.out", $$, "*"].join(".")
101
+ files = Dir.glob(File.join(path, pattern))
102
+ FileUtils.rm_f(files)
103
+ end
104
+
105
+ def file_name_for_thread(thread)
106
+ if thread.fiber_id == Fiber.current.object_id
107
+ [base_name, "callgrind.out", $$].join(".")
108
+ else
109
+ [base_name, "callgrind.out", $$, thread.fiber_id].join(".")
110
+ end
111
+ end
112
+
113
+ def file_path_for_thread(thread)
114
+ File.join(path, file_name_for_thread(thread))
115
+ end
116
+
117
+ def print_headers(output, thread)
118
+ output << "#{@event_specification}\n\n"
119
+ # this doesn't work. kcachegrind does not fully support the spec.
120
+ # output << "thread: #{thread.id}\n\n"
121
+ end
122
+
123
+ def print_method(output, method)
124
+ # Print out the file and method name
125
+ output << "fl=#{file(method)}\n"
126
+ output << "fn=#{method.calltree_name}\n"
127
+
128
+ # Now print out the function line number and its self time
129
+ output << "#{method.line} #{convert(method.self_time)}\n"
130
+
131
+ # Now print out all the children methods
132
+ method.children.each do |callee|
133
+ output << "cfl=#{file(callee.target)}\n"
134
+ output << "cfn=#{callee.target.calltree_name}\n"
135
+ output << "calls=#{callee.called} #{callee.line}\n"
136
+
137
+ # Print out total times here!
138
+ output << "#{callee.line} #{convert(callee.total_time)}\n"
139
+ end
140
+ output << "\n"
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,132 @@
1
+ # encoding: utf-8
2
+
3
+ require 'set'
4
+
5
+ module RubyProf
6
+ # Generates a graphviz graph in dot format.
7
+ # To use the dot printer:
8
+ #
9
+ # result = RubyProf.profile do
10
+ # [code to profile]
11
+ # end
12
+ #
13
+ # printer = RubyProf::DotPrinter.new(result)
14
+ # printer.print(STDOUT)
15
+ #
16
+ # You can use either dot viewer such as GraphViz, or the dot command line tool
17
+ # to reformat the output into a wide variety of outputs:
18
+ #
19
+ # dot -Tpng graph.dot > graph.png
20
+ #
21
+ class DotPrinter < RubyProf::AbstractPrinter
22
+ CLASS_COLOR = '"#666666"'
23
+ EDGE_COLOR = '"#666666"'
24
+
25
+ # Creates the DotPrinter using a RubyProf::Proile.
26
+ def initialize(result)
27
+ super(result)
28
+ @seen_methods = Set.new
29
+ end
30
+
31
+ # Print a graph report to the provided output.
32
+ #
33
+ # output - Any IO object, including STDOUT or a file. The default value is
34
+ # STDOUT.
35
+ #
36
+ # options - Hash of print options. See #setup_options
37
+ # for more information.
38
+ #
39
+ # When profiling results that cover a large number of method calls it
40
+ # helps to use the :min_percent option, for example:
41
+ #
42
+ # DotPrinter.new(result).print(STDOUT, :min_percent=>5)
43
+ #
44
+ def print(output = STDOUT, options = {})
45
+ @output = output
46
+ setup_options(options)
47
+
48
+ puts 'digraph "Profile" {'
49
+ #puts "label=\"#{mode_name} >=#{min_percent}%\\nTotal: #{total_time}\";"
50
+ puts "labelloc=t;"
51
+ puts "labeljust=l;"
52
+ print_threads
53
+ puts '}'
54
+ end
55
+
56
+ private
57
+
58
+ # Something of a hack, figure out which constant went with the
59
+ # RubyProf.measure_mode so that we can display it. Otherwise it's easy to
60
+ # forget what measurement was made.
61
+ def mode_name
62
+ RubyProf.constants.find{|c| RubyProf.const_get(c) == RubyProf.measure_mode}
63
+ end
64
+
65
+ def print_threads
66
+ @result.threads.each do |thread|
67
+ puts "subgraph \"Thread #{thread.id}\" {"
68
+
69
+ print_thread(thread)
70
+ puts "}"
71
+
72
+ print_classes(thread)
73
+ end
74
+ end
75
+
76
+ # Determines an ID to use to represent the subject in the Dot file.
77
+ def dot_id(subject)
78
+ subject.object_id
79
+ end
80
+
81
+ def print_thread(thread)
82
+ total_time = thread.total_time
83
+ thread.methods.sort_by(&sort_method).reverse_each do |method|
84
+ total_percentage = (method.total_time/total_time) * 100
85
+
86
+ next if total_percentage < min_percent
87
+ name = method_name(method).split("#").last
88
+ puts "#{dot_id(method)} [label=\"#{name}\\n(#{total_percentage.round}%)\"];"
89
+ @seen_methods << method
90
+ print_edges(total_time, method)
91
+ end
92
+ end
93
+
94
+ def print_classes(thread)
95
+ grouped = {}
96
+ thread.methods.each{|m| grouped[m.klass_name] ||= []; grouped[m.klass_name] << m}
97
+ grouped.each do |cls, methods2|
98
+ # Filter down to just seen methods
99
+ big_methods = methods2.select{|m| @seen_methods.include? m}
100
+
101
+ if !big_methods.empty?
102
+ puts "subgraph cluster_#{cls.object_id} {"
103
+ puts "label = \"#{cls}\";"
104
+ puts "fontcolor = #{CLASS_COLOR};"
105
+ puts "fontsize = 16;"
106
+ puts "color = #{CLASS_COLOR};"
107
+ big_methods.each do |m|
108
+ puts "#{m.object_id};"
109
+ end
110
+ puts "}"
111
+ end
112
+ end
113
+ end
114
+
115
+ def print_edges(total_time, method)
116
+ method.aggregate_children.sort_by(&:total_time).reverse.each do |child|
117
+
118
+ target_percentage = (child.target.total_time / total_time) * 100.0
119
+ next if target_percentage < min_percent
120
+
121
+ # Get children method
122
+ puts "#{dot_id(method)} -> #{dot_id(child.target)} [label=\"#{child.called}/#{child.target.called}\" fontsize=10 fontcolor=#{EDGE_COLOR}];"
123
+ end
124
+ end
125
+
126
+ # Silly little helper for printing to the @output
127
+ def puts(str)
128
+ @output.puts(str)
129
+ end
130
+
131
+ end
132
+ end