aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Kemper <jeremy@bitsweat.net>2007-11-01 08:01:42 +0000
committerJeremy Kemper <jeremy@bitsweat.net>2007-11-01 08:01:42 +0000
commitdbcf59d47d49e8054a4d1c361dbd4856fe501244 (patch)
tree5727f80495c5a5e9f828713f6124a09cc568aa90
parent82314608b529debe36d5bfc88af5ee0fdd609eef (diff)
downloadrails-dbcf59d47d49e8054a4d1c361dbd4856fe501244.tar.gz
rails-dbcf59d47d49e8054a4d1c361dbd4856fe501244.tar.bz2
rails-dbcf59d47d49e8054a4d1c361dbd4856fe501244.zip
Profile an integration session instead of a single request
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@8065 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
-rwxr-xr-xactionpack/lib/action_controller/request_profiler.rb145
1 files changed, 58 insertions, 87 deletions
diff --git a/actionpack/lib/action_controller/request_profiler.rb b/actionpack/lib/action_controller/request_profiler.rb
index 88e25737a6..62f6e665f1 100755
--- a/actionpack/lib/action_controller/request_profiler.rb
+++ b/actionpack/lib/action_controller/request_profiler.rb
@@ -1,39 +1,46 @@
require 'optparse'
+require 'action_controller/integration'
module ActionController
class RequestProfiler
- # CGI with stubbed environment and standard input.
- class StubCGI < CGI
- attr_accessor :env_table, :stdinput
-
- def initialize(env_table, stdinput)
- @env_table = env_table
- super
- @stdinput = stdinput
- end
- end
-
- # Stripped-down dispatcher.
+ # Wrap up the integration session runner.
class Sandbox
- attr_accessor :env, :body
+ include Integration::Runner
- def self.benchmark(n, env, body)
- Benchmark.realtime { n.times { new(env, body).dispatch } }
+ def self.benchmark(n, script)
+ new(script).benchmark(n)
end
- def initialize(env, body)
- @env, @body = env, body
+ def initialize(script_path)
+ @quiet = false
+ define_run_method(File.read(script_path))
+ reset!
end
- def dispatch
- cgi = StubCGI.new(env, StringIO.new(body))
-
- request = CgiRequest.new(cgi)
- response = CgiResponse.new(cgi)
+ def benchmark(n)
+ @quiet = true
+ print ' '
+ result = Benchmark.realtime do
+ n.times do |i|
+ run
+ print i % 10 == 0 ? 'x' : '.'
+ $stdout.flush
+ end
+ end
+ puts
+ result
+ ensure
+ @quiet = false
+ end
- controller = Routing::Routes.recognize(request)
- controller.process(request, response)
+ def say(message)
+ puts " #{message}" unless @quiet
end
+
+ private
+ def define_run_method(script)
+ instance_eval "def run; #{script}; end", __FILE__, __LINE__
+ end
end
@@ -51,90 +58,58 @@ module ActionController
end
def run
- warmup
- options[:benchmark] ? benchmark : profile
- end
-
- def profile
- load_ruby_prof
-
- results = RubyProf.profile { benchmark }
-
- show_profile_results results
- results
- end
-
- def benchmark
- puts '%d req/sec' % (options[:n] / Sandbox.benchmark(options[:n], env, body))
- end
+ sandbox = Sandbox.new(options[:script])
- def warmup
- puts "#{options[:benchmark] ? 'Benchmarking' : 'Profiling'} #{options[:n]}x"
- puts "\nrequest headers: #{env.to_yaml}"
+ puts 'Warming up once'
- response = Sandbox.new(env, body).dispatch
+ elapsed = warmup(sandbox)
+ puts '%.2f sec, %d requests, %d req/sec' % [elapsed, sandbox.request_count, sandbox.request_count / elapsed]
+ puts "\n#{options[:benchmark] ? 'Benchmarking' : 'Profiling'} #{options[:n]}x"
- puts "\nresponse body: #{response.body[0...100]}#{'[...]' if response.body.size > 100}"
- puts "\nresponse headers: #{response.headers.to_yaml}"
- puts
+ options[:benchmark] ? benchmark(sandbox) : profile(sandbox)
end
+ def profile(sandbox)
+ load_ruby_prof
- def uri
- URI.parse(options[:uri])
- rescue URI::InvalidURIError
- URI.parse(default_uri)
- end
-
- def default_uri
- '/'
- end
-
- def env
- @env ||= default_env
- end
-
- def default_env
- defaults = {
- 'HTTP_HOST' => "#{uri.host || 'localhost'}:#{uri.port || 3000}",
- 'REQUEST_URI' => uri.path,
- 'REQUEST_METHOD' => method,
- 'CONTENT_LENGTH' => body.size }
+ results = RubyProf.profile { benchmark(sandbox) }
- if fixture = options[:fixture]
- defaults['CONTENT_TYPE'] = "multipart/form-data; boundary=#{extract_multipart_boundary(fixture)}"
- end
-
- defaults
+ show_profile_results results
+ results
end
- def method
- options[:method] || (options[:fixture] ? 'POST' : 'GET')
+ def benchmark(sandbox)
+ sandbox.request_count = 0
+ elapsed = sandbox.benchmark(options[:n]).to_f
+ count = sandbox.request_count.to_i
+ puts '%.2f sec, %d requests, %d req/sec' % [elapsed, count, count / elapsed]
end
- def body
- options[:fixture] ? File.read(options[:fixture]) : ''
+ def warmup(sandbox)
+ Benchmark.realtime { sandbox.run }
end
-
def default_options
- { :n => 1000, :open => 'open %s &' }
+ { :n => 100, :open => 'open %s &' }
end
# Parse command-line options
def parse_options(args)
OptionParser.new do |opt|
- opt.banner = "USAGE: #{$0} uri [options]"
+ opt.banner = "USAGE: #{$0} [options] [session script path]"
- opt.on('-u', '--uri [URI]', 'Request URI. Defaults to http://localhost:3000/') { |v| options[:uri] = v }
- opt.on('-n', '--times [0000]', 'How many requests to process. Defaults to 1000.') { |v| options[:n] = v.to_i }
+ opt.on('-n', '--times [0000]', 'How many requests to process. Defaults to 100.') { |v| options[:n] = v.to_i }
opt.on('-b', '--benchmark', 'Benchmark instead of profiling') { |v| options[:benchmark] = v }
- opt.on('--method [GET]', 'HTTP request method. Defaults to GET.') { |v| options[:method] = v.upcase }
- opt.on('--fixture [FILE]', 'Path to POST fixture file') { |v| options[:fixture] = v }
opt.on('--open [CMD]', 'Command to open profile results. Defaults to "open %s &"') { |v| options[:open] = v }
opt.on('-h', '--help', 'Show this help') { puts opt; exit }
opt.parse args
+
+ if args.empty?
+ puts opt
+ exit
+ end
+ options[:script] = args.pop
end
end
@@ -148,10 +123,6 @@ module ActionController
end
end
- def extract_multipart_boundary(path)
- File.open(path) { |f| f.readline }
- end
-
def show_profile_results(results)
File.open "#{RAILS_ROOT}/tmp/profile-graph.html", 'w' do |file|
RubyProf::GraphHtmlPrinter.new(results).print(file)