ruby-prof 0.17.0 → 0.18.0

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 (185) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES +500 -482
  3. data/LICENSE +24 -24
  4. data/README.rdoc +487 -485
  5. data/Rakefile +113 -113
  6. data/bin/ruby-prof +345 -345
  7. data/bin/ruby-prof-check-trace +45 -45
  8. data/examples/flat.txt +50 -50
  9. data/examples/graph.dot +84 -84
  10. data/examples/graph.html +823 -823
  11. data/examples/graph.txt +139 -139
  12. data/examples/multi.flat.txt +23 -23
  13. data/examples/multi.graph.html +760 -760
  14. data/examples/multi.grind.dat +114 -114
  15. data/examples/multi.stack.html +547 -547
  16. data/examples/stack.html +547 -547
  17. data/ext/ruby_prof/extconf.rb +68 -68
  18. data/ext/ruby_prof/rp_call_info.c +425 -425
  19. data/ext/ruby_prof/rp_call_info.h +53 -53
  20. data/ext/ruby_prof/rp_measure.c +40 -40
  21. data/ext/ruby_prof/rp_measure.h +45 -45
  22. data/ext/ruby_prof/rp_measure_allocations.c +76 -76
  23. data/ext/ruby_prof/rp_measure_cpu_time.c +136 -136
  24. data/ext/ruby_prof/rp_measure_gc_runs.c +73 -73
  25. data/ext/ruby_prof/rp_measure_gc_time.c +60 -60
  26. data/ext/ruby_prof/rp_measure_memory.c +77 -77
  27. data/ext/ruby_prof/rp_measure_process_time.c +71 -71
  28. data/ext/ruby_prof/rp_measure_wall_time.c +45 -45
  29. data/ext/ruby_prof/rp_method.c +630 -636
  30. data/ext/ruby_prof/rp_method.h +75 -75
  31. data/ext/ruby_prof/rp_stack.c +173 -173
  32. data/ext/ruby_prof/rp_stack.h +63 -63
  33. data/ext/ruby_prof/rp_thread.c +277 -276
  34. data/ext/ruby_prof/rp_thread.h +27 -27
  35. data/ext/ruby_prof/ruby_prof.c +794 -774
  36. data/ext/ruby_prof/ruby_prof.h +60 -59
  37. data/ext/ruby_prof/vc/ruby_prof.sln +20 -21
  38. data/ext/ruby_prof/vc/{ruby_prof_20.vcxproj → ruby_prof.vcxproj} +31 -0
  39. data/lib/ruby-prof.rb +68 -68
  40. data/lib/ruby-prof/aggregate_call_info.rb +76 -76
  41. data/lib/ruby-prof/assets/call_stack_printer.css.html +116 -116
  42. data/lib/ruby-prof/assets/call_stack_printer.js.html +384 -384
  43. data/lib/ruby-prof/call_info.rb +115 -115
  44. data/lib/ruby-prof/call_info_visitor.rb +40 -40
  45. data/lib/ruby-prof/compatibility.rb +179 -178
  46. data/lib/ruby-prof/method_info.rb +121 -121
  47. data/lib/ruby-prof/printers/abstract_printer.rb +104 -103
  48. data/lib/ruby-prof/printers/call_info_printer.rb +41 -41
  49. data/lib/ruby-prof/printers/call_stack_printer.rb +265 -265
  50. data/lib/ruby-prof/printers/call_tree_printer.rb +143 -143
  51. data/lib/ruby-prof/printers/dot_printer.rb +132 -132
  52. data/lib/ruby-prof/printers/flat_printer.rb +70 -70
  53. data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +83 -83
  54. data/lib/ruby-prof/printers/graph_html_printer.rb +249 -249
  55. data/lib/ruby-prof/printers/graph_printer.rb +116 -116
  56. data/lib/ruby-prof/printers/multi_printer.rb +84 -84
  57. data/lib/ruby-prof/profile.rb +26 -26
  58. data/lib/ruby-prof/profile/exclude_common_methods.rb +207 -201
  59. data/lib/ruby-prof/profile/legacy_method_elimination.rb +50 -49
  60. data/lib/ruby-prof/rack.rb +174 -174
  61. data/lib/ruby-prof/task.rb +147 -147
  62. data/lib/ruby-prof/thread.rb +35 -35
  63. data/lib/ruby-prof/version.rb +3 -3
  64. data/lib/unprof.rb +10 -10
  65. data/ruby-prof.gemspec +58 -58
  66. data/test/abstract_printer_test.rb +53 -0
  67. data/test/aggregate_test.rb +136 -136
  68. data/test/basic_test.rb +128 -128
  69. data/test/block_test.rb +74 -74
  70. data/test/call_info_test.rb +78 -78
  71. data/test/call_info_visitor_test.rb +31 -31
  72. data/test/duplicate_names_test.rb +32 -32
  73. data/test/dynamic_method_test.rb +55 -55
  74. data/test/enumerable_test.rb +21 -21
  75. data/test/exceptions_test.rb +24 -16
  76. data/test/exclude_methods_test.rb +146 -146
  77. data/test/exclude_threads_test.rb +53 -53
  78. data/test/fiber_test.rb +79 -79
  79. data/test/issue137_test.rb +63 -63
  80. data/test/line_number_test.rb +80 -80
  81. data/test/measure_allocations_test.rb +26 -26
  82. data/test/measure_cpu_time_test.rb +212 -213
  83. data/test/measure_gc_runs_test.rb +32 -32
  84. data/test/measure_gc_time_test.rb +36 -36
  85. data/test/measure_memory_test.rb +33 -33
  86. data/test/measure_process_time_test.rb +61 -63
  87. data/test/measure_wall_time_test.rb +255 -255
  88. data/test/method_elimination_test.rb +84 -84
  89. data/test/module_test.rb +45 -45
  90. data/test/multi_printer_test.rb +104 -104
  91. data/test/no_method_class_test.rb +15 -15
  92. data/test/pause_resume_test.rb +166 -166
  93. data/test/prime.rb +54 -54
  94. data/test/printers_test.rb +275 -275
  95. data/test/printing_recursive_graph_test.rb +127 -127
  96. data/test/rack_test.rb +157 -157
  97. data/test/recursive_test.rb +215 -215
  98. data/test/singleton_test.rb +38 -38
  99. data/test/stack_printer_test.rb +77 -78
  100. data/test/stack_test.rb +138 -138
  101. data/test/start_stop_test.rb +112 -112
  102. data/test/test_helper.rb +267 -275
  103. data/test/thread_test.rb +187 -187
  104. data/test/unique_call_path_test.rb +202 -202
  105. data/test/yarv_test.rb +55 -55
  106. metadata +17 -96
  107. data/doc/LICENSE.html +0 -115
  108. data/doc/README_rdoc.html +0 -637
  109. data/doc/Rack.html +0 -96
  110. data/doc/Rack/RubyProf.html +0 -233
  111. data/doc/Rack/RubyProf/RackProfiler.html +0 -343
  112. data/doc/RubyProf.html +0 -974
  113. data/doc/RubyProf/AbstractPrinter.html +0 -625
  114. data/doc/RubyProf/AggregateCallInfo.html +0 -552
  115. data/doc/RubyProf/CallInfo.html +0 -579
  116. data/doc/RubyProf/CallInfoPrinter.html +0 -121
  117. data/doc/RubyProf/CallInfoVisitor.html +0 -199
  118. data/doc/RubyProf/CallStackPrinter.html +0 -1127
  119. data/doc/RubyProf/CallTreePrinter.html +0 -725
  120. data/doc/RubyProf/Cmd.html +0 -637
  121. data/doc/RubyProf/DeprecationWarnings.html +0 -148
  122. data/doc/RubyProf/DotPrinter.html +0 -258
  123. data/doc/RubyProf/FlatPrinter.html +0 -164
  124. data/doc/RubyProf/FlatPrinterWithLineNumbers.html +0 -210
  125. data/doc/RubyProf/GraphHtmlPrinter.html +0 -558
  126. data/doc/RubyProf/GraphPrinter.html +0 -140
  127. data/doc/RubyProf/MethodInfo.html +0 -676
  128. data/doc/RubyProf/MultiPrinter.html +0 -574
  129. data/doc/RubyProf/Profile.html +0 -908
  130. data/doc/RubyProf/Profile/ExcludeCommonMethods.html +0 -411
  131. data/doc/RubyProf/Profile/LegacyMethodElimination.html +0 -158
  132. data/doc/RubyProf/ProfileTask.html +0 -491
  133. data/doc/RubyProf/Thread.html +0 -275
  134. data/doc/created.rid +0 -33
  135. data/doc/css/fonts.css +0 -167
  136. data/doc/css/rdoc.css +0 -590
  137. data/doc/examples/flat_txt.html +0 -139
  138. data/doc/examples/graph_html.html +0 -910
  139. data/doc/examples/graph_txt.html +0 -248
  140. data/doc/fonts/Lato-Light.ttf +0 -0
  141. data/doc/fonts/Lato-LightItalic.ttf +0 -0
  142. data/doc/fonts/Lato-Regular.ttf +0 -0
  143. data/doc/fonts/Lato-RegularItalic.ttf +0 -0
  144. data/doc/fonts/SourceCodePro-Bold.ttf +0 -0
  145. data/doc/fonts/SourceCodePro-Regular.ttf +0 -0
  146. data/doc/images/add.png +0 -0
  147. data/doc/images/arrow_up.png +0 -0
  148. data/doc/images/brick.png +0 -0
  149. data/doc/images/brick_link.png +0 -0
  150. data/doc/images/bug.png +0 -0
  151. data/doc/images/bullet_black.png +0 -0
  152. data/doc/images/bullet_toggle_minus.png +0 -0
  153. data/doc/images/bullet_toggle_plus.png +0 -0
  154. data/doc/images/date.png +0 -0
  155. data/doc/images/delete.png +0 -0
  156. data/doc/images/find.png +0 -0
  157. data/doc/images/loadingAnimation.gif +0 -0
  158. data/doc/images/macFFBgHack.png +0 -0
  159. data/doc/images/package.png +0 -0
  160. data/doc/images/page_green.png +0 -0
  161. data/doc/images/page_white_text.png +0 -0
  162. data/doc/images/page_white_width.png +0 -0
  163. data/doc/images/plugin.png +0 -0
  164. data/doc/images/ruby.png +0 -0
  165. data/doc/images/tag_blue.png +0 -0
  166. data/doc/images/tag_green.png +0 -0
  167. data/doc/images/transparent.png +0 -0
  168. data/doc/images/wrench.png +0 -0
  169. data/doc/images/wrench_orange.png +0 -0
  170. data/doc/images/zoom.png +0 -0
  171. data/doc/index.html +0 -666
  172. data/doc/js/darkfish.js +0 -161
  173. data/doc/js/jquery.js +0 -4
  174. data/doc/js/navigation.js +0 -142
  175. data/doc/js/navigation.js.gz +0 -0
  176. data/doc/js/search.js +0 -109
  177. data/doc/js/search_index.js +0 -1
  178. data/doc/js/search_index.js.gz +0 -0
  179. data/doc/js/searcher.js +0 -229
  180. data/doc/js/searcher.js.gz +0 -0
  181. data/doc/table_of_contents.html +0 -1052
  182. data/examples/cachegrind.out.1 +0 -114
  183. data/examples/cachegrind.out.1.32313213 +0 -114
  184. data/ext/ruby_prof/vc/ruby_prof_18.vcxproj +0 -108
  185. data/ext/ruby_prof/vc/ruby_prof_19.vcxproj +0 -110
@@ -1,143 +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
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
@@ -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::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
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