Open-source project
tmm1/rbtrace avatar
tmm1/rbtrace

rbtrace: method calls in a live Ruby process, without restarting it

like strace, but for ruby code

1,726 stars73 forksRubyMIT

At a glance

What is it?
rbtrace attaches to a running Ruby MRI process and prints the method calls as they happen, in a firehose, a slow-call threshold, a GC view or a memory report. It is a C extension with a message channel to the traced process, and the README is candid about where it stops working.
Who is it for?
Adopt rbtrace when you need to see what a running Ruby MRI process is actually doing, without a code change, a restart or a profiler install, for example to find which calls are slow in production or to watch garbage collection behaviour. Do not adopt it as a general profiler, since it is a tracer of method calls and arguments rather than a statistical sampling profiler, and do not expect it to work on non-MRI Ruby implementations, which the README does not cover.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 17 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

It attaches to a running process instead of asking you to change code

rbtrace shows method calls happening inside another Ruby process in real time. That is the whole idea, and it is what separates it from the alternatives. You do not add a require, you do not wrap a method, and you do not restart the process with a different command line. You point it at a process id and it attaches.

The tool is described as working on Ruby 1.8 through 2.2 and beyond, on Linux or macOS, and as designed to have minimal overhead so that it "should be safe to run in production". The supported Ruby list is more recent: the README says rbtrace supports all stable versions of Ruby MRI, as of 2018-01-23 being Ruby 2.2 and later.

Installation is a gem:

bash
gem install rbtrace
rbtrace --help

The repository layout explains how that is possible. There is an `ext/` directory for the native extension, a `lib/` directory for the Ruby side, a `bin/` directory for the executable, a `tracers/` directory holding the predefined tracers, and a `server.rb` file at the root. The `server.rb` is the Ruby end of the channel: the README's detailed example shows a script that starts with `require 'rbtrace'`, and the todo list at the end of the README includes an item to "correct irb implementation so it establishes a dedicated channel". So the traced process needs a Ruby-side agent, and that agent is what `require 'rbtrace'` starts.

The last push was on 2026-10-06, and the repository has no GitHub releases, so the gem is the distribution channel rather than a tagged artefact.

Six tracer modes, and only three of them combine

The tracer modes are the tool's vocabulary, and the README lists them as flags on the same `-p` process id.

`--firehose` shows everything. `--slow=<N>` shows any method calls that take longer than N milliseconds. `--methods` traces calls to specific methods, given as names such as `Kernel#sleep` and `Proc#call`. `--gc` traces garbage collections. `--memory` produces a basic memory report regarding the process, including `GC.stat` and `ObjectSpace` stats. `--backtraces` returns backtraces for all active threads in a process.

The notes section is short and worth reading twice. `--firehose` is not reliable on macOS. And `--slow`, `--gc` and `--methods` can be combined, so a slow-call filter can run alongside a GC view and a method filter, while `--firehose` and `--memory` and `--backtraces` stand apart.

The distinction between the modes is a real difference in kind. `--slow` answers "what is taking time", `--gc` answers "is the collector the problem", `--memory` answers "what is the heap doing", and `--backtraces` answers "where is everything stuck right now". The last of those is the one that answers a question no log line can: in a live process with a stuck request, the other modes tell you what happened, and the backtrace mode tells you what is happening.

The same flags are available with the short forms used in the examples, where `-m` stands for the method filter and `-c` for the predefined tracer categories.

You can print argument values, and the source line where the call came from

The tracer does more than list method names. Given an expression in the method filter, it prints the arguments the method received, and the example in the README shows a call filter with three expressions:

bash
rbtrace -p 87854 -m "String#gsub(self, @test)" "String#*(self, __source__)" "String#multiply_vowels(self, self.length, num)"

The output shows the receiver and the evaluated arguments inline, and the `__source__` placeholder yields the file and line the call came from, in the form `server.rb:6`.

That is the feature that makes rbtrace useful for argument bugs rather than just for performance work. A method that is being called with the wrong value is invisible in a stack trace and invisible in a profiler's aggregate, but obvious when every call prints its arguments. The receiver is printed as `self`, instance variables as `@test`, and ordinary expressions such as `self.length` are evaluated at the point of the call.

The cost is output volume, which is why the method filter takes several names and why the example narrows the trace to a handful of methods on a specific class. The other examples show the same idea at different breadths: tracing a single method by name, tracing every method in a class or module by prefix such as `Kernel#`, and the firehose that shows the whole call tree with indentation.

Predefined tracers for the libraries that usually matter

The `tracers/` directory at the repository root holds a set of predefined tracers for popular Ruby libraries and functions, and they are selected with the `-c` flag. The documented example traces calls to ActiveRecord adapters and any I/O functions:

bash
rbtrace -p <PID> -c activerecord io

The value of a predefined tracer is that it encodes the set of methods worth looking at for a given library. Tracing every method of an ActiveRecord adapter means tracing SQL execution, connection handling, type casting and instrumentation, and the output is unreadable. A predefined tracer narrows it to the calls that answer a question you are actually asking.

The I/O tracer is the other obvious one. When a service is slow and nobody can say where, the first question is usually which file, socket or pipe it is waiting on, and an I/O tracer answers that directly rather than by inference from a slow call.

The README links to the `tracers` directory in the repository for the full set, so the list of supported categories is browsable. It does not enumerate the categories in the text, which means the practical approach is to look at the directory before assuming a library is covered.

The channel underneath is a native extension with a hard limit on macOS

Two details in the todo list at the end of the README explain the shape of the implementation. One item is to "optimize local variable lookup to avoid instance_eval", which is a hint that the Ruby side evaluates expressions in the context of the traced object to read the argument values. The other is to "investigate mach_msg on osx since msgget(2) has hard kernel limits".

That second item is the most informative line in the README. The tracer and the traced process exchange events over a message queue, and on macOS the underlying system call has a hard kernel limit, so the channel can run out of room. It is listed as an open investigation rather than a solved problem, and it is consistent with the note that `--firehose` is not reliable on macOS: a firehose produces the largest volume of events, which is exactly the case where a finite queue matters.

The design goal stated at the top is minimal overhead and safety in production, and the native `ext/` directory is how that goal is met. A tracer implemented in Ruby alone could not observe calls in another process, so the extension is not an optimisation, it is the mechanism.

For a Ruby service that runs on Linux, this caveat mostly disappears. For a team on macOS, it is the difference between using `--firehose` casually and using `--slow` and `--methods` deliberately.

The todo list is the roadmap, and it is short

The README ends with a todo list, and reading it tells you where the author sees the gaps.

The first item is to correct the irb implementation so it establishes a dedicated channel, which suggests that the interactive mode currently shares a channel with the tracer and that a dedicated one would be cleaner. The second is to add triggers to start tracing slow methods only inside another method, so a slow-call filter can be scoped to a particular code path rather than applied globally. The third is to add watch expressions to fire tracers only when an expression is true, which is the conditional filter that the argument-printing syntax makes technically possible.

The fourth is to add special expressions for method arguments, `_arg0_` and `_arguments_`, so you can print the first argument or the whole argument list without naming the parameter. The fifth is the local variable lookup optimisation mentioned above.

None of these are implemented. That matters for the evaluation, because the tool is at a stage where the interesting ideas are partly in the backlog. The scope-limited slow tracing and the watch expressions are the two that would change how the tool is used day to day, and both are still to do.

The repository also carries a `Rakefile`, a `Gemfile`, an `rbtrace.gemspec`, a `test.sh` script and a `test/` directory, and a `CHANGELOG` file, so there is a development setup and a history, even if there are no tagged releases.

Where rbtrace is the wrong tool

The first wrong case is a non-MRI Ruby. The supported list is explicitly Ruby MRI, and the mechanism depends on the internals of that interpreter, so JRuby or TruffleRuby are out of scope, and the README does not mention them.

The second is when you need a statistical profile rather than a trace. rbtrace shows you method calls as they happen, which is superb for a slow call, a wrong argument or a stuck thread, and useless for answering "where does the 2% of requests spend their time". A sampling profiler gives you an aggregate picture over many requests; a tracer gives you every event for the ones you watch. For a live incident, the tracer is the better tool. For a performance budget, it is the wrong one.

The third is a firehose on macOS, which the README states is not reliable. If you need to see everything happening in a process, do it on Linux.

The fourth is a process you cannot attach to. The traced process needs the Ruby side loaded, and the README's own example shows that as a `require 'rbtrace'` in the running script. For a process that was started without it, the tool has nothing to connect to, and the README does not describe an attach-time injection method.

Editorial conclusion

Adopt rbtrace when you need to see what a running Ruby MRI process is actually doing, without a code change, a restart or a profiler install, for example to find which calls are slow in production or to watch garbage collection behaviour. Do not adopt it as a general profiler, since it is a tracer of method calls and arguments rather than a statistical sampling profiler, and do not expect it to work on non-MRI Ruby implementations, which the README does not cover. Before you attach, note that `--firehose` is described as unreliable on macOS, and check the `tracers/` directory for a predefined tracer for the library you are investigating rather than tracing every method by hand.

Frequently asked questions

How do I install rbtrace and trace a running Ruby process?

Install the gem with `gem install rbtrace`, then attach to a process with `rbtrace -p <PID>` and one of the tracer modes, for example `--firehose`, `--slow=<N>`, `--methods`, `--gc`, `--memory` or `--backtraces`.

Can rbtrace print the arguments a method was called with?

Yes. The method filter accepts expressions such as `String#gsub(self, @test)`, and the output shows the receiver and the evaluated argument values. The `__source__` placeholder reports the file and line the call came from.

Which tracer modes can be combined?

The README states that `--slow`, `--gc` and `--methods` can be combined, and separately notes that `--firehose` is not reliable on macOS.

Does rbtrace work on JRuby or other Ruby implementations?

The supported list in the README covers Ruby MRI, as of 2018-01-23 being version 2.2 and later, and the README does not mention other Ruby implementations. The tool runs on Linux and macOS.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. tmm1/rbtrace on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tmm1-rbtrace.svg)](https://hysenlabs.com/projects/tmm1-rbtrace)

Was this useful?