Read more

Inspecting a live Ruby process

Tobias Kraze
April 12, 2012Software engineer at makandra GmbH

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
Illustration web development

Do you need DevOps-experts?

Your development team has a full backlog? No time for infrastructure architecture? Our DevOps team is ready to support you!

  • We build reliable cloud solutions with Infrastructure as code
  • We are experts in security, Linux and databases
  • We support your dev team to perform
Read more Show archive.org snapshot

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.

Posted by Tobias Kraze to makandra dev (2012-04-12 14:07)