Just a few days ago, Kent Sibilev quietly released an amazing little plugin with far reaching consequences: ruby-debug. This is a marked improvement over Ruby’s own rdebug. The major difference is that he removed the major slow down that impacted rdebug usability with Ruby on Rails so it is blazing fast.
Instead of using the Kernel#set_trace_func API, which makes it possible to implement a debugger in Ruby, but has a negative effect on the speed on your program execution, he uses a native extension with a new hook using the Ruby C API
For each trace call Ruby interpreter creates a Binding object, even though it is not being used most of the time. ruby-debug library moves most of the functionality of debug.rb to a native extension, this way significantly improving the execution of your program.
This means that watchpoints and the standard tracing facility are not supported, but I’m gladly giving that up for the comfort and speed. And if you are wondering how the speed is, that is really the difference between utter frustration each time you debug something to bliss!
Previously I covered some options for Debugging in Rails. This is going to become my preferred option by far, and I’m willing to bet this will become yours too. Why? Because you can see the source code of where you are, you can execute step while watching some or your variables, you can inspect of your variables (this you could do with breakpointer), you can use conditional breakpoints, you can get a list of expressions to be displayed every time you step. The downside compared to breakpointer? You can’t do it remotely, which in most instances, as best as I can tell, is not going to be a problem at all.
Installation
Ruby-debug comes as a gem so to install, just run:
sudo gem install ruby-debug
Make sure you chose the proper windows or ruby version depending on your platform.
Using ruby-debug
To use in your rails application, assuming you want this to only be available in development mode (this is not such a great idea to leave in in production mode, just in case you forget to remove the breakpoints, and also for performance reasons as I’ll explain in a bit)
In environement.rb add the following:
SCRIPT_LINES__ = {} if ENV['RAILS_ENV'] == 'development'
This line is important if you want to be able to see your source code. SCRIPT_LINES__ is an obscure feature of the ruby interpreter. If it is defined, it will store all loaded ruby file in a hash, which debug-ruby will use to display where you are in your source code. The only problem is that it can have some impact on performance, and worst of all, use up quite a bit of memory, which is not so good in production (hence the “if ENV[‘RAILS_ENV’] == ‘development'”). SCRIPT_LINES__ needs to be initialized as early as possible so it can capture all loaded ruby files.
To add a breakpoint, you will need to use:
require 'ruby-debug'
...
def your_method
...
debugger if ENV['RAILS_ENV] == 'development'
...
end
Then start your app using webrick (it does not work with lighttpd and I have not investigated why just yet):
script/server webrick
When the code hits the breakpoint, you are in a console like mode (not unlike irb or script/console).
Ruby-debug commands
- b[reak]
list breakpoints
- b[reak] [file|class:]LINE|METHOD [if expr]
- b[reak] [class.]LINE|METHOD [if expr]
set breakpoint to some position, optionally if expr == true
- cat[ch]
show catchpoint
- cat[ch] EXCEPTION
set catchpoint to an exception
- disp[lay] EXPRESSION
add expression into display expression list
- undisp[lay][ nnn]
delete one particular or all display expressions if no expression number given
- del[ete][ nnn]
delete some or all breakpoints (get the number using “break”)
- c[ont]
run until program ends or hit breakpoint
- r[un]
alias for cont
- s[tep][ nnn]
step (into methods) one line or till line nnn
- n[ext][ nnn]
go over one line or till line nnn
- w[here]
displays stack
- f[rame]
alias for where
- l[ist][ (-|nn-mm)]
list program, – list backwards, nn-mm list given lines. No arguments keeps listing
- up[ nn]
move to higher frame
- down[ nn]
move to lower frame
- fin[ish]
return to outer frame
- q[uit]
exit from debugger
- v[ar] g[lobal]
show global variables
- v[ar] l[ocal]
show local variables
- v[ar] i[nstance] OBJECT
show instance variables of object
- v[ar] c[onst] OBJECT
show constants of object
- m[ethod] i[nstance] OBJECT
show methods of object
- m[ethod] CLASS|MODULE
show instance methods of class or module
- th[read] l[ist]
list all threads
- th[read] c[ur[rent]]
show current thread
- th[read] [sw[itch]] nnn
switch thread context to nnn
- th[read] stop nnn
stop thread nnn
- th[read] resume nnn
resume thread nnn
- p EXPRESSION
evaluate expression and print its value
- pp EXPRESSSION
evaluate expression and print its value
- h[elp]
print this help
- RETURN KEY
redo previous command. Convenient when using list, step, next, up, down,
- EVERYHTING ELSE
evaluate
Happy debugging!