rack-perftools_profiler 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ .rvmrc
7
+ *.gem
8
+ .bundle
9
+ Gemfile.lock
10
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
@@ -1,146 +1,160 @@
1
- = Rack::PerftoolsProfiler
1
+ # Rack::PerftoolsProfiler
2
2
 
3
- Middleware for profiling Rack-compatible apps using perftools.rb (http://github.com/tmm1/perftools.rb)
3
+ Middleware for profiling Rack-compatible apps using [perftools.rb](http://github.com/tmm1/perftools.rb)
4
4
 
5
- == Requirements
5
+ ## Quick start
6
+
7
+ Assuming your application is using Rails 3 (and you have installed the requirements in the next section), add the following code:
8
+
9
+ Gemfile:
10
+
11
+ gem 'rack-perftools_profiler', :require => 'rack/perftools_profiler'
12
+
13
+ config/environment.rb:
14
+
15
+ config.middleware.use ::Rack::PerftoolsProfiler, :default_printer => 'gif', :bundler => true
16
+
17
+ The visit the page you want to profile:
18
+
19
+ http://localhost:3000/some_action?profile=true
20
+
21
+ ## Requirements
6
22
 
7
23
  You'll need graphviz to generate call graphs using dot (for the GIF printer):
8
24
 
9
- sudo port install graphviz # OS X
10
- brew install graphviz # Homebrew
11
- sudo apt-get install graphviz # Debian/Ubuntu
25
+ sudo port install graphviz # OS X
26
+ brew install graphviz # Homebrew
27
+ sudo apt-get install graphviz # Debian/Ubuntu
12
28
 
13
29
  You'll need ps2pdf to generate PDFs (On OS X, ps2pdf comes is installed as part of Ghostscript)
14
-
15
- sudo port install ghostscript # OSX
16
- brew install ghostscript # Homebrew
17
- sudo apt-get install ps2pdf # Debian/Ubuntu
18
30
 
19
- == Configuration
31
+ sudo port install ghostscript # OSX
32
+ brew install ghostscript # Homebrew
33
+ sudo apt-get install ps2pdf # Debian/Ubuntu
34
+
35
+ ## Configuration
20
36
 
21
37
  Install the gem
22
38
 
23
- gem install rack-perftools_profiler
39
+ gem install rack-perftools_profiler
24
40
 
25
41
  Include the middleware
26
42
 
27
- require 'rack/perftools_profiler'
43
+ require 'rack/perftools_profiler'
28
44
 
29
- For Rails 2, add the following to config/environment.rb
45
+ For Rails 2, add the following to `config/environment.rb`
30
46
 
31
- config.gem 'rack-perftools_profiler', :lib => 'rack/perftools_profiler'
32
- require 'rack/perftools_profiler'
33
- config.middleware.use ::Rack::PerftoolsProfiler, :default_printer => 'gif'
47
+ config.gem 'rack-perftools_profiler', :lib => 'rack/perftools_profiler'
48
+ require 'rack/perftools_profiler'
49
+ config.middleware.use ::Rack::PerftoolsProfiler, :default_printer => 'gif'
34
50
 
35
51
  For Rails 3, add the following to your Gemfile
36
52
 
37
- gem 'rack-perftools_profiler', :require => 'rack/perftools_profiler'
53
+ gem 'rack-perftools_profiler', :require => 'rack/perftools_profiler'
38
54
 
39
55
  and add the following to config/application.rb
40
56
 
41
- config.middleware.use ::Rack::PerftoolsProfiler, :default_printer => 'gif', :bundler => true
57
+ config.middleware.use ::Rack::PerftoolsProfiler, :default_printer => 'gif', :bundler => true
42
58
 
43
- For Sinatra, call 'use' inside a configure block, like so:
59
+ For Sinatra, call `use` inside a configure block, like so:
44
60
 
45
- configure :profiling do
46
- use ::Rack::PerftoolsProfiler, :default_printer => 'gif'
47
- end
61
+ configure do
62
+ use ::Rack::PerftoolsProfiler, :default_printer => 'gif'
63
+ end
48
64
 
49
- For Rack::Builder, call 'use' inside the Builder constructor block
65
+ For Rack::Builder, call `use` inside the Builder constructor block
50
66
 
51
- Rack::Builder.new do
52
- use ::Rack::PerftoolsProfiler, :default_printer => 'gif'
53
- end
54
-
55
- == Options
67
+ Rack::Builder.new do
68
+ use ::Rack::PerftoolsProfiler, :default_printer => 'gif'
69
+ end
56
70
 
57
- * :default_printer - can be set to 'text', 'gif', or 'pdf'. Default is 'text'.
58
- * :mode - can be set to 'cputime', 'methods', 'objects', 'walltime'. Default is :cputime. See the 'Profiling Modes' section below.
59
- * :frequency - in :cputime mode, the number of times per second the app will be sampled. Default is 100 (times/sec).
60
- * :bundler - run the profiler binary using 'bundle' if set to true. Default is false.
61
- * :gemfile_dir - directory with Gemfile. Default is the current directory.
62
- * :password - password-protect profiling.
71
+ ## Options
63
72
 
64
- == Usage
73
+ * `:default_printer` - can be set to 'text', 'gif', or 'pdf'. Default is 'text'.
74
+ * `:mode` - can be set to 'cputime', 'methods', 'objects', 'walltime'. Default is :cputime. See the 'Profiling Modes' section below.
75
+ * `:frequency` - in :cputime mode, the number of times per second the app will be sampled. Default is 100 (times/sec).
76
+ * `:bundler` - run the profiler binary using 'bundle' if set to true. Default is false.
77
+ * `:gemfile_dir` - directory with Gemfile. Default is the current directory.
78
+ * `:password` - password-protect profiling.
79
+
80
+ ## Usage
65
81
 
66
82
  There are two ways to profile your app: with a single request or with multiple requests.
67
83
 
68
- To profile with a single request, visit the URL you want to profile, but
69
- add the 'profile' and (optionally) the 'times' GET params (which will rerun the action
70
- the specified number of times).
84
+ To profile with a single request, visit the URL you want to profile, but add the `profile` and (optionally) the `times` GET params (which will rerun the action the specified number of times).
85
+
86
+ Example:
71
87
 
72
- Example:
73
- curl http://localhost:3000/foobar?profile=true&times=3
88
+ curl http://localhost:3000/foobar?profile=true&times=3
74
89
 
75
90
  Note that this will change the status, body, and headers of the response (you'll get
76
91
  back the profiling data, NOT the original response).
77
92
 
78
- You can also profile your application using multiple requests. When you profile using this method,
79
- all responses are normal. You must visit \_\_stop\_\_ to complete profiling and then you can view
80
- the profiling data by visiting \_\_data\_\_.
93
+ You can also profile your application using multiple requests. When you profile using this method, all responses are normal. You must visit `__stop__` to complete profiling and then you can view the profiling data by visiting `__data__`.
81
94
 
82
95
  Example:
83
- curl http://localhost:3000/__start__
84
- curl http://localhost:3000/foobar
85
- curl http://localhost:3000/foobaz
86
- curl http://localhost:3000/__stop__
87
- curl http://localhost:3000/__data__
88
96
 
89
- == Profiling Data Options
97
+ curl http://localhost:3000/__start__
98
+ curl http://localhost:3000/foobar
99
+ curl http://localhost:3000/foobaz
100
+ curl http://localhost:3000/__stop__
101
+ curl http://localhost:3000/__data__
102
+
103
+ ## Profiling Data Options
90
104
 
91
- Regardless of how you profile your application, you can add additional params to change how the
92
- data is displayed. When using a single request, these params are just added to the URL being profiled.
93
- When using multiple requests, they are added to the \_\_data\_\_ URL.
105
+ Regardless of how you profile your application, you can add additional params to change how the
106
+ data is displayed. When using a single request, these params are just added to the URL being profiled.
107
+ When using multiple requests, they are added to the `__data__` URL.
94
108
 
95
109
  * printer - overrides the default_printer option (see above)
96
- * ignore - a regular expression of the area of code to ignore
110
+ * ignore - a regular expression of the area of code to ignore
97
111
  * focus - a regular expression of the area of code to solely focus on.
98
112
 
99
113
  (for 'ignore' and 'focus', please see http://google-perftools.googlecode.com/svn/trunk/doc/cpuprofile.html#pprof
100
114
  for more details)
101
115
 
102
- == Profiling Modes
116
+ ## Profiling Modes
103
117
 
104
118
  perftools.rb (and therefore, the Rack middleware) can be put into three different profiling modes.
105
119
 
106
- * CPU time mode - Reports how many CPU cycles are spent in each section of code. This is the default and can be enabled by setting ':mode => :cputime'
107
- * Method call mode - Report how many method calls are made inside each method. Enable by setting ':mode => :methods'
108
- * Object allocation mode - Reports the percentage of object allocations performed in each section of code. Enable by setting ':mode => :objects'
109
- * Wall time mode - Reports the amount of time (as in, wall clock time) spent in each section of code. Enable by setting ':mode => :walltime'
120
+ * CPU time mode - Reports how many CPU cycles are spent in each section of code. This is the default and can be enabled by setting `:mode => :cputime`
121
+ * Method call mode - Report how many method calls are made inside each method. Enable by setting `:mode => :methods`
122
+ * Object allocation mode - Reports the percentage of object allocations performed in each section of code. Enable by setting `:mode => :objects`
123
+ * Wall time mode - Reports the amount of time (as in, wall clock time) spent in each section of code. Enable by setting `:mode => :walltime`
110
124
 
111
125
  For example, consider the following Sinatra application:
112
126
 
113
- require 'sinatra'
114
- require 'rack/perftools_profiler'
127
+ require 'sinatra'
128
+ require 'rack/perftools_profiler'
115
129
 
116
- configure do
117
- use ::Rack::PerftoolsProfiler, :default_printer => 'gif', :mode => :cputime
118
- end
130
+ configure do
131
+ use ::Rack::PerftoolsProfiler, :default_printer => 'gif', :mode => :cputime
132
+ end
119
133
 
120
- get "/slow" do
121
- sleep(3)
122
- "hello"
123
- end
134
+ get "/slow" do
135
+ sleep(3)
136
+ "hello"
137
+ end
124
138
 
125
139
  In the default mode, there will be no profiling data for the 'slow' route, because it uses few CPU cycles (You'll see the message 'No nodes to print').
126
140
 
127
- If you change the mode to ':walltime', you'll get profiling data, since the call to 'sleep' causes the code to spend several seconds of wall time in the block.
141
+ If you change the mode to `:walltime`, you'll get profiling data, since the call to `sleep` causes the code to spend several seconds of wall time in the block.
128
142
 
129
- == Overriding the Profiling mode
143
+ ## Overriding the Profiling mode
130
144
 
131
145
  You can also switch the profiling mode on a per-request basis, but ONLY if you are switching to 'methods' or 'objects' mode. Due to the implementation of perftools.rb, it is NOT possible to switch to 'walltime' or 'cputime' modes.
132
146
 
133
147
  To switch to another mode, provide the 'mode' option. When profiling with a single request, add the option to the URL profiled:
134
148
 
135
- curl http://localhost:3000/foobar?profile=true&mode=objects
149
+ curl http://localhost:3000/foobar?profile=true&mode=objects
136
150
 
137
- When profiling using multiple requests, add the option when visiting \_\_start\_\_ :
151
+ When profiling using multiple requests, add the option when visiting `__start__` :
138
152
 
139
- curl http://localhost:3000/__start__?mode=objects
153
+ curl http://localhost:3000/__start__?mode=objects
140
154
 
141
155
  If the 'mode' option is omitted, the middleware will default to the mode specified at configuration.
142
156
 
143
- == Profiling in production
157
+ ## Profiling in production
144
158
 
145
159
  It is recommended that you always profile your application in the 'production' environment (using `rails server -e production` or an equivalent), since there can be important differences between 'development' and 'production' that may affect performance.
146
160
 
@@ -148,31 +162,34 @@ However, it is recommended that you profile your application on a development or
148
162
 
149
163
  Profiling a single request will work if there are multiple server processes. If your staging machine is publicly accessible, you can password-protect single-request profiling by using the `:password` option and then using the `profile` GET parameter to provide the password:
150
164
 
151
- curl http://localhost:3000/foobar?profile=PASSWORD
165
+ curl http://localhost:3000/foobar?profile=PASSWORD
152
166
 
153
- == Changing behavior with environment variables
167
+ ## Changing behavior with environment variables
154
168
 
155
169
  The mode and frequency settings are enabled by setting environment variables. Some of these environment variables must be set before 'perftools' is required. If you only require 'rack/perftools_profiler', it will do the right thing (require 'perftools' after setting the environment variables).
156
170
 
157
171
  If you need to require 'perftools' before 'rack/perftools_profiler' (or you have other problems changing the mode or frequency), try using these environment variables yourself.
158
172
 
159
173
  Setting the frequency:
160
- CPUPROFILE_FREQUENCY=500 ruby your_app.rb
174
+
175
+ CPUPROFILE_FREQUENCY=500 ruby your_app.rb
161
176
 
162
177
  Setting the mode to 'wall time'
163
- CPUPROFILE_REALTIME=1 ruby your_app.rb
178
+
179
+ CPUPROFILE_REALTIME=1 ruby your_app.rb
164
180
 
165
181
  Setting the mode to 'object allocation'
166
- CPUPROFILE_OBJECTS=1 ruby your_app.rb
167
182
 
168
- == Acknowledgments
183
+ CPUPROFILE_OBJECTS=1 ruby your_app.rb
184
+
185
+ ## Acknowledgments
169
186
 
170
187
  A huge thanks to Aman Gupta for the awesome perftools.rb gem.
171
188
 
172
189
  The basic idea and initial implementation of the middleware was heavily influenced by Rack::Profiler from rack-contrib.
173
190
 
174
- == Note on Patches/Pull Requests
175
-
191
+ ## Note on Patches/Pull Requests
192
+
176
193
  * Fork the project.
177
194
  * Make your feature addition or bug fix.
178
195
  * Add tests for it. This is important so I don't break it in a
@@ -181,6 +198,6 @@ The basic idea and initial implementation of the middleware was heavily influenc
181
198
  (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
182
199
  * Send me a pull request. Bonus points for topic branches.
183
200
 
184
- == Copyright
201
+ ## Copyright
185
202
 
186
203
  Copyright (c) 2010-2011 Ben Brinckerhoff. See LICENSE for details.
data/Rakefile CHANGED
@@ -1,27 +1,4 @@
1
- require 'rubygems'
2
- require 'rake'
3
-
4
- begin
5
- require 'jeweler'
6
- Jeweler::Tasks.new do |gem|
7
- gem.name = 'rack-perftools_profiler'
8
- gem.summary = %Q{Middleware for profiling Rack-compatible apps using perftools.rb}
9
- gem.description = %Q{Middleware for profiling Rack-compatible apps using perftools.rb}
10
- gem.email = 'ben@bbrinck.com'
11
- gem.homepage = 'http://github.com/bhb/rack-perftools_profiler'
12
- gem.authors = ['Ben Brinckerhoff']
13
- gem.add_dependency 'perftools.rb', '~> 0.5.6'
14
- gem.add_dependency 'rack', '~> 1.0'
15
- gem.add_dependency('open4', '~> 1.0')
16
- gem.add_development_dependency 'rack', '~> 1.1'
17
- gem.add_development_dependency 'shoulda', '~> 2.10'
18
- gem.add_development_dependency 'mocha', '~> 0.9'
19
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
20
- end
21
- Jeweler::GemcutterTasks.new
22
- rescue LoadError
23
- puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
24
- end
1
+ require "bundler/gem_tasks"
25
2
 
26
3
  require 'rake/testtask'
27
4
  Rake::TestTask.new(:test) do |test|
@@ -29,34 +6,3 @@ Rake::TestTask.new(:test) do |test|
29
6
  test.pattern = 'test/**/*_test.rb'
30
7
  test.verbose = true
31
8
  end
32
-
33
- begin
34
- require 'rcov/rcovtask'
35
- Rcov::RcovTask.new do |test|
36
- test.libs << 'test'
37
- test.pattern = 'test/**/*_test.rb'
38
- test.verbose = true
39
- end
40
- rescue LoadError
41
- task :rcov do
42
- abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
43
- end
44
- end
45
-
46
- task :test => :check_dependencies
47
-
48
- task :default => :test
49
-
50
- require 'rake/rdoctask'
51
- Rake::RDocTask.new do |rdoc|
52
- if File.exist?('VERSION')
53
- version = File.read('VERSION')
54
- else
55
- version = ""
56
- end
57
-
58
- rdoc.rdoc_dir = 'rdoc'
59
- rdoc.title = "rack-perftools_profiler #{version}"
60
- rdoc.rdoc_files.include('README*')
61
- rdoc.rdoc_files.include('lib/**/*.rb')
62
- end
@@ -1,5 +1,5 @@
1
1
  module Rack::PerftoolsProfiler
2
-
2
+
3
3
  class Action
4
4
 
5
5
  def initialize(env, profiler, middleware)
@@ -9,35 +9,39 @@ module Rack::PerftoolsProfiler
9
9
  @profiler = profiler
10
10
  @middleware = middleware
11
11
  end
12
-
12
+
13
13
  def act
14
14
  # do nothing
15
15
  end
16
16
 
17
17
  def self.for_env(env, profiler, middleware)
18
18
  request = Rack::Request.new(env)
19
- klass =
20
- if !profiler.password_valid?(request.GET['profile'])
21
- ReturnPasswordError
19
+ password = request.GET['profile']
20
+ accepted = profiler.accepts?(password)
21
+ klass =
22
+ case request.path_info
23
+ when %r{/__start__$}
24
+ password_protect(StartProfiling, accepted)
25
+ when %r{/__stop__$}
26
+ password_protect(StopProfiling, accepted)
27
+ when %r{/__data__$}
28
+ password_protect(ReturnData, accepted)
22
29
  else
23
- case request.path_info
24
- when %r{/__start__$}
25
- StartProfiling
26
- when %r{/__stop__$}
27
- StopProfiling
28
- when %r{/__data__$}
29
- ReturnData
30
+ if ProfileOnce.has_special_param?(request)
31
+ password_protect(ProfileOnce, accepted)
30
32
  else
31
- if ProfileOnce.has_special_param?(request)
32
- ProfileOnce
33
- else
34
- CallAppDirectly
35
- end
33
+ CallAppDirectly
36
34
  end
37
35
  end
38
36
  klass.new(env, profiler, middleware)
39
37
  end
40
38
 
39
+ private
40
+
41
+ def self.password_protect(klass, accepted)
42
+ accepted ? klass : ReturnPasswordError
43
+ end
44
+
41
45
  end
42
46
 
43
47
  end
@@ -34,7 +34,7 @@ module Rack::PerftoolsProfiler
34
34
  @mode = (options.delete(:mode) { DEFAULT_MODE }).to_sym
35
35
  @bundler = options.delete(:bundler) { false }
36
36
  @gemfile_dir = options.delete(:gemfile_dir) { DEFAULT_GEMFILE_DIR }
37
- @password = options.delete(:password) { nil }
37
+ @password = options.delete(:password) { :not_set }
38
38
  @mode_for_request = nil
39
39
  ProfileDataAction.check_printer(@printer)
40
40
  ensure_mode_is_valid(@mode)
@@ -43,7 +43,7 @@ module Rack::PerftoolsProfiler
43
43
  require 'perftools'
44
44
  raise ProfilerArgumentError, "Invalid option(s): #{options.keys.join(' ')}" unless options.empty?
45
45
  end
46
-
46
+
47
47
  def profile(mode = nil)
48
48
  start(mode)
49
49
  yield
@@ -55,16 +55,28 @@ module Rack::PerftoolsProfiler
55
55
  ::File.delete(PROFILING_DATA_FILE) if ::File.exists?(PROFILING_DATA_FILE)
56
56
  end
57
57
 
58
+ def accepts?(password)
59
+ if password_protected?
60
+ password_valid?(password)
61
+ else
62
+ true
63
+ end
64
+ end
65
+
58
66
  def password_valid?(password)
59
67
  @password.nil? || password == @password
60
68
  end
61
-
69
+
70
+ def password_protected?
71
+ @password != :not_set
72
+ end
73
+
62
74
  def start(mode = nil)
63
75
  ensure_mode_is_changeable(mode) if mode
64
76
  PerfTools::CpuProfiler.stop
65
77
  if (mode)
66
78
  @mode_for_request = mode
67
- end
79
+ end
68
80
  unset_env_vars
69
81
  set_env_vars
70
82
  PerfTools::CpuProfiler.start(PROFILING_DATA_FILE)
@@ -146,7 +158,7 @@ module Rack::PerftoolsProfiler
146
158
  ENV.delete('CPUPROFILE_OBJECTS')
147
159
  ENV.delete('CPUPROFILE_METHODS')
148
160
  end
149
-
161
+
150
162
  def profiling=(value)
151
163
  pstore_transaction(false) do |store|
152
164
  store[:profiling?] = value