Current State of Ruby Debuggers

Posted on July 21, 2006

Scott Bronson has published a very cool article about the current offerings when it comes to debugging Ruby applications. First off, I appreciate the kind words regarding ruby-debug extension. Second off, he made a very interesting observation:

    I hardly ever need a debugger when developing new Ruby on Rails applications.  
    However, when trying to understand an existing codebase, I find that few things 
    help more in understanding exactly what is going on than stepping through 
    the code using a debugger.

I fully subscribe to this idea. I've been developing with Ruby since 2002 and I hardly ever needed a debugger. But there were a couple of times when I really wanted to have a debugger. It was when I ran into a bug in an existing library and I didn't have an intimate knowledge of its internals (no pun intended). It's just much easier to step through the code and see how things are meshed together.

Also, I'd like to make some comments on the current debugger offerings:

  • There is an official debug.rb library bundled with the standard Ruby distribution. AFAIK, all other debuggers, except maybe Arachno Ruby's one, are based on this small and nifty library, including my ruby-debug. The major drawback of it is the speed of the execution.

  • Debugger from ZenHack tries to fix this problem by re-implementing trace function in C using RubyInline extension. Unfortunately, it is still quite slow to my taste.

  • Debugger from Arachno Ruby IDE is very nice, but the last time I checked it used a patched version of Ruby interpreter. Which means that you have to ask for a new patch with each subsequent Ruby release. (But don't quote me on that though :-)

  • There is another GUI debugger called MrGuid which uses a slightly modified version of debug.rb.

Changes in version ruby-debug 0.2

Posted on July 16, 2006

Remote Debugging

I've just released a new version of ruby-debug which has a new feature: remote debugging. In order to use it you have to pass -s parameter to rdebug script:

    $ rdebug -s <script.rb>

This option makes the debugger start listening for an incoming TCP connection on port 8989, by default. You can change the default port and host name with -p and -h option respectively.

Now when the debugger is started this way, you can connect to it using this command:

    $ rdebug -c

Also since this feature is implemented using plain sockets, you can connect to the remote debugger using a plain telnet client:

    $ telnet localhost 8989
    Trying ::1...
    Connected to localhost.
    Escape character is '^]'.
    script/../config/../app/controllers/shop_controller.rb:28: product_id = params['id']
    PROMPT (rdb:3) 
    list
    [23, 32] in script/../config/../app/controllers/shop_controller.rb
       23      end
       24    end
       25  
       26    def product
       27      debugger
    => 28      product_id = params['id']
       29      unless product_id
       30        redirect_to :action => 'index'
       31        return
       32      end
    PROMPT (rdb:3) 
    cont

API Changes

One more thing. There are two important changes have been made.

  • First off, starting from this version when you require 'ruby-debug', it doesn't activate the debugger by default. You have to explicitly activate it by calling Debugger#start method. Also if you want to activate remote debugging, you should start the listener server by calling Debugger#start_server(host, port) method.

    require 'ruby-debug'
    Debugger.start_server
    Debugger.start
    
  • Second off, it used to be possible at the debugger prompt to type anything and the debugger evaluates the expression, now you should use eval or p command.

Win32 gem

Thanks to Max Muermann, win32 version of ruby-debug 0.1.5 is available for download. The ruby-debug 0.2 version is coming too.

Update: win32 version of ruby-debug 0.2 is now available.

Asynchronous Email Delivery

Posted on July 13, 2006

It's a quite common for an action to finish by sending a confirmation email to the client. Usually you don't want to put this logic directly in your model. Models deal with a business rules and shouldn't care about emails or any other infrastructure nonsense (unless of cause this is the business domain of your application). In Rails you define your business logic in terms of ActiveRecord::Base and the delivery of emails in terms of ActionMailer::Base. In order to decouple these two, Rails provides a mechanism of ActiveRecord observers. These glue objects listen for changes in the model and react accordingly (usually using after_save method). If we need to perform a complex operation that involves updating several tables, it is better to wrap it with a database transaction. What I didn't pay attention to is that observer's after_save method is also a part of the transaction, meaning the transaction will remain open until this method finish.

This lead to a potential problem. What happens when your email server is under heavy load or you have some kind of DNS problem and the address resolution takes unusual amount of time? Your transactions will stay open for a long time and some of them will eventually time out.

Tutorial on ruby-debug

Posted on July 11, 2006

Preface

Overcomplicated specifications lead to overcomplicated implementations. Lately I've been fixing issues with ActionWebService framework - a soon to be removed part of Ruby on Rails. I have to use SOAP in the web application I'm developing. My client needs to keep his product inventory in a good shape and he requested to implement some of the inventory management functionality using handheld devices. There is a .Net environment available for this kind of devices and it works very well. So I desperately needed a functional web service implementation for Rails. That why I volunteered to fix AWS. And that's when I found out that ruby-breakpoint just doesn't cut it. Don't get me wrong, soap4r is a fine piece of software, but SOAP is difficult and obscure, which leads to complicated libraries that implement it. The situation is even worse when such libraries have almost no documentation whatsoever.

So instead of just stopping at some point in your program to examine the environment (the facility offered by ruby-breakpoint library), ruby-debug extension offers the full-fledged debugger for Ruby. The main difference between ruby-debug and the standard debug.rb library is the speed of the execution. Major problem with debug.rb is that it uses Kernel#set_trace_func method, which requires creation of Binding object for each hook invocation. It is fine for small scripts, but for the real world applications like Rails ones, debug.rb is almost impossible to use. You just sit and watch how Ruby interpreter creates enormous amount of Binding objects, just in order to destroy them with the immediate garbage collection cycle. It also explains that ruby-debug doesn't support watchpoints for the same reason.

Faster debugger for Ruby

Posted on July 07, 2006

I've been watching a thread on ruby-talk about the possibility to have a faster debugger for Ruby.

Now when Ruby 1.8.4 has a better C based API for tracing code execution, it is possible to significantly speed up debug.rb. I've been playing with this idea for last two days and came up with ruby-debug extension:

Follow these easy steps:

  • Download extension ruby-debug-0.1.gem.
  • Install it with sudo gem install ruby-debug-0.1.gem
  • Use either

        $ rdebug _your-script_
    

    Or for your Rails application:

    1. Add to your config/environments/development.rb

       require 'ruby-debug'
      
    2. Use Kernel#debugger method to interrupt your application and invoke debugger

       def myaction
        ...
        debugger
        ...
       end
      

Enjoy.

Update. I've created a project on rubyforge.org for this extension and uploaded a bugfix version 0.1.2. Now you don't have to download the gem file from this site. Just use

    $ gem install ruby-debug

command to install it.