bin/ruby-prof in ruby-prof-1.1.0 vs bin/ruby-prof in ruby-prof-1.2.0
- old
+ new
@@ -1,9 +1,8 @@
#! /usr/bin/env ruby
# First require ruby-prof
-require 'rubygems'
require 'ruby-prof'
# Now setup option parser
require 'ostruct'
require 'optparse'
@@ -12,68 +11,49 @@
# == Synopsis
#
# Profiles a Ruby program.
#
# == Usage
+ # ruby-prof [options] <script.rb> [--] [profiled-script-command-line-options]
#
- # ruby_prof [options] <script.rb> [--] [script-options]
- #
# Options:
- # -p, --printer=printer Select a printer:
- # flat - Prints a flat profile as text (default).
- # graph - Prints a graph profile as text.
- # graph_html - Prints a graph profile as html.
- # call_tree - format for KCacheGrind
- # call_stack - prints a HTML visualization of the call tree
- # dot - Prints a graph profile as a dot file
- # multi - Creates several reports in output directory
- #
- # -m, --min_percent=min_percent The minimum percent a method must take before
- # being included in output reports. This option is not supported for call tree reports.
- #
- # -f, --file=path Output results to a file instead of standard out.
- #
- # --mode=measure_mode Select what ruby-prof should measure:
- # wall - Wall time (default)
- # process - Process time
- # allocations - Object allocations
- # memory - Allocated memory
- #
- # -s, --sort=sort_mode Select how ruby-prof results should be sorted:
- # total - Total time
- # self - Self time
- # wait - Wait time
- # child - Child time
- #
- # --replace-progname Replace $0 when loading the .rb files.
- #
- # --specialized-instruction Turn on specified instruction.
- #
- # -v Show version, set $VERBOSE to true, profile script if option given
- #
- # -d Set $DEBUG to true
- #
- # -R, --require-noprof lib Require a specific library (not profiled)
- #
- # -E, --eval-noprof code Execute the ruby statements (not profiled)
- #
- # -x, --exclude regexp Exclude methods by regexp (see method elimination)
- #
- # -X, --exclude-file file Exclude methods by regexp listed in file (see method elimination)
- #
- # --exclude-common-cycles Make common iterators like Integer#times appear inlined
- #
- # --exclude-common-callbacks Make common callbacks invocations like Integer#times appear inlined so you can see call origins in graph
- #
- # -h, --help Show help message
- #
- # --version Show version
- #
- #
+ # -p, --printer=printer Select a printer:
+ # flat - Prints a flat profile as text (default).
+ # graph - Prints a graph profile as text.
+ # graph_html - Prints a graph profile as html.
+ # call_tree - format for KCacheGrind
+ # call_stack - prints a HTML visualization of the call tree
+ # dot - Prints a graph profile as a dot file
+ # multi - Creates several reports in output directory
+ # -m, --min_percent=min_percent The minimum percent a method must take before
+ # being included in output reports.
+ # This option is not supported for call tree.
+ # -f, --file=path Output results to a file instead of standard out.
+ # --mode=measure_mode Select what ruby-prof should measure:
+ # wall - Wall time (default).
+ # process - Process time.
+ # allocations - Object allocations (requires patched Ruby interpreter).
+ # memory - Allocated memory in KB (requires patched Ruby interpreter).
+ # -s, --sort=sort_mode Select how ruby-prof results should be sorted:
+ # total - Total time
+ # self - Self time
+ # wait - Wait time
+ # child - Child time
+ # --allow_exceptions Raise exceptions encountered during profiling (true) or suppress them (false)
+ # -R, --require-noprof=lib require a specific library (not profiled)
+ # -E, --eval-noprof=code execute the ruby statements (not profiled)
+ # --exclude=methods A comma separated list of methods to exclude.
+ # Specify instance methods via # (Integer#times)
+ # Specify class methods via . (Integer.superclass)
+ # --exclude-common Remove common methods from the profile
+ # -h, --help Show help message
+ # -v, --version version Show version (1.1.0)
+
class Cmd
# :enddoc:
attr_accessor :options
+ attr_reader :profile
def initialize
setup_options
parse_args
@@ -82,19 +62,56 @@
end
def setup_options
@options = OpenStruct.new
options.printer = RubyProf::FlatPrinter
+ options.measure_mode = RubyProf::WALL_TIME
options.min_percent = 0
options.file = nil
- options.replace_prog_name = false
- options.specialized_instruction = false
-
+ options.allow_exceptions = false
+ options.exclude_common = false
+ options.exclude = Array.new
options.pre_libs = Array.new
options.pre_execs = Array.new
end
+ # This is copied from ActiveSupport:
+ def constantize(camel_cased_word)
+ if !camel_cased_word.include?("::")
+ Object.const_get(camel_cased_word)
+ else
+ names = camel_cased_word.split("::")
+
+ # Trigger a built-in NameError exception including the ill-formed constant in the message.
+ Object.const_get(camel_cased_word) if names.empty?
+
+ # Remove the first blank element in case of '::ClassName' notation.
+ names.shift if names.size > 1 && names.first.empty?
+
+ names.inject(Object) do |constant, name|
+ if constant == Object
+ constant.const_get(name)
+ else
+ candidate = constant.const_get(name)
+ next candidate if constant.const_defined?(name, false)
+ next candidate unless Object.const_defined?(name)
+
+ # Go down the ancestors to check if it is owned directly. The check
+ # stops when we reach Object or the end of ancestors tree.
+ constant = constant.ancestors.inject(constant) do |const, ancestor|
+ break const if ancestor == Object
+ break ancestor if ancestor.const_defined?(name, false)
+ const
+ end
+
+ # owner is in Object, so raise
+ constant.const_get(name, false)
+ end
+ end
+ end
+ end
+
def option_parser
OptionParser.new do |opts|
opts.banner = "ruby_prof #{RubyProf::VERSION}\n" +
"Usage: ruby-prof [options] <script.rb> [--] [profiled-script-command-line-options]"
@@ -180,99 +197,50 @@
when :child
:children_time
end
end
- opts.on("--replace-progname", "Replace $0 when loading the .rb files.") do
- options.replace_prog_name = true
- end
-
- if defined?(RubyVM)
- opts.on("--specialized-instruction", "Turn on specified instruction.") do
- options.specialized_instruction = true
- end
- end
-
opts.on_tail("-h", "--help", "Show help message") do
puts opts
exit
end
- opts.on_tail("--version", "Show version #{RubyProf::VERSION}") do
+ opts.on_tail("-v version", "--version", "Show version (#{RubyProf::VERSION})") do
puts "ruby_prof " + RubyProf::VERSION
exit
end
- opts.on("-v","Show version, set $VERBOSE to true, profile script if option given") do
- puts "ruby version: " + [RUBY_PATCHLEVEL, RUBY_PLATFORM, RUBY_VERSION].join(' ')
- $VERBOSE = true
+ opts.on('--allow_exceptions', 'Raise exceptions encountered during profiling (true) or suppress them (false)') do
+ options.allow_exceptions = true
end
- opts.on("-d", "Set $DEBUG to true") do
- $DEBUG = true
- end
-
- opts.on('-R lib', '--require-noprof lib', 'require a specific library (not profiled)') do |lib|
+ opts.on('-R lib', '--require-noprof=lib', 'require a specific library (not profiled)') do |lib|
options.pre_libs << lib
end
- opts.on('-E code', '--eval-noprof code', 'execute the ruby statements (not profiled)') do |code|
+ opts.on('-E code', '--eval-noprof=code', 'execute the ruby statements (not profiled)') do |code|
options.pre_execs << code
end
- opts.on('-x regexp', '--exclude regexp', 'exclude methods by regexp (see method elimination)') do |meth|
- options.eliminate_methods ||= []
- options.eliminate_methods << Regexp.new(meth)
+ opts.on('--exclude=methods', String,
+ 'A comma separated list of methods to exclude.',
+ ' Specify instance methods via # (Integer#times)',
+ ' Specify class methods via . (Integer.superclass)') do |exclude_string|
+ exclude_string.split(',').each do |string|
+ match = string.strip.match(/(.*)(#|\.)(.*)/)
+ klass = constantize(match[1])
+ if match[2] == '.'
+ klass = klass.singleton_class
+ end
+ method = match[3].to_sym
+ options.exclude << [klass, method]
+ end
end
- opts.on('-X file', '--exclude-file file', 'exclude methods by regexp listed in file (see method elimination)') do|file|
- options.eliminate_methods_files ||= []
- options.eliminate_methods_files << file
+ opts.on('--exclude-common', 'Remove common methods from the profile') do
+ options.exclude_common = true
end
-
- opts.on('--exclude-common-cycles', 'make common iterators like Integer#times appear inlined') do |meth|
- options.eliminate_methods ||= []
- options.eliminate_methods += %w{
- Integer#times
- Integer#upto
- Integer#downto
- Enumerator#each
- Enumerator#each_with_index
- Enumerator#each_with_object
-
- Array#each
- Array#each_index
- Array#reverse_each
- Array#map
-
- Hash#each
- Hash#each_pair
- Hash#each_key
- Hash#each_value
-
- Range#each
- Enumerable#each_cons
- Enumerable#each_entry
- Enumerable#each_slice
- Enumerable#each_with_index
- Enumerable#each_with_object
- Enumerable#reverse_each
- Enumerable#inject
- Enumerable#collect
- Enumerable#reduce
- }
- #TODO: may be the whole Enumerable module should be excluded via 'Enumerable#.*', we need feedback on use cases.
- end
-
- opts.on('--exclude-common-callbacks', 'make common callbacks invocations like Integer#times appear inlined so you can see call origins in graph') do|meth|
- options.eliminate_methods ||= []
- options.eliminate_methods += %w{
- Method#call
- Proc#call
- ActiveSupport::Callbacks::ClassMethods#__run_callback
- }
- end
end
end
def parse_args
# Make sure the user specified at least one file
@@ -311,27 +279,19 @@
eval(exec)
end
end
def run
- # Get the script we will execute
script = ARGV.shift
- if options.replace_prog_name
- $0 = File.expand_path(script)
+ @profile = Profile.new(options.to_h)
+ options.exclude.each do |klass, method|
+ @profile.exclude_method!(klass, method)
end
- # Set VM compile option
- if defined?(RubyVM)
- RubyVM::InstructionSequence.compile_option = {
- :trace_instruction => true,
- :specialized_instruction => options.specialized_instruction
- }
+ profile.profile do
+ load script
end
-
- # Set the measure mode
- RubyProf.measure_mode = options.measure_mode if options.measure_mode
- RubyProf.start_script(script)
end
end
end
# Parse command line options
@@ -340,23 +300,11 @@
# Install at_exit handler. It is important that we do this
# before loading the scripts so our at_exit handler run
# *after* any other one that will be installed.
at_exit {
- # Stop profiling
- result = RubyProf.stop
-
- # Eliminate unwanted methods from call graph
- if cmd.options.eliminate_methods
- result.eliminate_methods!(cmd.options.eliminate_methods)
- end
-
- if cmd.options.eliminate_methods_files
- cmd.options.eliminate_methods_files.each {|f| result.eliminate_methods!(f)}
- end
-
# Create a printer
- printer = cmd.options.printer.new(result)
+ printer = cmd.options.printer.new(cmd.profile)
printer_options = {:min_percent => cmd.options.min_percent, :sort_method => cmd.options.sort_method}
# Get output
if cmd.options.file
# 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.