Inspecting a live Ruby process

Updated . Posted . Visible to the public.

How to get a backtrace from a running Ruby process:

Ruby 2.6

# First, find out the PID of your Ruby process (e.g. passenger-status)
$ sudo gdb -p PID
(gdb) call rb_eval_string("$stderr.reopen('/tmp/ruby-debug.' + Process.pid.to_s); $stderr.sync = true") # redirects stderr
(gdb) call rb_backtrace() # prints current backtrace to /tmp/ruby-debug.xxx

Stop the process afterwards, since stderr is now borked.

It is possible you have to call rb_backtrace() multiple times to get the full stacktrace.

Previous method on Ruby 2.4+

TL;DR to get the backtrace of a live process (sudo required):

# First, find out the PID of your Ruby process (e.g. passenger-status)
$ sudo gdb -p PID
(gdb) generate-core-file                  # generate a core.PID file for later inspection
(gdb) t a a bt                            # thread apply all backtrace
(gdb) call (void) close(1)                # close the existing file descriptors for stdout
(gdb) call (void) close(2)                # close the existing file descriptors for stderr
(gdb) shell tty                           # we need to figure out the device name for the current TTY:
/dev/pts/X                                # X is an integer, adjust for your shell
(gdb) call (int) open("/dev/pts/X", 2, 0) # attach stdout to current TTY
(gdb) call (int) open("/dev/pts/X", 2, 0) # attach stderr to current TTY
call (void) rb_backtrace()                # print the Ruby backtrace to the current shell
(gdb) detach                              # detach from the Ruby process (PID)
(gdb) quit                                # exit gdb

Important notes:

  • You are irreversibly redirecting STDOUT/STDERR of a running process. Restart the ruby process after you are done!
  • While the above worked like a charm locally, we were not able to attach STDOUT/STDERR to the current TTY of a remote passenger worker. Please update the card accordingly if you solve that issue.
  • If your server is running passenger enterprise, rather use that tool to inspect your process.

You can use the core-file for an inspection that is not attached to the live process:

$ gdb ruby core.PID

Previous method on Ruby < 2.4

Ruby 1.8.7

# First, find out the PID of your Ruby process (e.g. passenger-status)
$ sudo gdb -p PID
(gdb) call rb_eval_string("$_old_stdout, $stdout = $stdout, File.open('/tmp/ruby-debug.' + Process.pid.to_s, 'a'); $stdout.sync = true") # redirects stdout
(gdb) call rb_backtrace() # prints current backtrace to /tmp/ruby-debug.xxx

Stop the process afterwards, since stdout is now borked.

It is possible you have to call rb_backtrace() multiple times to get the full stacktrace.

Tobias Kraze
Last edit
Henning Koch
Keywords
debug, gdb, debugging
License
Source code in this card is licensed under the MIT License.
Posted by Tobias Kraze to makandra dev (2012-04-12 12:07)