1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
|
# Using with Ruby profilers
Test Prof allows you to use general Ruby profilers to profile test suites without needing to write any profiling code yourself.
Just install the profiler library and run your tests!
Supported profilers:
- [StackProf](#stackprof)
- [Vernier](#vernier)
- [RubyProf](#rubyprof)
## StackProf
[StackProf][] is a sampling call-stack profiler for Ruby.
Make sure you have `stackprof` in your dependencies:
```ruby
# Gemfile
group :development, :test do
gem "stackprof", ">= 0.2.9", require: false
end
```
### Profiling the whole test suite with StackProf
**NOTE:** It's recommended to use [test sampling](../recipes/tests_sampling.md) to generate smaller profiling reports.
You can activate StackProf profiling by setting the `TEST_STACK_PROF` env variable:
```sh
TEST_STACK_PROF=1 bundle exec rake test
# or for RSpec
TEST_STACK_PROF=1 bundle exec rspec ...
```
At the end of the test run, you will see the message from Test Prof including paths to generated reports (raw StackProf format and JSON):
```sh
...
[TEST PROF INFO] StackProf report generated: tmp/test_prof/stack-prof-report-wall-raw-total.dump
[TEST PROF INFO] StackProf JSON report generated: tmp/test_prof/stack-prof-report-wall-raw-total.json
```
We recommend uploading JSON reports to [Speedscope][] and analyze flamegraphs. Otherwise, feel free to use the `stackprof` CLI
to manipulate the raw report.
### Profiling individual examples with StackProf
Test Prof provides a built-in shared context for RSpec to profile examples individually:
```ruby
it "is doing heavy stuff", :sprof do
# ...
end
```
**NOTE:** per-example profiling doesn't work when the global (per-suite) profiling is activated.
### Profiling application boot with StackProf
The application boot time could also makes testing slower. Try to profile your boot process with StackProf using the following command:
```sh
# pick some random spec (1 is enough)
$ TEST_STACK_PROF=boot bundle exec rspec ./spec/some_spec.rb
...
[TEST PROF INFO] StackProf report generated: tmp/test_prof/stack-prof-report-wall-raw-boot.dump
[TEST PROF INFO] StackProf JSON report generated: tmp/test_prof/stack-prof-report-wall-raw-boot.json
```
### StackProf configuration
You can change StackProf mode (which is `wall` by default) through `TEST_STACK_PROF_MODE` env variable.
You can also change StackProf interval through `TEST_STACK_PROF_INTERVAL` env variable.
For modes `wall` and `cpu`, `TEST_STACK_PROF_INTERVAL` represents microseconds and will default to 1000 as per `stackprof`.
For mode `object`, `TEST_STACK_PROF_INTERVAL` represents allocations and will default to 1 as per `stackprof`.
You can disable garbage collection frames by setting `TEST_STACK_PROF_IGNORE_GC` env variable.
Garbage collection time will still be present in the profile but not explicitly marked with
its own frame.
See [stack_prof.rb](https://github.com/test-prof/test-prof/tree/master/lib/test_prof/stack_prof.rb) for all available configuration options and their usage.
## Vernier
[Vernier][] is next generation sampling profiler for Ruby. Give it a try and see if it can help in identifying test peformance bottlenecks!
Make sure you have `vernier` in your dependencies:
```ruby
# Gemfile
group :development, :test do
gem "vernier", ">= 0.3.0", require: false
end
```
### Profiling the whole test suite with Vernier
**NOTE:** It's recommended to use [test sampling](../recipes/tests_sampling.md) to generate smaller profiling reports.
You can activate Verner profiling by setting the `TEST_VERNIER` env variable:
```sh
TEST_VERNIER=1 bundle exec rake test
# or for RSpec
TEST_VERNIER=1 bundle exec rspec ...
```
At the end of the test run, you will see the message from Test Prof including the path to the generated report:
```sh
...
[TEST PROF INFO] Vernier report generated: tmp/test_prof/vernier-report-wall-raw-total.json
```
Use the [profile-viewer](https://github.com/tenderlove/profiler/tree/ruby) gem or upload your profiles to [vernier.prof](https://vernier.prof). Alternatively, you can use [profiler.firefox.com](https://profiler.firefox.com) which profile-viewer is a fork of.
### Profiling individual examples with Vernier
Test Prof provides a built-in shared context for RSpec to profile examples individually:
```ruby
it "is doing heavy stuff", :vernier do
# ...
end
```
**NOTE:** per-example profiling doesn't work when the global (per-suite) profiling is activated.
### Profiling application boot with Vernier
You can also profile your application boot process:
```sh
# pick some random spec (1 is enough)
TEST_VERNIER=boot bundle exec rspec ./spec/some_spec.rb
```
### Add markers from Active Support Notifications
You can add more insights to the resulting report by adding event markers from Active Support Notifications:
```sh
TEST_VERNIER=1 TEST_VERNIER_HOOKS=rails bundle exec rake test
# or for RSpec
TEST_VERNIER=1 TEST_VERNIER_HOOKS=rails bundle exec rspec ...
```
Or you can set the hooks parameter through the `Vernier` configuration:
```ruby
TestProf::Vernier.configure do |config|
config.hooks = :rails
end
```
## RubyProf
Easily integrate the power of [ruby-prof](https://github.com/ruby-prof/ruby-prof) into your test suite.
Make sure `ruby-prof` is installed:
```ruby
# Gemfile
group :development, :test do
gem "ruby-prof", ">= 1.4.0", require: false
end
```
### Profiling the whole test suite with RubyProf
**NOTE:** It's highly recommended to use [test sampling](../recipes/tests_sampling.md) to generate smaller profiling reports and avoid slow test runs (RubyProf has a signifact overhead).
You can activate the global profiling using the environment variable `TEST_RUBY_PROF`:
```sh
TEST_RUBY_PROF=1 bundle exec rake test
# or for RSpec
TEST_RUBY_PROF=1 bundle exec rspec ...
```
At the end of the test run, you will see the message from Test Prof including paths to generated reports:
```sh
[TEST PROF INFO] RubyProf report generated: tmp/test_prof/ruby-prof-report-flat-wall-total.txt
```
#### Skipping test suite boot
**NOTE:** RSpec only.
It could be usefule to exclude the application boot and tests load from the RubyProf report to analyze only tests being executed (so you don't have `Kernel#require` being one of the top slowest methods).
For that, specify the `TEST_RUBY_PROF_BOOT=false` (or "0", or "f") env variable:
```sh
$ TEST_RUBY_PROF=1 TEST_RUBY_PROF_BOOT=0 bundle exec rspec ...
[TEST PROF] RubyProf enabled for examples
...
```
### Profiling individual examples with RubyProf
TestProf provides a built-in shared context for RSpec to profile examples individually:
```ruby
it "is doing heavy stuff", :rprof do
# ...
end
```
**NOTE:** per-example profiling doesn't work when the global profiling is activated.
### RubyProf configuration
The most useful configuration option is `printer` – it allows you to specify a RubyProf [printer](https://github.com/ruby-prof/ruby-prof#printers).
You can specify a printer through environment variable `TEST_RUBY_PROF`:
```sh
TEST_RUBY_PROF=call_stack bundle exec rake test
```
Or in your code:
```ruby
TestProf::RubyProf.configure do |config|
config.printer = :call_stack
end
```
By default, we use `FlatPrinter`.
**NOTE:** to specify the printer for per-example profiles use `TEST_RUBY_PROF_PRINTER` env variable ('cause using `TEST_RUBY_PROF` activates the global profiling).
Also, you can specify RubyProf mode (`wall`, `cpu`, etc) through `TEST_RUBY_PROF_MODE` env variable.
See [ruby_prof.rb](https://github.com/test-prof/test-prof/tree/master/lib/test_prof/ruby_prof.rb) for all available configuration options and their usage.
It's useful to exclude some methods from the profile to focus only on the application code.
TestProf uses RubyProf [`exclude_common_methods!`](https://github.com/ruby-prof/ruby-prof/blob/e087b7d7ca11eecf1717d95a5c5fea1e36ea3136/lib/ruby-prof/profile/exclude_common_methods.rb) by default (disable it with `config.exclude_common_methods = false`).
We exclude some other common methods and RSpec specific internal methods by default.
To disable TestProf-defined exclusions set `config.test_prof_exclusions_enabled = false`.
You can specify custom exclusions through `config.custom_exclusions`, e.g.:
```ruby
TestProf::RubyProf.configure do |config|
config.custom_exclusions = {User => %i[save save!]}
end
```
[StackProf]: https://github.com/tmm1/stackprof
[Speedscope]: https://www.speedscope.app
[Vernier]: https://github.com/jhawthorn/vernier
|