Thursday, August 11, 2011

Inspect Ruby Process From gdb

If you have been developing in Ruby and Ruby on Rails for any length of time, it is very likely that you have encountered a 'Segmentation fault' from Ruby without any information about where the error is coming from.

Normally, a gem written in C extension is to blame. If pure Ruby code meets an error, a nice stacktrace with a line number will be there to point you in the right direction, but not for code in C extension. You won't find more information about the error unless you fire up gdb.

Assuming the code that crashes is bug.rb, instead of doing 'ruby bug.rb', just do:
$gdb ruby
(gdb) run bug.rb

Once the the crash is reproduced, type:
(gdb) bt

'bt' means 'backtrace'. A C callstack will show up. And you will find some valuable information there. If 'bt' does not give you function name and line# you want, it normally means it can not locate the source code, and you can use the 'dir' command to help gdb locate the source code (Ruby's source code is freely available, just download the one that matches the binary):
(gdb) dir /root/downloaded/ruby-1.8.6-p111

If the program that crashed is a rake task, just do some thing similar. Rake itself is a Ruby script:
$gdb ruby
(gdb) run /usr/bin/rake -T

If you have a daemon written in Ruby, sometimes you might see that it hangs (does nothing) or uses too many resources (100% cpu usage). To inspect a live process with process id 12345, do this:
$gdb ruby
(gdb) attach 12345
(gdb) bt

For Rails applications, a bug may only happen in the production environment. Before you enter 'run', use this to setup the environment:
(gdb) set environment RAILS_ENV = production

Here are some tips to get more from a C stacktrace.
You can call a C function with a 'call' command under gdb shell -- yep, that makes C feel like an interpreted language. For example, you have a stack trace entry like this:
rb_call0 (klass=47110398570320, recv=47110462241640, id=22977,
oid=, argc=0, argv=0x7fffee0453c8, body=0x2ad8be3946c8,
flags=) at eval.c:5998
You can find out what 'klass' is by using its memory address:
(gdb) call rb_class2name(47110398570320)
Or you can look up a Ruby function's name:
(gdb) call rb_id2name(22977)

This GDB QUICK REFERENCE and Extending Ruby chapter of 'Programming Ruby' book are great references if you want to know more!