Using GDB to Inspect a Running Ruby Process

Jon Yurek

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 gdb.

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:

  1. Start up gdb by running gdb /path/to/ruby PID, where /path/to/ruby is the full path to the actual ruby binary and PID is the process ID of the ruby you want to check out.
  2. Paste those functions above into the gdb prompt (you might also want to store them in ~/.gdbinit for later).
  3. Run redirect_stdout, which will put all the ruby output into a file called /tmp/ruby-debug.PID where PID in this case if the process id of gdb – not terribly important, but a differentiator in case you do this a lot.
  4. Run commands via ruby_eval('Kernel.caller') and object_id and things like that. You should be able to get local variables from wherever you broke into the program.

These 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 String#index.