diff options
-rw-r--r-- | actionpack/Rakefile | 24 | ||||
-rw-r--r-- | activerecord/Rakefile | 24 | ||||
-rw-r--r-- | railties/lib/rails/rubyprof_ext.rb | 35 | ||||
-rwxr-xr-x | tools/line_statistics | 41 | ||||
-rwxr-xr-x | tools/profile | 162 |
5 files changed, 159 insertions, 127 deletions
diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 7eab972595..d12213a2d8 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -40,27 +40,9 @@ task :release => :package do end task :lines do - lines, codelines, total_lines, total_codelines = 0, 0, 0, 0 - - FileList["lib/**/*.rb"].each do |file_name| - next if file_name =~ /vendor/ - File.open(file_name, 'r') do |f| - while line = f.gets - lines += 1 - next if line =~ /^\s*$/ - next if line =~ /^\s*#/ - codelines += 1 - end - end - puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}" - - total_lines += lines - total_codelines += codelines - - lines, codelines = 0, 0 - end - - puts "Total: Lines #{total_lines}, LOC #{total_codelines}" + load File.expand_path('..', File.dirname(__FILE__)) + '/tools/line_statistics' + files = FileList["lib/**/*.rb"] + CodeTools::LineStatistics.new(files).print_loc end rule '.rb' => '.y' do |t| diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 7769966a22..b1069e5dcc 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -146,27 +146,9 @@ task :drop_postgresql_databases => 'db:postgresql:drop' task :rebuild_postgresql_databases => 'db:postgresql:rebuild' task :lines do - lines, codelines, total_lines, total_codelines = 0, 0, 0, 0 - - FileList["lib/active_record/**/*.rb"].each do |file_name| - next if file_name =~ /vendor/ - File.open(file_name, 'r') do |f| - while line = f.gets - lines += 1 - next if line =~ /^\s*$/ - next if line =~ /^\s*#/ - codelines += 1 - end - end - puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}" - - total_lines += lines - total_codelines += codelines - - lines, codelines = 0, 0 - end - - puts "Total: Lines #{total_lines}, LOC #{total_codelines}" + load File.expand_path('..', File.dirname(__FILE__)) + '/tools/line_statistics' + files = FileList["lib/active_record/**/*.rb"] + CodeTools::LineStatistics.new(files).print_loc end spec = eval(File.read('activerecord.gemspec')) diff --git a/railties/lib/rails/rubyprof_ext.rb b/railties/lib/rails/rubyprof_ext.rb deleted file mode 100644 index 017eba3a76..0000000000 --- a/railties/lib/rails/rubyprof_ext.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'prof' - -module Prof #:nodoc: - # Adapted from Shugo Maeda's unprof.rb - def self.print_profile(results, io = $stderr) - total = results.detect { |i| - i.method_class.nil? && i.method_id == :"#toplevel" - }.total_time - total = 0.001 if total < 0.001 - - io.puts " %% cumulative self self total" - io.puts " time seconds seconds calls ms/call ms/call name" - - sum = 0.0 - results.each do |r| - sum += r.self_time - - name = if r.method_class.nil? - r.method_id.to_s - elsif r.method_class.is_a?(Class) - "#{r.method_class}##{r.method_id}" - else - "#{r.method_class}.#{r.method_id}" - end - io.printf "%6.2f %8.3f %8.3f %8d %8.2f %8.2f %s\n", - r.self_time / total * 100, - sum, - r.self_time, - r.count, - r.self_time * 1000 / r.count, - r.total_time * 1000 / r.count, - name - end - end -end diff --git a/tools/line_statistics b/tools/line_statistics new file mode 100755 index 0000000000..5eb5cfdc3d --- /dev/null +++ b/tools/line_statistics @@ -0,0 +1,41 @@ +#!/usr/bin/env ruby +# Example: +# files = FileList["lib/active_record/**/*.rb"] +# CodeTools::LineStatistics.new(files).print_loc +module CodeTools + class LineStatistics + + # @param files [Array, FileList, Enumerable] + # e.g. FileList["lib/active_record/**/*.rb"] + def initialize(files) + @files = Array(files).compact + end + + # Calculates LOC for each file + # Outputs each file and a total LOC + def print_loc + lines, codelines, total_lines, total_codelines = 0, 0, 0, 0 + + @files.each do |file_name| + next if file_name =~ /vendor/ + File.open(file_name, 'r') do |f| + while line = f.gets + lines += 1 + next if line =~ /^\s*$/ + next if line =~ /^\s*#/ + codelines += 1 + end + end + puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}" + + total_lines += lines + total_codelines += codelines + + lines, codelines = 0, 0 + end + + puts "Total: Lines #{total_lines}, LOC #{total_codelines}" + end + + end +end diff --git a/tools/profile b/tools/profile index fbea67492b..a35dd18b77 100755 --- a/tools/profile +++ b/tools/profile @@ -1,71 +1,133 @@ #!/usr/bin/env ruby # Example: -# tools/profile activesupport/lib/active_support.rb +# tools/profile activesupport/lib/active_support.rb [ruby-prof mode] [ruby-prof printer] ENV['NO_RELOAD'] ||= '1' ENV['RAILS_ENV'] ||= 'development' -require 'benchmark' +module CodeTools + class Profiler + Error = Class.new(StandardError) -module RequireProfiler - private - def require(file, *args) RequireProfiler.profile(file) { super } end - def load(file, *args) RequireProfiler.profile(file) { super } end + attr_reader :path, :mode + def initialize(path, mode=nil) + assert_ruby_file_exists(path) + @path, @mode = path, mode + require 'benchmark' + end + + def profile_requires + GC.start + before_rss = `ps -o rss= -p #{Process.pid}`.to_i + + if mode + require 'ruby-prof' + RubyProf.measure_mode = RubyProf.const_get(mode.upcase) + RubyProf.start + else + Object.instance_eval { include RequireProfiler } + end + + elapsed = Benchmark.realtime { require path } + results = RubyProf.stop if mode - @depth, @stats = 0, [] - class << self - attr_accessor :depth - attr_accessor :stats + GC.start + after_rss = `ps -o rss= -p #{Process.pid}`.to_i - def profile(file) - stats << [file, depth] - self.depth += 1 - result = nil - elapsed = Benchmark.realtime { result = yield } - self.depth -= 1 - stats.pop if stats.last.first == file - stats << [file, depth, elapsed] if result - result + if mode + if printer = ARGV.shift + puts "RubyProf outputting to stderr with printer #{printer}" + RubyProf.const_get("#{printer.to_s.classify}Printer").new(results).print($stdout) + elsif RubyProf.const_defined?(:CallStackPrinter) + filename = "#{File.basename(path, '.rb')}.#{mode}.html" + puts "RubyProf outputting to #{filename}" + File.open(filename, 'w') do |out| + RubyProf::CallStackPrinter.new(results).print(out) + end + else + filename = "#{File.basename(path, '.rb')}.#{mode}.callgrind" + puts "RubyProf outputting to #{filename}" + File.open(filename, 'w') do |out| + RubyProf::CallTreePrinter.new(results).print(out) + end + end + end + + RequireProfiler.stats.each do |file, depth, sec| + if sec + puts "%8.1f ms %s%s" % [sec * 1000, ' ' * depth, file] + else + puts "#{' ' * (13 + depth)}#{file}" + end + end + puts "%8.1f ms %d KB RSS" % [elapsed * 1000, after_rss - before_rss] end - end -end -GC.start -before_rss = `ps -o rss= -p #{Process.pid}`.to_i + private -path = ARGV.shift -if mode = ARGV.shift - require 'ruby-prof' - RubyProf.measure_mode = RubyProf.const_get(mode.upcase) - RubyProf.start -else - Object.instance_eval { include RequireProfiler } -end + def assert_ruby_file_exists(path) + fail Error.new("No such file") unless File.exists?(path) + fail Error.new("#{path} is a directory") if File.directory?(path) + ruby_extension = File.extname(path) == '.rb' + ruby_executable = File.open(path, 'rb') {|f| f.readline } =~ [/\A#!.*ruby/] + fail Error.new("Not a ruby file") unless ruby_extension or ruby_executable + end -elapsed = Benchmark.realtime { require path } -results = RubyProf.stop if mode + module RequireProfiler + private + def require(file, *args) RequireProfiler.profile(file) { super } end + def load(file, *args) RequireProfiler.profile(file) { super } end -GC.start -after_rss = `ps -o rss= -p #{Process.pid}`.to_i + @depth, @stats = 0, [] + class << self + attr_accessor :depth + attr_accessor :stats -if mode - if printer = ARGV.shift - RubyProf.const_get("#{printer.to_s.classify}Printer").new(results).print($stdout) - elsif RubyProf.const_defined?(:CallStackPrinter) - File.open("#{File.basename(path, '.rb')}.#{mode}.html", 'w') do |out| - RubyProf::CallStackPrinter.new(results).print(out) + def profile(file) + stats << [file, depth] + self.depth += 1 + result = nil + elapsed = Benchmark.realtime { result = yield } + self.depth -= 1 + stats.pop if stats.last.first == file + stats << [file, depth, elapsed] if result + result + end + end end - else - File.open("#{File.basename(path, '.rb')}.#{mode}.callgrind", 'w') do |out| - RubyProf::CallTreePrinter.new(results).print(out) + end +end +# ruby-prof printer name causes the third arg to be sent :classify +# which is probably overkill if you already know the name of the ruby-prof +# printer you want to use, e.g. Graph +begin + require 'active_support/inflector' + require 'active_support/core_ext/string/inflections' +rescue LoadError + STDERR.puts $!.message + class String + # File activesupport/lib/active_support/inflector/methods.rb, line 150 + def classify + # strip out any leading schema name + camelize(self.sub(/.*\./, '')) + end + # File activesupport/lib/active_support/inflector/methods.rb, line 68 + def camelize(uppercase_first_letter = true) + string = self + if uppercase_first_letter + string = string.sub(/^[a-z\d]*/) { $&.capitalize } + else + string = string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { $&.downcase } + end + string.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.gsub('/', '::') end end end - -RequireProfiler.stats.each do |file, depth, sec| - if sec - puts "%8.1f ms %s%s" % [sec * 1000, ' ' * depth, file] +if $0 == __FILE__ + if (filename = ARGV.shift) + path = File.expand_path(filename) + mode = ARGV.shift + CodeTools::Profiler.new(path, mode).profile_requires else - puts "#{' ' * (13 + depth)}#{file}" + STDERR.puts "No file path entered. Usage is tools/profile path/to/file.rb [ruby-prof mode] [ruby-prof printer]" end end -puts "%8.1f ms %d KB RSS" % [elapsed * 1000, after_rss - before_rss] |