Skip to content

Commit

Permalink
docs: merge stack_prof/ruby_prof pages into a single ruby profilers page
Browse files Browse the repository at this point in the history
  • Loading branch information
palkan committed Nov 15, 2023
1 parent 37770c2 commit f6d4418
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 170 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## master (unreleased)

- StackProf uses JSON format by default. ([@palkan][])

- MemoryProf ia added. ([@Vankiru][])

## 1.2.3 (2023-09-11)
Expand Down
3 changes: 1 addition & 2 deletions docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
* [Playbook](/playbook.md)

* Profilers
* [RubyProf Integration](/profilers/ruby_prof.md)
* [StackProf Integration](/profilers/stack_prof.md)
* [Ruby profilers](/profilers/ruby_profilers.md)
* [Event Profiler](/profilers/event_prof.md)
* [Tag Profiler](/profilers/tag_prof.md)
* [Factory Doctor](/profilers/factory_doctor.md)
Expand Down
86 changes: 2 additions & 84 deletions docs/profilers/ruby_prof.md
Original file line number Diff line number Diff line change
@@ -1,84 +1,2 @@
# Profiling with RubyProf

Easily integrate the power of [ruby-prof](https://github.com/ruby-prof/ruby-prof) into your test suite.

## Instructions

Install `ruby-prof` gem (>= 1.4.0):

```ruby
# Gemfile
group :development, :test do
gem "ruby-prof", ">= 1.4.0", require: false
end
```

RubyProf profiler has two modes: `global` and `per-example`.

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 rspec ...
```

Or in your code:

```ruby
TestProf::RubyProf.run
```

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.

## 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.

### Methods Exclusion

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
```
<meta http-equiv="Refresh" content="0; url=https://test-prof.evilmartians.io/profilers/ruby_profilers" />
<p>Please follow <a href="https://test-prof.evilmartians.io/profilers/ruby_profilers">this link</a>.</p>
174 changes: 174 additions & 0 deletions docs/profilers/ruby_profilers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# 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)
- [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.

## 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
```

### 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
85 changes: 2 additions & 83 deletions docs/profilers/stack_prof.md
Original file line number Diff line number Diff line change
@@ -1,83 +1,2 @@
# Profiling with StackProf

[StackProf](https://github.com/tmm1/stackprof) is a sampling call-stack profiler for ruby.

## Instructions

Install `stackprof` gem (>= 0.2.9):

```ruby
# Gemfile
group :development, :test do
gem "stackprof", ">= 0.2.9", require: false
end
```

StackProf profiler has 2 modes: `global` and `per-example`.

You can activate global profiling using the environment variable `TEST_STACK_PROF`:

```sh
TEST_STACK_PROF=1 bundle exec rake test

# or for RSpec
TEST_STACK_PROF=1 rspec ...
```

Or in your code:

```ruby
TestProf::StackProf.run
```

TestProf 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 profiling is activated.

## Report formats

Stackprof provides a CLI tool to manipulate generated reports (e.g. convert to different formats).

By default, Test Prof shows you a command\* to generate an HTML report for analyzing flamegraphs, so you should run it yourself.

\* only if you're collecting _raw_ samples data, which is the default Test Prof behaviour.

Sometimes it's useful to have a JSON report (e.g. to use it with [speedscope](https://www.speedscope.app)), but `stackprof` only supports this only since version [0.2.13](https://github.com/tmm1/stackprof/blob/master/CHANGELOG.md#0213).

If you're using an older version of Stackprof, Test Prof can help in generating JSON reports from _raw_ dumps. For that, use `TEST_STACK_PROF_FORMAT=json` or configure the default format in your code:

```ruby
TestProf::StackProf.configure do |config|
config.format = "json"
end
```

## Profiling application boot

The application boot time could also makes testing slower. Try to profile your boot process with StackProf using the following command:

```sh
TEST_STACK_PROF=boot rspec ./spec/some_spec.rb
```

**NOTE:** we recommend to analyze the boot time using flame graphs, that's why raw data collection is always on in `boot` mode.

## 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.
<meta http-equiv="Refresh" content="0; url=https://test-prof.evilmartians.io/profilers/ruby_profilers" />
<p>Please follow <a href="https://test-prof.evilmartians.io/profilers/ruby_profilers">this link</a>.</p>
2 changes: 1 addition & 1 deletion lib/test_prof/stack_prof.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def initialize
if FORMATS.include?(ENV["TEST_STACK_PROF_FORMAT"])
ENV["TEST_STACK_PROF_FORMAT"]
else
"html"
"json"
end

sample_interval = ENV["TEST_STACK_PROF_INTERVAL"].to_i
Expand Down

0 comments on commit f6d4418

Please sign in to comment.