ruby-prof 0.13.1 → 1.4.2

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 (209) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES +579 -371
  3. data/LICENSE +24 -23
  4. data/README.rdoc +5 -433
  5. data/Rakefile +98 -110
  6. data/bin/ruby-prof +328 -329
  7. data/bin/ruby-prof-check-trace +45 -0
  8. data/ext/ruby_prof/extconf.rb +16 -59
  9. data/ext/ruby_prof/rp_aggregate_call_tree.c +59 -0
  10. data/ext/ruby_prof/rp_aggregate_call_tree.h +13 -0
  11. data/ext/ruby_prof/rp_allocation.c +287 -0
  12. data/ext/ruby_prof/rp_allocation.h +31 -0
  13. data/ext/ruby_prof/rp_call_tree.c +369 -0
  14. data/ext/ruby_prof/rp_call_tree.h +43 -0
  15. data/ext/ruby_prof/rp_call_trees.c +288 -0
  16. data/ext/ruby_prof/rp_call_trees.h +28 -0
  17. data/ext/ruby_prof/rp_measure_allocations.c +50 -65
  18. data/ext/ruby_prof/rp_measure_memory.c +42 -73
  19. data/ext/ruby_prof/rp_measure_process_time.c +65 -71
  20. data/ext/ruby_prof/rp_measure_wall_time.c +64 -42
  21. data/ext/ruby_prof/rp_measurement.c +237 -0
  22. data/ext/ruby_prof/rp_measurement.h +50 -0
  23. data/ext/ruby_prof/rp_method.c +491 -420
  24. data/ext/ruby_prof/rp_method.h +62 -57
  25. data/ext/ruby_prof/rp_profile.c +908 -0
  26. data/ext/ruby_prof/rp_profile.h +35 -0
  27. data/ext/ruby_prof/rp_stack.c +212 -128
  28. data/ext/ruby_prof/rp_stack.h +53 -51
  29. data/ext/ruby_prof/rp_thread.c +362 -268
  30. data/ext/ruby_prof/rp_thread.h +39 -27
  31. data/ext/ruby_prof/ruby_prof.c +52 -695
  32. data/ext/ruby_prof/ruby_prof.h +26 -55
  33. data/ext/ruby_prof/vc/ruby_prof.sln +28 -21
  34. data/ext/ruby_prof/vc/{ruby_prof_20.vcxproj → ruby_prof.vcxproj} +56 -8
  35. data/lib/ruby-prof.rb +52 -67
  36. data/lib/ruby-prof/assets/call_stack_printer.html.erb +710 -0
  37. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  38. data/lib/ruby-prof/assets/graph_printer.html.erb +355 -0
  39. data/lib/ruby-prof/call_tree.rb +57 -0
  40. data/lib/ruby-prof/call_tree_visitor.rb +36 -0
  41. data/lib/ruby-prof/compatibility.rb +99 -169
  42. data/lib/ruby-prof/exclude_common_methods.rb +198 -0
  43. data/lib/ruby-prof/measurement.rb +17 -0
  44. data/lib/ruby-prof/method_info.rb +78 -131
  45. data/lib/ruby-prof/printers/abstract_printer.rb +137 -85
  46. data/lib/ruby-prof/printers/call_info_printer.rb +53 -41
  47. data/lib/ruby-prof/printers/call_stack_printer.rb +180 -773
  48. data/lib/ruby-prof/printers/call_tree_printer.rb +151 -92
  49. data/lib/ruby-prof/printers/dot_printer.rb +132 -132
  50. data/lib/ruby-prof/printers/flat_printer.rb +53 -69
  51. data/lib/ruby-prof/printers/graph_html_printer.rb +63 -255
  52. data/lib/ruby-prof/printers/graph_printer.rb +113 -116
  53. data/lib/ruby-prof/printers/multi_printer.rb +127 -56
  54. data/lib/ruby-prof/profile.rb +37 -77
  55. data/lib/ruby-prof/rack.rb +62 -15
  56. data/lib/ruby-prof/task.rb +147 -147
  57. data/lib/ruby-prof/thread.rb +10 -12
  58. data/lib/ruby-prof/version.rb +3 -0
  59. data/lib/unprof.rb +10 -10
  60. data/ruby-prof.gemspec +65 -61
  61. data/test/abstract_printer_test.rb +26 -0
  62. data/test/alias_test.rb +126 -0
  63. data/test/basic_test.rb +43 -128
  64. data/test/call_tree_visitor_test.rb +32 -0
  65. data/test/call_trees_test.rb +66 -0
  66. data/test/duplicate_names_test.rb +32 -32
  67. data/test/dynamic_method_test.rb +53 -74
  68. data/test/enumerable_test.rb +21 -16
  69. data/test/exceptions_test.rb +24 -16
  70. data/test/exclude_methods_test.rb +151 -0
  71. data/test/exclude_threads_test.rb +53 -54
  72. data/test/fiber_test.rb +129 -65
  73. data/test/gc_test.rb +90 -0
  74. data/test/inverse_call_tree_test.rb +175 -0
  75. data/test/line_number_test.rb +158 -71
  76. data/test/marshal_test.rb +113 -0
  77. data/test/measure_allocations.rb +30 -0
  78. data/test/measure_allocations_test.rb +375 -25
  79. data/test/measure_allocations_trace_test.rb +375 -0
  80. data/test/measure_memory_trace_test.rb +1101 -0
  81. data/test/measure_process_time_test.rb +785 -62
  82. data/test/measure_times.rb +56 -0
  83. data/test/measure_wall_time_test.rb +434 -254
  84. data/test/multi_printer_test.rb +71 -82
  85. data/test/no_method_class_test.rb +15 -15
  86. data/test/pause_resume_test.rb +175 -166
  87. data/test/prime.rb +54 -54
  88. data/test/prime_script.rb +6 -0
  89. data/test/printer_call_stack_test.rb +27 -0
  90. data/test/printer_call_tree_test.rb +30 -0
  91. data/test/printer_flat_test.rb +99 -0
  92. data/test/printer_graph_html_test.rb +59 -0
  93. data/test/printer_graph_test.rb +40 -0
  94. data/test/printers_test.rb +141 -257
  95. data/test/printing_recursive_graph_test.rb +81 -0
  96. data/test/profile_test.rb +16 -0
  97. data/test/rack_test.rb +93 -0
  98. data/test/recursive_test.rb +206 -215
  99. data/test/singleton_test.rb +38 -38
  100. data/test/stack_printer_test.rb +64 -78
  101. data/test/start_stop_test.rb +109 -112
  102. data/test/test_helper.rb +13 -115
  103. data/test/thread_test.rb +144 -178
  104. data/test/unique_call_path_test.rb +120 -224
  105. data/test/yarv_test.rb +56 -0
  106. metadata +77 -133
  107. data/doc/LICENSE.html +0 -155
  108. data/doc/README_rdoc.html +0 -648
  109. data/doc/Rack.html +0 -167
  110. data/doc/Rack/RubyProf.html +0 -319
  111. data/doc/RubyProf.html +0 -1000
  112. data/doc/RubyProf/AbstractPrinter.html +0 -580
  113. data/doc/RubyProf/AggregateCallInfo.html +0 -570
  114. data/doc/RubyProf/CallInfo.html +0 -512
  115. data/doc/RubyProf/CallInfoPrinter.html +0 -190
  116. data/doc/RubyProf/CallInfoVisitor.html +0 -332
  117. data/doc/RubyProf/CallStackPrinter.html +0 -1600
  118. data/doc/RubyProf/CallTreePrinter.html +0 -413
  119. data/doc/RubyProf/Cmd.html +0 -669
  120. data/doc/RubyProf/DotPrinter.html +0 -312
  121. data/doc/RubyProf/FlatPrinter.html +0 -229
  122. data/doc/RubyProf/FlatPrinterWithLineNumbers.html +0 -267
  123. data/doc/RubyProf/GraphHtmlPrinter.html +0 -630
  124. data/doc/RubyProf/GraphPrinter.html +0 -209
  125. data/doc/RubyProf/MethodInfo.html +0 -713
  126. data/doc/RubyProf/MultiPrinter.html +0 -407
  127. data/doc/RubyProf/Profile.html +0 -821
  128. data/doc/RubyProf/ProfileTask.html +0 -532
  129. data/doc/RubyProf/Test.html +0 -578
  130. data/doc/RubyProf/Thread.html +0 -262
  131. data/doc/created.rid +0 -32
  132. data/doc/examples/flat_txt.html +0 -191
  133. data/doc/examples/graph_txt.html +0 -305
  134. data/doc/images/add.png +0 -0
  135. data/doc/images/brick.png +0 -0
  136. data/doc/images/brick_link.png +0 -0
  137. data/doc/images/bug.png +0 -0
  138. data/doc/images/bullet_black.png +0 -0
  139. data/doc/images/bullet_toggle_minus.png +0 -0
  140. data/doc/images/bullet_toggle_plus.png +0 -0
  141. data/doc/images/date.png +0 -0
  142. data/doc/images/delete.png +0 -0
  143. data/doc/images/find.png +0 -0
  144. data/doc/images/loadingAnimation.gif +0 -0
  145. data/doc/images/macFFBgHack.png +0 -0
  146. data/doc/images/package.png +0 -0
  147. data/doc/images/page_green.png +0 -0
  148. data/doc/images/page_white_text.png +0 -0
  149. data/doc/images/page_white_width.png +0 -0
  150. data/doc/images/plugin.png +0 -0
  151. data/doc/images/ruby.png +0 -0
  152. data/doc/images/tag_blue.png +0 -0
  153. data/doc/images/tag_green.png +0 -0
  154. data/doc/images/transparent.png +0 -0
  155. data/doc/images/wrench.png +0 -0
  156. data/doc/images/wrench_orange.png +0 -0
  157. data/doc/images/zoom.png +0 -0
  158. data/doc/index.html +0 -647
  159. data/doc/js/darkfish.js +0 -155
  160. data/doc/js/jquery.js +0 -18
  161. data/doc/js/navigation.js +0 -142
  162. data/doc/js/search.js +0 -94
  163. data/doc/js/search_index.js +0 -1
  164. data/doc/js/searcher.js +0 -228
  165. data/doc/rdoc.css +0 -543
  166. data/doc/table_of_contents.html +0 -462
  167. data/examples/empty.png +0 -0
  168. data/examples/flat.txt +0 -55
  169. data/examples/graph.dot +0 -106
  170. data/examples/graph.html +0 -823
  171. data/examples/graph.png +0 -0
  172. data/examples/graph.txt +0 -170
  173. data/examples/minus.png +0 -0
  174. data/examples/multi.flat.txt +0 -23
  175. data/examples/multi.graph.html +0 -906
  176. data/examples/multi.grind.dat +0 -194
  177. data/examples/multi.stack.html +0 -573
  178. data/examples/plus.png +0 -0
  179. data/examples/stack.html +0 -573
  180. data/ext/ruby_prof/rp_call_info.c +0 -407
  181. data/ext/ruby_prof/rp_call_info.h +0 -48
  182. data/ext/ruby_prof/rp_measure.c +0 -48
  183. data/ext/ruby_prof/rp_measure.h +0 -45
  184. data/ext/ruby_prof/rp_measure_cpu_time.c +0 -112
  185. data/ext/ruby_prof/rp_measure_gc_runs.c +0 -65
  186. data/ext/ruby_prof/rp_measure_gc_time.c +0 -57
  187. data/ext/ruby_prof/vc/ruby_prof_18.vcxproj +0 -108
  188. data/ext/ruby_prof/vc/ruby_prof_19.vcxproj +0 -110
  189. data/ext/ruby_prof/version.h +0 -7
  190. data/lib/ruby-prof/aggregate_call_info.rb +0 -72
  191. data/lib/ruby-prof/call_info.rb +0 -89
  192. data/lib/ruby-prof/call_info_visitor.rb +0 -44
  193. data/lib/ruby-prof/images/empty.png +0 -0
  194. data/lib/ruby-prof/images/minus.png +0 -0
  195. data/lib/ruby-prof/images/plus.png +0 -0
  196. data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +0 -57
  197. data/lib/ruby-prof/test.rb +0 -150
  198. data/test/aggregate_test.rb +0 -136
  199. data/test/call_info_test.rb +0 -78
  200. data/test/call_info_visitor_test.rb +0 -31
  201. data/test/exec_test.rb +0 -14
  202. data/test/measure_cpu_time_test.rb +0 -220
  203. data/test/measure_gc_runs_test.rb +0 -32
  204. data/test/measure_gc_time_test.rb +0 -36
  205. data/test/measure_memory_test.rb +0 -31
  206. data/test/method_elimination_test.rb +0 -84
  207. data/test/module_test.rb +0 -45
  208. data/test/stack_test.rb +0 -138
  209. data/test/test_suite.rb +0 -37
@@ -1,92 +1,151 @@
1
- # encoding: utf-8
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
- # Specify print options.
9
- #
10
- # options - Hash table
11
- # :min_percent - Number 0 to 100 that specifes the minimum
12
- # %self (the methods self time divided by the
13
- # overall total time) that a method must take
14
- # for it to be printed out in the report.
15
- # Default value is 0.
16
- #
17
- # :print_file - True or false. Specifies if a method's source
18
- # file should be printed. Default value if false.
19
- #
20
- def print(output = STDOUT, options = {})
21
- @output = output
22
- setup_options(options)
23
-
24
- # add a header - this information is somewhat arbitrary
25
- @output << "events: "
26
- case RubyProf.measure_mode
27
- when RubyProf::PROCESS_TIME
28
- @value_scale = RubyProf::CLOCKS_PER_SEC;
29
- @output << 'process_time'
30
- when RubyProf::WALL_TIME
31
- @value_scale = 1_000_000
32
- @output << 'wall_time'
33
- when RubyProf.const_defined?(:CPU_TIME) && RubyProf::CPU_TIME
34
- @value_scale = RubyProf.cpu_frequency
35
- @output << 'cpu_time'
36
- when RubyProf.const_defined?(:ALLOCATIONS) && RubyProf::ALLOCATIONS
37
- @value_scale = 1
38
- @output << 'allocations'
39
- when RubyProf.const_defined?(:MEMORY) && RubyProf::MEMORY
40
- @value_scale = 1
41
- @output << 'memory'
42
- when RubyProf.const_defined?(:GC_RUNS) && RubyProf::GC_RUNS
43
- @value_scale = 1
44
- @output << 'gc_runs'
45
- when RubyProf.const_defined?(:GC_TIME) && RubyProf::GC_TIME
46
- @value_scale = 1000000
47
- @output << 'gc_time'
48
- else
49
- raise "Unknown measure mode: #{RubyProf.measure_mode}"
50
- end
51
- @output << "\n\n"
52
-
53
- print_threads
54
- end
55
-
56
- def print_threads
57
- @result.threads.each do |thread|
58
- print_thread(thread)
59
- end
60
- end
61
-
62
- def convert(value)
63
- (value * @value_scale).round
64
- end
65
-
66
- def file(method)
67
- File.expand_path(method.source_file)
68
- end
69
-
70
- def print_thread(thread)
71
- thread.methods.reverse_each do |method|
72
- # Print out the file and method name
73
- @output << "fl=#{file(method)}\n"
74
- @output << "fn=#{method_name(method)}\n"
75
-
76
- # Now print out the function line number and its self time
77
- @output << "#{method.line} #{convert(method.self_time)}\n"
78
-
79
- # Now print out all the children methods
80
- method.children.each do |callee|
81
- @output << "cfl=#{file(callee.target)}\n"
82
- @output << "cfn=#{method_name(callee.target)}\n"
83
- @output << "calls=#{callee.called} #{callee.line}\n"
84
-
85
- # Print out total times here!
86
- @output << "#{callee.line} #{convert(callee.total_time)}\n"
87
- end
88
- @output << "\n"
89
- end
90
- end #end print_methods
91
- end # end class
92
- end # end packages
1
+ # encoding: utf-8
2
+
3
+ require 'fiber'
4
+ require 'thread'
5
+ require 'fileutils'
6
+
7
+ module RubyProf
8
+ # Generates profiling information in callgrind format for use by
9
+ # kcachegrind and similar tools.
10
+
11
+ class CallTreePrinter < AbstractPrinter
12
+ def calltree_name(method_info)
13
+ klass_path = method_info.klass_name.gsub("::", '/')
14
+ result = "#{klass_path}::#{method_info.method_name}"
15
+
16
+ case method_info.klass_flags
17
+ when 0x2
18
+ "#{result}^"
19
+ when 0x4
20
+ "#{result}^"
21
+ when 0x8
22
+ "#{result}*"
23
+ else
24
+ result
25
+ end
26
+ end
27
+
28
+ def determine_event_specification_and_value_scale
29
+ @event_specification = "events: "
30
+ case RubyProf.measure_mode
31
+ when RubyProf::PROCESS_TIME
32
+ @value_scale = RubyProf::CLOCKS_PER_SEC
33
+ @event_specification << 'process_time'
34
+ when RubyProf::WALL_TIME
35
+ @value_scale = 1_000_000
36
+ @event_specification << 'wall_time'
37
+ when RubyProf.const_defined?(:ALLOCATIONS) && RubyProf::ALLOCATIONS
38
+ @value_scale = 1
39
+ @event_specification << 'allocations'
40
+ when RubyProf.const_defined?(:MEMORY) && RubyProf::MEMORY
41
+ @value_scale = 1
42
+ @event_specification << 'memory'
43
+ when RubyProf.const_defined?(:GC_RUNS) && RubyProf::GC_RUNS
44
+ @value_scale = 1
45
+ @event_specification << 'gc_runs'
46
+ when RubyProf.const_defined?(:GC_TIME) && RubyProf::GC_TIME
47
+ @value_scale = 1000000
48
+ @event_specification << 'gc_time'
49
+ else
50
+ raise "Unknown measure mode: #{RubyProf.measure_mode}"
51
+ end
52
+ end
53
+
54
+ def print(options = {})
55
+ validate_print_params(options)
56
+ setup_options(options)
57
+ determine_event_specification_and_value_scale
58
+ print_threads
59
+ end
60
+
61
+ def validate_print_params(options)
62
+ if options.is_a?(IO)
63
+ raise ArgumentError, "#{self.class.name}#print cannot print to IO objects"
64
+ elsif !options.is_a?(Hash)
65
+ raise ArgumentError, "#{self.class.name}#print requires an options hash"
66
+ end
67
+ end
68
+
69
+ def print_threads
70
+ remove_subsidiary_files_from_previous_profile_runs
71
+ # TODO: merge fibers of a given thread here, instead of relying
72
+ # on the profiler to merge fibers.
73
+ @result.threads.each do |thread|
74
+ print_thread(thread)
75
+ end
76
+ end
77
+
78
+ def convert(value)
79
+ (value * @value_scale).round
80
+ end
81
+
82
+ def file(method)
83
+ method.source_file ? File.expand_path(method.source_file) : ''
84
+ end
85
+
86
+ def print_thread(thread)
87
+ File.open(file_path_for_thread(thread), "w") do |f|
88
+ print_headers(f, thread)
89
+ thread.methods.reverse_each do |method|
90
+ print_method(f, method)
91
+ end
92
+ end
93
+ end
94
+
95
+ def path
96
+ @options[:path] || "."
97
+ end
98
+
99
+ def self.needs_dir?
100
+ true
101
+ end
102
+
103
+ def base_name
104
+ @options[:profile] || "profile"
105
+ end
106
+
107
+ def remove_subsidiary_files_from_previous_profile_runs
108
+ pattern = [base_name, "callgrind.out", $$, "*"].join(".")
109
+ files = Dir.glob(File.join(path, pattern))
110
+ FileUtils.rm_f(files)
111
+ end
112
+
113
+ def file_name_for_thread(thread)
114
+ if thread.fiber_id == Fiber.current.object_id
115
+ [base_name, "callgrind.out", $$].join(".")
116
+ else
117
+ [base_name, "callgrind.out", $$, thread.fiber_id].join(".")
118
+ end
119
+ end
120
+
121
+ def file_path_for_thread(thread)
122
+ File.join(path, file_name_for_thread(thread))
123
+ end
124
+
125
+ def print_headers(output, thread)
126
+ output << "#{@event_specification}\n\n"
127
+ # this doesn't work. kcachegrind does not fully support the spec.
128
+ # output << "thread: #{thread.id}\n\n"
129
+ end
130
+
131
+ def print_method(output, method)
132
+ # Print out the file and method name
133
+ output << "fl=#{file(method)}\n"
134
+ output << "fn=#{self.calltree_name(method)}\n"
135
+
136
+ # Now print out the function line number and its self time
137
+ output << "#{method.line} #{convert(method.self_time)}\n"
138
+
139
+ # Now print out all the children methods
140
+ method.call_trees.callees.each do |callee|
141
+ output << "cfl=#{file(callee.target)}\n"
142
+ output << "cfn=#{self.calltree_name(callee.target)}\n"
143
+ output << "calls=#{callee.called} #{callee.line}\n"
144
+
145
+ # Print out total times here!
146
+ output << "#{callee.line} #{convert(callee.total_time)}\n"
147
+ end
148
+ output << "\n"
149
+ end
150
+ end
151
+ end
@@ -1,132 +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::Result.
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
1
+ # encoding: utf-8
2
+
3
+ require 'set'
4
+
5
+ module RubyProf
6
+ # Generates a graphviz graph in dot format.
7
+ #
8
+ # To use the dot printer:
9
+ #
10
+ # result = RubyProf.profile do
11
+ # [code to profile]
12
+ # end
13
+ #
14
+ # printer = RubyProf::DotPrinter.new(result)
15
+ # printer.print(STDOUT)
16
+ #
17
+ # You can use either dot viewer such as GraphViz, or the dot command line tool
18
+ # to reformat the output into a wide variety of outputs:
19
+ #
20
+ # dot -Tpng graph.dot > graph.png
21
+ #
22
+ class DotPrinter < RubyProf::AbstractPrinter
23
+ CLASS_COLOR = '"#666666"'
24
+ EDGE_COLOR = '"#666666"'
25
+
26
+ # Creates the DotPrinter using a RubyProf::Proile.
27
+ def initialize(result)
28
+ super(result)
29
+ @seen_methods = Set.new
30
+ end
31
+
32
+ # Print a graph report to the provided output.
33
+ #
34
+ # output - Any IO object, including STDOUT or a file. The default value is
35
+ # STDOUT.
36
+ #
37
+ # options - Hash of print options. See #setup_options
38
+ # for more information.
39
+ #
40
+ # When profiling results that cover a large number of method calls it
41
+ # helps to use the :min_percent option, for example:
42
+ #
43
+ # DotPrinter.new(result).print(STDOUT, :min_percent=>5)
44
+ #
45
+ def print(output = STDOUT, options = {})
46
+ @output = output
47
+ setup_options(options)
48
+
49
+ puts 'digraph "Profile" {'
50
+ #puts "label=\"#{mode_name} >=#{min_percent}%\\nTotal: #{total_time}\";"
51
+ puts "labelloc=t;"
52
+ puts "labeljust=l;"
53
+ print_threads
54
+ puts '}'
55
+ end
56
+
57
+ private
58
+
59
+ # Something of a hack, figure out which constant went with the
60
+ # RubyProf.measure_mode so that we can display it. Otherwise it's easy to
61
+ # forget what measurement was made.
62
+ def mode_name
63
+ RubyProf.constants.find{|c| RubyProf.const_get(c) == RubyProf.measure_mode}
64
+ end
65
+
66
+ def print_threads
67
+ @result.threads.each do |thread|
68
+ puts "subgraph \"Thread #{thread.id}\" {"
69
+
70
+ print_thread(thread)
71
+ puts "}"
72
+
73
+ print_classes(thread)
74
+ end
75
+ end
76
+
77
+ # Determines an ID to use to represent the subject in the Dot file.
78
+ def dot_id(subject)
79
+ subject.object_id
80
+ end
81
+
82
+ def print_thread(thread)
83
+ total_time = thread.total_time
84
+ thread.methods.sort_by(&sort_method).reverse_each do |method|
85
+ total_percentage = (method.total_time/total_time) * 100
86
+
87
+ next if total_percentage < min_percent
88
+ name = method.full_name.split("#").last
89
+ puts "#{dot_id(method)} [label=\"#{name}\\n(#{total_percentage.round}%)\"];"
90
+ @seen_methods << method
91
+ print_edges(total_time, method)
92
+ end
93
+ end
94
+
95
+ def print_classes(thread)
96
+ grouped = {}
97
+ thread.methods.each{|m| grouped[m.klass_name] ||= []; grouped[m.klass_name] << m}
98
+ grouped.each do |cls, methods2|
99
+ # Filter down to just seen methods
100
+ big_methods = methods2.select{|m| @seen_methods.include? m}
101
+
102
+ if !big_methods.empty?
103
+ puts "subgraph cluster_#{cls.object_id} {"
104
+ puts "label = \"#{cls}\";"
105
+ puts "fontcolor = #{CLASS_COLOR};"
106
+ puts "fontsize = 16;"
107
+ puts "color = #{CLASS_COLOR};"
108
+ big_methods.each do |m|
109
+ puts "#{m.object_id};"
110
+ end
111
+ puts "}"
112
+ end
113
+ end
114
+ end
115
+
116
+ def print_edges(total_time, method)
117
+ method.call_trees.callers.sort_by(&:total_time).reverse.each do |call_tree|
118
+ target_percentage = (call_tree.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(call_tree.target)} [label=\"#{call_tree.called}/#{call_tree.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