Using GDB to Inspect a Running Ruby Process
This job shouldn't be taking this long!
That's not a great thing to have to say, is it? However, I bet you've said it before and may not have immediately know why.
With liberal use of
puts and maybe
pry, you can figure out what a problem might be next time you run it, but sometimes you need to figure out what that problem is right now.
As it turns out, and I know this is a shocker, Ruby processes are just regular processes. They can be debugged with
Having recently had the need to find out why a job of mine was running particularly slowly, I found out about this lovely tool the hard way: frantic googling. I found some very useful functions for gdb in a blog post by Rasmus on Ruby callstacks.
define redirect_stdout call rb_eval_string("$_old_stdout, $stdout = $stdout, File.open('/tmp/ruby-debug.' + Process.pid.to_s, 'a'); $stdout.sync = true") end define ruby_eval call(rb_p(rb_eval_string_protect($arg0,(int*)0))) end
How to use these:
- Start up
gdb /path/to/ruby PID, where
/path/to/rubyis the full path to the actual ruby binary and
PIDis the process ID of the ruby you want to check out.
- Paste those functions above into the gdb prompt (you might also want to store them in
redirect_stdout, which will put all the ruby output into a file called
PIDin this case if the process id of gdb – not terribly important, but a differentiator in case you do this a lot.
- Run commands via
object_idand things like that. You should be able to get local variables from wherever you broke into the program.
ruby_eval commands will output into the tempfile that
redirect_stdout created, so you'll need to
tail -f that file in a different console. Now, with that small headache over with, you can see exactly where your program is and if there is a stupid loop where you forgot to check a boundary condition, or what thing you're doing with a regular expression on where you should have just used