ruby-prof 0.13.1 → 1.4.2

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