I worked with GNU C/C++ for a long time before I met Ruby. The two languages are very different; they have their own strengths and weaknesses in different application scenarios. But there is one nice thing that they have in common: they both have great debuggers for their developers who are hitting their heads on their keyboards trying to find out what has gone wrong with their applications.
My associate, Yong Zhi, wrote a great blog post on how to debug a Ruby application, a standalone script, or a full-stacked Rails application with GDB, GNU Project Debugger. Within the Ruby community there is always more than one option you can choose from. As many of you know, ruby-debug provides almost the same directives with GDB, so, for the Ruby developers who have used GDB before, there will not be a steep learning curve. What I want to share with you today is how I make my life easier with help from ruby-debug.
The following steps are used to get ruby-debug working with a Rails 3 application.
Step 1: Install ruby-debug
Open Gemfile, add the following lines to it:
group :development do gem 'ruby-debug' end
and run bundle install
to install the gem.
Step 2: Start the server for development
rails server --debugger
Step 3: Find the bug
For the line you where you want to setup a breakpoint, just place a ‘debugger’, whether it’s in a view, a controller or a model.
# in a view file new.html.erb <%= content_for_the_main %> <% debugger %> <%= content_for_the_side_column %> # in a controller file, posts_controller.rb class PostsController < ApplicationController ... def new debugger post = current_user.posts.new end ... end # in a model file, post.rb class Post < ActiveRecord::Base ... def self.funny_ones debugger self.where(:funny => false) end ... end
Send another request after setting up the breakpoints; the server will stop at the first breakpoint it runs into, waiting for the further instructions:
(rdb:1)
With the prompt, you can get a list of directives with “help”, or “help backtrace” for the usage of specific directive. Usually, I’ll use ‘l’, short for ‘list’, to take a look at where the application stops:
(rdb:1) l 22 def new 23 debugger => 24 post = current_user.posts.new(params[:post])
After that, check out the value of the variable with ‘p’, short for ‘print’,
(rdb:1) p params.inspect
After that, you can continue to the next line with ‘n’, short for ‘next’,
(rdb:1) n
Or, you can use ‘c’, short for ‘continue’, to get to the next breakpoint. If there are not any breakpoints left, the reponse will be sent back to the client and the request-response loop is over.
(rdb:1) c
If you want to know how you got where you are, just call ‘bt’, short for ‘backtrace’,
(rdb:1) bt --> #0 TabsController.edit at line /home/hao/Sandbox/demo/app/controllers/posts_controller.rb:24 #1 Kernel.send_action at line /home/hao/.rvm/gems/ree-1.8.7-2010.02@demo/gems/actionpack-3.0.0/lib/action_controller/metal/implicit_render.rb:4 #2 ActionController::ImplicitRender.send_action at line /home/hao/.rvm/gems/ree-1.8.7-2010.02@demo/gems/actionpack-3.0.0/lib/action_controller/metal/implicit_render.rb:4 #3 AbstractController::Base.process_action(method_name#String) at line /home/hao/.rvm/gems/ree-1.8.7-2010.02@demo/gems/actionpack-3.0.0/lib/abstract_controller/base.rb:150
If you want to step into a function call you may have interest in, you can call ‘s’, short for ‘step’,
(rdb:1) s
To step out of the function call you just stepped into, use ‘fin’, short for ‘finish’,
(rdb:1) fin
To quit the debugger, type ‘q’, short for ‘quit’,
(rdb:1) q
Those are most of the ruby-debug directives that I find myself using daily. There’s one more feature of ruby-debug that can bring great help for the debugging: irb session.
(rdb:1) irb
You can open as many irb sessions as you want, you can change the value of the variable to try it out.
ree-1.8.7-2010.02 > params[:post] = {:title => "Hello, world!", :body => "Hello, world, again!"} ree-1.8.7-2010.02 > exit (rdb:1) c
There are lots of other great features in ruby-debug I’m not covering today. Here are some resources to get you started with ruby-debug very quickly: