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.
Posted by Tobias Kraze to makandra dev (2012-04-12 12:07)