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
data/Rakefile CHANGED
@@ -1,110 +1,98 @@
1
- # encoding: utf-8
2
-
3
- require "rubygems/package_task"
4
- require "rake/extensiontask"
5
- require "rake/testtask"
6
- require "rdoc/task"
7
- require "date"
8
- require "rake/clean"
9
- begin
10
- require "bundler/setup"
11
- Bundler::GemHelper.install_tasks
12
- [:build, :install, :release].each {|t| Rake::Task[t].enhance [:rdoc] }
13
- rescue LoadError
14
- $stderr.puts "Install bundler to get support for simplified gem publishing"
15
- end
16
-
17
- # To release a version of ruby-prof:
18
- # * Update version.h
19
- # * Update CHANGES
20
- # * git commit to commit files
21
- # * rake clobber to remove extra files
22
- # * rake compile to build windows gems
23
- # * rake package to create the gems
24
- # * Tag the release (git tag 0.10.1)
25
- # * Push to ruybgems.org (gem push pkg/<gem files>)
26
- # For a ruby only release, just run
27
- # * rake release
28
- # it will push changes to github, tag the release, build the package and upload it to rubygems.org
29
- # and in case you forgot to increment the version number or have uncommitted changes, it will refuse to work
30
-
31
- GEM_NAME = 'ruby-prof'
32
- SO_NAME = 'ruby_prof'
33
-
34
- default_spec = Gem::Specification.load("#{GEM_NAME}.gemspec")
35
-
36
- # specify which versions/builds to cross compile
37
- Rake::ExtensionTask.new do |ext|
38
- ext.gem_spec = default_spec
39
- ext.name = SO_NAME
40
- ext.ext_dir = "ext/#{SO_NAME}"
41
- ext.lib_dir = "lib/#{RUBY_VERSION.sub(/\.\d$/, '')}"
42
- ext.cross_compile = true
43
- ext.cross_platform = ['x86-mswin32-60', 'x86-mingw32-60']
44
- end
45
-
46
- # Rake task to build the default package
47
- Gem::PackageTask.new(default_spec) do |pkg|
48
- pkg.need_tar = true
49
- end
50
-
51
- # make sure rdoc has been built when packaging
52
- # why do we ship rdoc as part of the gem?
53
- Rake::Task[:package].enhance [:rdoc]
54
-
55
- # Setup Windows Gem
56
- if RUBY_PLATFORM.match(/win32|mingw32/)
57
- # Windows specification
58
- win_spec = default_spec.clone
59
- win_spec.platform = Gem::Platform::CURRENT
60
- win_spec.files += Dir.glob('lib/**/*.so')
61
- win_spec.instance_variable_set(:@cache_file, nil) # Hack to work around gem issue
62
-
63
- # Unset extensions
64
- win_spec.extensions = nil
65
-
66
- # Rake task to build the windows package
67
- Gem::PackageTask.new(win_spec) do |pkg|
68
- pkg.need_tar = false
69
- end
70
- end
71
-
72
- # --------- RDoc Documentation ------
73
- desc "Generate rdoc documentation"
74
- RDoc::Task.new("rdoc") do |rdoc|
75
- rdoc.rdoc_dir = 'doc'
76
- rdoc.title = "ruby-prof"
77
- # Show source inline with line numbers
78
- rdoc.options << "--inline-source" << "--line-numbers"
79
- # Make the readme file the start page for the generated html
80
- rdoc.options << '--main' << 'README.rdoc'
81
- rdoc.rdoc_files.include('bin/**/*',
82
- 'doc/*.rdoc',
83
- 'examples/flat.txt',
84
- 'examples/graph.txt',
85
- 'examples/graph.html',
86
- 'lib/**/*.rb',
87
- 'ext/ruby_prof/ruby_prof.c',
88
- 'ext/ruby_prof/version.h',
89
- 'ext/ruby_prof/measure_*.h',
90
- 'README.rdoc',
91
- 'LICENSE')
92
- end
93
-
94
- task :default => :test
95
-
96
- for file in Dir['**/*.{o,so,bundle}']
97
- CLEAN.include file
98
- end
99
-
100
- for file in Dir['tmp/*.{txt,dat,png,html}']
101
- CLEAN.include file
102
- end
103
-
104
- desc 'Run the ruby-prof test suite'
105
- Rake::TestTask.new do |t|
106
- t.libs += %w(lib ext test)
107
- t.test_files = Dir['test/test_suite.rb']
108
- t.verbose = true
109
- t.warning = true
110
- end
1
+ # encoding: utf-8
2
+
3
+ require "rubygems/package_task"
4
+ require "rake/extensiontask"
5
+ require "rake/testtask"
6
+ require "rdoc/task"
7
+ require "date"
8
+ require "rake/clean"
9
+
10
+ # To release a version of ruby-prof:
11
+ # * Update lib/ruby-prof/version.rb
12
+ # * Update CHANGES
13
+ # * git commit to commit files
14
+ # * rake clobber to remove extra files
15
+ # * rake compile to build windows gems
16
+ # * rake package to create the gems
17
+ # * Tag the release (git tag 0.10.1)
18
+ # * Push to ruybgems.org (gem push pkg/<gem files>)
19
+
20
+ GEM_NAME = 'ruby-prof'
21
+ SO_NAME = 'ruby_prof'
22
+
23
+ default_spec = Gem::Specification.load("#{GEM_NAME}.gemspec")
24
+
25
+ # specify which versions/builds to cross compile
26
+ Rake::ExtensionTask.new do |ext|
27
+ ext.gem_spec = default_spec
28
+ ext.name = SO_NAME
29
+ ext.ext_dir = "ext/#{SO_NAME}"
30
+ ext.lib_dir = "lib/#{Gem::Version.new(RUBY_VERSION).segments[0..1].join('.')}"
31
+ ext.cross_compile = true
32
+ ext.cross_platform = ['x64-mingw32']
33
+ end
34
+
35
+ # Rake task to build the default package
36
+ Gem::PackageTask.new(default_spec) do |pkg|
37
+ pkg.need_tar = true
38
+ end
39
+
40
+ # make sure rdoc has been built when packaging
41
+ # why do we ship rdoc as part of the gem?
42
+ Rake::Task[:package].enhance [:rdoc]
43
+
44
+ # Setup Windows Gem
45
+ if RUBY_PLATFORM.match(/win32|mingw32/)
46
+ # Windows specification
47
+ win_spec = default_spec.clone
48
+ win_spec.platform = Gem::Platform::CURRENT
49
+ win_spec.files += Dir.glob('lib/**/*.so')
50
+
51
+ # Unset extensions
52
+ win_spec.extensions = nil
53
+
54
+ # Rake task to build the windows package
55
+ Gem::PackageTask.new(win_spec) do |pkg|
56
+ pkg.need_tar = false
57
+ end
58
+ end
59
+
60
+ # --------- RDoc Documentation ------
61
+ desc "Generate rdoc documentation"
62
+ RDoc::Task.new("rdoc") do |rdoc|
63
+ rdoc.rdoc_dir = 'doc'
64
+ rdoc.title = "ruby-prof"
65
+ # Show source inline with line numbers
66
+ rdoc.options << "--line-numbers"
67
+ # Make the readme file the start page for the generated html
68
+ rdoc.options << '--main' << 'README.rdoc'
69
+ rdoc.rdoc_files.include('bin/*',
70
+ 'doc/*.rdoc',
71
+ 'lib/**/*.rb',
72
+ 'ext/ruby_prof/*.c',
73
+ 'ext/ruby_prof/*.h',
74
+ 'README.rdoc',
75
+ 'LICENSE')
76
+ end
77
+
78
+ task :default => :test
79
+
80
+ for file in Dir['lib/**/*.{o,so,bundle}']
81
+ CLEAN.include file
82
+ end
83
+ for file in Dir['doc/**/*.{txt,dat,png,html}']
84
+ CLEAN.include file
85
+ end
86
+ CLEAN.reject!{|f| !File.exist?(f)}
87
+ task :clean do
88
+ # remove tmp dir contents completely after cleaning
89
+ FileUtils.rm_rf('tmp/*')
90
+ end
91
+
92
+ desc 'Run the ruby-prof test suite'
93
+ Rake::TestTask.new do |t|
94
+ t.libs += %w(lib ext test)
95
+ t.test_files = Dir['test/**_test.rb']
96
+ t.verbose = true
97
+ t.warning = true
98
+ end
@@ -1,329 +1,328 @@
1
- #! /usr/bin/env ruby
2
-
3
- # == Synopsis
4
- #
5
- # Profiles a Ruby program.
6
- #
7
- # == Usage
8
- #
9
- # ruby_prof [options] <script.rb> [--] [script-options]"
10
- #
11
- # Various options:
12
- # run "$ ruby-prof --help" to see them
13
- #
14
- # See also the readme "reports" section for the various outputs
15
-
16
- # First require ruby-prof
17
- require 'rubygems'
18
- require 'ruby-prof'
19
-
20
- # Now setup option parser
21
- require 'ostruct'
22
- require 'optparse'
23
-
24
- module RubyProf
25
- class Cmd
26
- attr_accessor :options
27
-
28
- def initialize
29
- setup_options
30
- parse_args
31
-
32
- load_pre_libs
33
- load_pre_execs
34
- end
35
-
36
- def setup_options
37
- @options = OpenStruct.new
38
- options.measure_mode = RubyProf::PROCESS_TIME
39
- options.printer = RubyProf::FlatPrinter
40
- options.min_percent = 0
41
- options.file = nil
42
- options.replace_prog_name = false
43
- options.specialized_instruction = false
44
-
45
- options.pre_libs = Array.new
46
- options.pre_execs = Array.new
47
- end
48
-
49
- def option_parser
50
- OptionParser.new do |opts|
51
- opts.banner = "ruby_prof #{RubyProf::VERSION}\n" +
52
- "Usage: ruby-prof [options] <script.rb> [--] [profiled-script-command-line-options]"
53
-
54
- opts.separator ""
55
- opts.separator "Options:"
56
-
57
- opts.on('-p printer', '--printer=printer', [:flat, :flat_with_line_numbers, :graph, :graph_html, :call_tree, :call_stack, :dot],
58
- 'Select a printer:',
59
- ' flat - Prints a flat profile as text (default).',
60
- ' flat_with_line_numbers - same as flat, with line numbers.',
61
- ' graph - Prints a graph profile as text.',
62
- ' graph_html - Prints a graph profile as html.',
63
- ' call_tree - format for KCacheGrind',
64
- ' call_stack - prints a HTML visualization of the call tree',
65
- ' dot - Prints a graph profile as a dot file'
66
- ) do |printer|
67
-
68
-
69
- case printer
70
- when :flat
71
- options.printer = RubyProf::FlatPrinter
72
- when :flat_with_line_numbers
73
- options.printer = RubyProf::FlatPrinterWithLineNumbers
74
- when :graph
75
- options.printer = RubyProf::GraphPrinter
76
- when :graph_html
77
- options.printer = RubyProf::GraphHtmlPrinter
78
- when :call_tree
79
- options.printer = RubyProf::CallTreePrinter
80
- when :call_stack
81
- options.printer = RubyProf::CallStackPrinter
82
- when :dot
83
- options.printer = RubyProf::DotPrinter
84
- end
85
- end
86
-
87
- opts.on('-m min_percent', '--min_percent=min_percent', Float,
88
- 'The minimum percent a method must take before ',
89
- ' being included in output reports.',
90
- ' this option is not supported for call tree.') do |min_percent|
91
- options.min_percent = min_percent
92
- end
93
-
94
- opts.on('-f path', '--file=path',
95
- 'Output results to a file instead of standard out.') do |file|
96
- options.file = file
97
- options.old_wd = Dir.pwd
98
- end
99
-
100
- opts.on('--mode=measure_mode',
101
- [:process, :wall, :cpu, :allocations, :memory, :gc_runs, :gc_time],
102
- 'Select what ruby-prof should measure:',
103
- ' process - Process time (default).',
104
- ' wall - Wall time.',
105
- ' cpu - CPU time (Pentium and PowerPCs only).',
106
- ' allocations - Object allocations (requires patched Ruby interpreter).',
107
- ' memory - Allocated memory in KB (requires patched Ruby interpreter).',
108
- ' gc_runs - Number of garbage collections (requires patched Ruby interpreter).',
109
- ' gc_time - Time spent in garbage collection (requires patched Ruby interpreter).') do |measure_mode|
110
-
111
- case measure_mode
112
- when :process
113
- options.measure_mode = RubyProf::PROCESS_TIME
114
- when :wall
115
- options.measure_mode = RubyProf::WALL_TIME
116
- when :cpu
117
- options.measure_mode = RubyProf::CPU_TIME
118
- when :allocations
119
- options.measure_mode = RubyProf::ALLOCATIONS
120
- when :memory
121
- options.measure_mode = RubyProf::MEMORY
122
- when :gc_runs
123
- options.measure_mode = RubyProf::GC_RUNS
124
- when :gc_time
125
- options.measure_mode = RubyProf::GC_TIME
126
- end
127
- end
128
-
129
- opts.on('-s sort_mode', '--sort=sort_mode', [:total, :self, :wait, :child],
130
- 'Select how ruby-prof results should be sorted:',
131
- ' total - Total time',
132
- ' self - Self time',
133
- ' wait - Wait time',
134
- ' child - Child time') do |sort_mode|
135
-
136
- options.sort_method = case sort_mode
137
- when :total
138
- :total_time
139
- when :self
140
- :self_time
141
- when :wait
142
- :wait_time
143
- when :child
144
- :children_time
145
- end
146
- end
147
-
148
- opts.on("--replace-progname", "Replace $0 when loading the .rb files.") do
149
- options.replace_prog_name = true
150
- end
151
-
152
- if defined?(VM)
153
- opts.on("--specialized-instruction", "Turn on specified instruction.") do
154
- options.specialized_instruction = true
155
- end
156
- end
157
-
158
- opts.on_tail("-h", "--help", "Show help message") do
159
- puts opts
160
- exit
161
- end
162
-
163
- opts.on_tail("--version", "Show version #{RubyProf::VERSION}") do
164
- puts "ruby_prof " + RubyProf::VERSION
165
- exit
166
- end
167
-
168
- opts.on("-v","Show version, set $VERBOSE to true, profile script if option given") do
169
- puts "ruby version: " + [RUBY_PATCHLEVEL, RUBY_PLATFORM, RUBY_VERSION].join(' ')
170
- $VERBOSE = true
171
- end
172
-
173
- opts.on("-d", "Set $DEBUG to true") do
174
- $DEBUG = true
175
- end
176
-
177
- opts.on('-R lib', '--require-noprof lib', 'require a specific library (not profiled)') do |lib|
178
- options.pre_libs << lib
179
- end
180
-
181
- opts.on('-E code', '--eval-noprof code', 'execute the ruby statements (not profiled)') do |code|
182
- options.pre_execs << code
183
- end
184
-
185
- opts.on('-x regexp', '--exclude regexp', 'exclude methods by regexp (see method elimination)') do|meth|
186
- options.eliminate_methods ||= []
187
- options.eliminate_methods << Regexp.new(meth)
188
- end
189
-
190
- opts.on('-X file', '--exclude-file file', 'exclude methods by regexp listed in file (see method elimination)') do|file|
191
- options.eliminate_methods_files ||= []
192
- options.eliminate_methods_files << file
193
- end
194
-
195
- opts.on('--exclude-common-cycles', 'make common iterators like Integer#times appear inlined') do |meth|
196
- options.eliminate_methods ||= []
197
- options.eliminate_methods += %w{
198
- Integer#times
199
- Integer#upto
200
- Integer#downto
201
- Enumerator#each
202
- Enumerator#each_with_index
203
- Enumerator#each_with_object
204
-
205
- Array#each
206
- Array#each_index
207
- Array#reverse_each
208
- Array#map
209
-
210
- Hash#each
211
- Hash#each_pair
212
- Hash#each_key
213
- Hash#each_value
214
-
215
- Range#each
216
- Enumerable#each_cons
217
- Enumerable#each_entry
218
- Enumerable#each_slice
219
- Enumerable#each_with_index
220
- Enumerable#each_with_object
221
- Enumerable#reverse_each
222
- Enumerable#inject
223
- Enumerable#collect
224
- Enumerable#reduce
225
- }
226
- #TODO: may be the whole Enumerable module should be excluded via 'Enumerable#.*', we need feedback on use cases.
227
- end
228
-
229
- opts.on('--exclude-common-callbacks', 'make common callbacks invocations like Integer#times appear inlined so you can see call origins in graph') do|meth|
230
- options.eliminate_methods ||= []
231
- options.eliminate_methods += %w{
232
- Method#call
233
- Proc#call
234
- ActiveSupport::Callbacks::ClassMethods#__run_callback
235
- }
236
- end
237
- end
238
- end
239
-
240
- def parse_args
241
- # Make sure the user specified at least one file
242
- if ARGV.length < 1 and not options.exec
243
- puts self.option_parser
244
- puts ""
245
- puts "Must specify a script to run"
246
- exit(-1)
247
- end
248
-
249
- self.option_parser.parse! ARGV
250
- rescue OptionParser::InvalidOption, OptionParser::InvalidArgument, OptionParser::MissingArgument => e
251
- puts self.option_parser
252
- puts e.message
253
- exit(-1)
254
- end
255
-
256
- def load_pre_libs
257
- options.pre_libs.each do |lib|
258
- require lib
259
- end
260
- end
261
-
262
- def load_pre_execs
263
- options.pre_execs.each do |exec|
264
- eval(exec)
265
- end
266
- end
267
-
268
- def run
269
- # Get the script we will execute
270
- script = ARGV.shift
271
- if options.replace_prog_name
272
- $0 = File.expand_path(script)
273
- end
274
-
275
- # Set VM compile option
276
- if defined?(VM)
277
- VM::InstructionSequence.compile_option = {
278
- :trace_instruction => true,
279
- :specialized_instruction => options.specialized_instruction
280
- }
281
- end
282
-
283
- # Set the measure mode
284
- RubyProf.measure_mode = options.measure_mode
285
- RubyProf.start_script(script)
286
- end
287
- end
288
- end
289
-
290
- # Parse command line options
291
- cmd = RubyProf::Cmd.new
292
-
293
- # Install at_exit handler. It is important that we do this
294
- # before loading the scripts so our at_exit handler run
295
- # *after* any other one that will be installed.
296
-
297
- at_exit {
298
- # Stop profiling
299
- result = RubyProf.stop
300
-
301
- # Eliminate unwanted methods from call graph
302
- if cmd.options.eliminate_methods
303
- result.eliminate_methods!(cmd.options.eliminate_methods)
304
- end
305
-
306
- if cmd.options.eliminate_methods_files
307
- cmd.options.eliminate_methods_files.each {|f| result.eliminate_methods!(f)}
308
- end
309
-
310
- # Create a printer
311
- printer = cmd.options.printer.new(result)
312
- printer_options = {:min_percent => cmd.options.min_percent, :sort_method => cmd.options.sort_method}
313
-
314
- # Get output
315
- if cmd.options.file
316
- # write it relative to the dir they *started* in, as it's a bit surprising to write it in the dir they end up in.
317
- Dir.chdir(cmd.options.old_wd) do
318
- File.open(cmd.options.file, 'w') do |file|
319
- printer.print(file, printer_options)
320
- end
321
- end
322
- else
323
- # Print out results
324
- printer.print(STDOUT, printer_options)
325
- end
326
- }
327
-
328
- # Now profile some code
329
- cmd.run
1
+ #! /usr/bin/env ruby
2
+
3
+ # First require ruby-prof
4
+ require 'ruby-prof'
5
+
6
+ # Now setup option parser
7
+ require 'ostruct'
8
+ require 'optparse'
9
+
10
+ module RubyProf
11
+ # == Synopsis
12
+ #
13
+ # Profiles a Ruby program.
14
+ #
15
+ # == Usage
16
+ # ruby-prof [options] <script.rb> [--] [profiled-script-command-line-options]
17
+ #
18
+ # Options:
19
+ # -p, --printer=printer Select a printer:
20
+ # flat - Prints a flat profile as text (default).
21
+ # graph - Prints a graph profile as text.
22
+ # graph_html - Prints a graph profile as html.
23
+ # call_tree - format for KCacheGrind
24
+ # call_stack - prints a HTML visualization of the call tree
25
+ # dot - Prints a graph profile as a dot file
26
+ # multi - Creates several reports in output directory
27
+ # -m, --min_percent=min_percent The minimum percent a method must take before
28
+ # being included in output reports.
29
+ # This option is not supported for call tree.
30
+ # -f, --file=path Output results to a file instead of standard out.
31
+ # --mode=measure_mode Select what ruby-prof should measure:
32
+ # wall - Wall time (default).
33
+ # process - Process time.
34
+ # allocations - Object allocations (requires patched Ruby interpreter).
35
+ # memory - Allocated memory in KB (requires patched Ruby interpreter).
36
+ # -s, --sort=sort_mode Select how ruby-prof results should be sorted:
37
+ # total - Total time
38
+ # self - Self time
39
+ # wait - Wait time
40
+ # child - Child time
41
+ # --allow_exceptions Raise exceptions encountered during profiling (true) or suppress them (false)
42
+ # -R, --require-noprof=lib require a specific library (not profiled)
43
+ # -E, --eval-noprof=code execute the ruby statements (not profiled)
44
+ # --exclude=methods A comma separated list of methods to exclude.
45
+ # Specify instance methods via # (Integer#times)
46
+ # Specify class methods via . (Integer.superclass)
47
+ # --exclude-common Remove common methods from the profile
48
+ # -h, --help Show help message
49
+ # -v, --version version Show version (1.1.0)
50
+
51
+ class Cmd
52
+ # :enddoc:
53
+ attr_accessor :options
54
+ attr_reader :profile
55
+
56
+ def initialize
57
+ setup_options
58
+ parse_args
59
+
60
+ load_pre_libs
61
+ load_pre_execs
62
+ end
63
+
64
+ def setup_options
65
+ @options = OpenStruct.new
66
+ options.printer = RubyProf::FlatPrinter
67
+ options.measure_mode = RubyProf::WALL_TIME
68
+ options.min_percent = 0
69
+ options.file = nil
70
+ options.allow_exceptions = false
71
+ options.exclude_common = false
72
+ options.exclude = Array.new
73
+ options.pre_libs = Array.new
74
+ options.pre_execs = Array.new
75
+ end
76
+
77
+ # This is copied from ActiveSupport:
78
+ def constantize(camel_cased_word)
79
+ if !camel_cased_word.include?("::")
80
+ Object.const_get(camel_cased_word)
81
+ else
82
+ names = camel_cased_word.split("::")
83
+
84
+ # Trigger a built-in NameError exception including the ill-formed constant in the message.
85
+ Object.const_get(camel_cased_word) if names.empty?
86
+
87
+ # Remove the first blank element in case of '::ClassName' notation.
88
+ names.shift if names.size > 1 && names.first.empty?
89
+
90
+ names.inject(Object) do |constant, name|
91
+ if constant == Object
92
+ constant.const_get(name)
93
+ else
94
+ candidate = constant.const_get(name)
95
+ next candidate if constant.const_defined?(name, false)
96
+ next candidate unless Object.const_defined?(name)
97
+
98
+ # Go down the ancestors to check if it is owned directly. The check
99
+ # stops when we reach Object or the end of ancestors tree.
100
+ constant = constant.ancestors.inject(constant) do |const, ancestor|
101
+ break const if ancestor == Object
102
+ break ancestor if ancestor.const_defined?(name, false)
103
+ const
104
+ end
105
+
106
+ # owner is in Object, so raise
107
+ constant.const_get(name, false)
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ def option_parser
114
+ OptionParser.new do |opts|
115
+ opts.banner = "ruby_prof #{RubyProf::VERSION}\n" +
116
+ "Usage: ruby-prof [options] <script.rb> [--] [profiled-script-command-line-options]"
117
+
118
+ opts.separator ""
119
+ opts.separator "Options:"
120
+
121
+ opts.on('-p printer', '--printer=printer', [:flat, :flat_with_line_numbers, :graph, :graph_html, :call_tree, :call_stack, :dot, :multi],
122
+ 'Select a printer:',
123
+ ' flat - Prints a flat profile as text (default).',
124
+ ' graph - Prints a graph profile as text.',
125
+ ' graph_html - Prints a graph profile as html.',
126
+ ' call_tree - format for KCacheGrind',
127
+ ' call_stack - prints a HTML visualization of the call tree',
128
+ ' dot - Prints a graph profile as a dot file',
129
+ ' multi - Creates several reports in output directory'
130
+ ) do |printer|
131
+
132
+ case printer
133
+ when :flat
134
+ options.printer = RubyProf::FlatPrinter
135
+ when :graph
136
+ options.printer = RubyProf::GraphPrinter
137
+ when :graph_html
138
+ options.printer = RubyProf::GraphHtmlPrinter
139
+ when :call_tree
140
+ options.printer = RubyProf::CallTreePrinter
141
+ when :call_stack
142
+ options.printer = RubyProf::CallStackPrinter
143
+ when :dot
144
+ options.printer = RubyProf::DotPrinter
145
+ when :multi
146
+ options.printer = RubyProf::MultiPrinter
147
+ end
148
+ end
149
+
150
+ opts.on('-m min_percent', '--min_percent=min_percent', Float,
151
+ 'The minimum percent a method must take before ',
152
+ ' being included in output reports.',
153
+ ' This option is not supported for call tree.') do |min_percent|
154
+ options.min_percent = min_percent
155
+ end
156
+
157
+ opts.on('-f path', '--file=path',
158
+ 'Output results to a file instead of standard out.') do |file|
159
+ options.file = file
160
+ options.old_wd = Dir.pwd
161
+ end
162
+
163
+ opts.on('--mode=measure_mode',
164
+ [:process, :wall, :allocations, :memory],
165
+ 'Select what ruby-prof should measure:',
166
+ ' wall - Wall time (default).',
167
+ ' process - Process time.',
168
+ ' allocations - Object allocations (requires patched Ruby interpreter).',
169
+ ' memory - Allocated memory in KB (requires patched Ruby interpreter).') do |measure_mode|
170
+
171
+ case measure_mode
172
+ when :wall
173
+ options.measure_mode = RubyProf::WALL_TIME
174
+ when :process
175
+ options.measure_mode = RubyProf::PROCESS_TIME
176
+ when :allocations
177
+ options.measure_mode = RubyProf::ALLOCATIONS
178
+ when :memory
179
+ options.measure_mode = RubyProf::MEMORY
180
+ end
181
+ end
182
+
183
+ opts.on('-s sort_mode', '--sort=sort_mode', [:total, :self, :wait, :child],
184
+ 'Select how ruby-prof results should be sorted:',
185
+ ' total - Total time',
186
+ ' self - Self time',
187
+ ' wait - Wait time',
188
+ ' child - Child time') do |sort_mode|
189
+
190
+ options.sort_method = case sort_mode
191
+ when :total
192
+ :total_time
193
+ when :self
194
+ :self_time
195
+ when :wait
196
+ :wait_time
197
+ when :child
198
+ :children_time
199
+ end
200
+ end
201
+
202
+ opts.on_tail("-h", "--help", "Show help message") do
203
+ puts opts
204
+ exit
205
+ end
206
+
207
+ opts.on_tail("-v version", "--version", "Show version (#{RubyProf::VERSION})") do
208
+ puts "ruby_prof " + RubyProf::VERSION
209
+ exit
210
+ end
211
+
212
+ opts.on('--allow_exceptions', 'Raise exceptions encountered during profiling (true) or suppress them (false)') do
213
+ options.allow_exceptions = true
214
+ end
215
+
216
+ opts.on('-R lib', '--require-noprof=lib', 'require a specific library (not profiled)') do |lib|
217
+ options.pre_libs << lib
218
+ end
219
+
220
+ opts.on('-E code', '--eval-noprof=code', 'execute the ruby statements (not profiled)') do |code|
221
+ options.pre_execs << code
222
+ end
223
+
224
+ opts.on('--exclude=methods', String,
225
+ 'A comma separated list of methods to exclude.',
226
+ ' Specify instance methods via # (Integer#times)',
227
+ ' Specify class methods via . (Integer.superclass)') do |exclude_string|
228
+ exclude_string.split(',').each do |string|
229
+ match = string.strip.match(/(.*)(#|\.)(.*)/)
230
+ klass = constantize(match[1])
231
+ if match[2] == '.'
232
+ klass = klass.singleton_class
233
+ end
234
+ method = match[3].to_sym
235
+ options.exclude << [klass, method]
236
+ end
237
+ end
238
+
239
+ opts.on('--exclude-common', 'Remove common methods from the profile') do
240
+ options.exclude_common = true
241
+ end
242
+ end
243
+ end
244
+
245
+ def parse_args
246
+ # Make sure the user specified at least one file
247
+ if ARGV.length < 1 and not options.exec
248
+ puts self.option_parser
249
+ puts ""
250
+ puts "Must specify a script to run"
251
+ exit(-1)
252
+ end
253
+
254
+ self.option_parser.parse! ARGV
255
+
256
+ if options.printer.needs_dir?
257
+ options.file ||= "."
258
+ options.old_wd ||= Dir.pwd
259
+ if !File.directory?(options.file)
260
+ puts "'#{options.file}' is not a directory"
261
+ puts "#{options.printer} needs an existing directory path to put profiles under."
262
+ exit(-1)
263
+ end
264
+ end
265
+ rescue OptionParser::InvalidOption, OptionParser::InvalidArgument, OptionParser::MissingArgument => e
266
+ puts self.option_parser
267
+ puts e.message
268
+ exit(-1)
269
+ end
270
+
271
+ def load_pre_libs
272
+ options.pre_libs.each do |lib|
273
+ require lib
274
+ end
275
+ end
276
+
277
+ def load_pre_execs
278
+ options.pre_execs.each do |exec|
279
+ eval(exec)
280
+ end
281
+ end
282
+
283
+ def run
284
+ script = ARGV.shift
285
+ @profile = Profile.new(options.to_h)
286
+ options.exclude.each do |klass, method|
287
+ @profile.exclude_method!(klass, method)
288
+ end
289
+
290
+ profile.profile do
291
+ load script
292
+ end
293
+ end
294
+ end
295
+ end
296
+
297
+ # Parse command line options
298
+ cmd = RubyProf::Cmd.new
299
+
300
+ # Install at_exit handler. It is important that we do this
301
+ # before loading the scripts so our at_exit handler run
302
+ # *after* any other one that will be installed.
303
+
304
+ at_exit {
305
+ # Create a printer
306
+ printer = cmd.options.printer.new(cmd.profile)
307
+ printer_options = {:min_percent => cmd.options.min_percent, :sort_method => cmd.options.sort_method}
308
+
309
+ # Get output
310
+ if cmd.options.file
311
+ # write it relative to the dir they *started* in, as it's a bit surprising to write it in the dir they end up in.
312
+ Dir.chdir(cmd.options.old_wd) do
313
+ if printer.class.needs_dir?
314
+ printer.print(printer_options.merge(:path => cmd.options.file))
315
+ else
316
+ File.open(cmd.options.file, 'w') do |file|
317
+ printer.print(file, printer_options)
318
+ end
319
+ end
320
+ end
321
+ else
322
+ # Print out results
323
+ printer.print(STDOUT, printer_options)
324
+ end
325
+ }
326
+
327
+ # Now profile some code
328
+ cmd.run