ruby-debug basics screencast

Posted on May 10, 2007

Brian Donovan shows the basics of using ruby-debug. This is very nice addition to my already outdated tutorial. He also announced that his next screencast will cover debugging of a Rails application.

One the same note, the next version of Rails comes with the native ruby-debug support. The relevant changeset is here.

ruby-debug version 0.9.1 has been released!

Posted on April 03, 2007

Installation

As usual:

    $ sudo gem in ruby-debug

Changes

This is mainly a maintenance release, which includes several important bugfixes related to frame calculations.

In particular,

ruby-debug version 0.8 has been released!

Posted on March 15, 2007

This is mostly a maintenance release. The major change is that ruby-debug has been split in two parts:

  1. ruby-debug-base gem contains the debugger core API written in C. Use this gem if you want to create your own interface to the debugger and don't want to depend on changes to the ruby-debug CLI. The API is considered somewhat stable.

  2. ruby-debug gem depends on ruby-debug-base and provides CLI interface.

ruby-debug 0.7 released

Posted on February 02, 2007

I'm happy to announce that ruby-debug 0.7 is available now.

This is mostly a bugfix release. It also has many internal changes that greatly improve the debugger performance. It is so much faster now, that sometime I don't even notice that I'm using it when I start mongrel with rdebug script. The list of changes includes:

  • Frame class has been removed and instead a preallocated block of memory is used to keep frames information. This change has decreased the number of object being created.
  • Another major change involves the fact that the debugger by default doesn't create Binding object for each frame. This is a very expensive operation and it's been the major source of performance problems. Instead, ruby-debug keeps references to the live Ruby's data structures that store information about the local context (ruby_scope). It's still possible to restore the old behavior by using --keep-frame-binding option, or Debugger.keep_frame_binding = true.
  • New set command has been introduced. You can use help set command to see what options are available.

Enjoy, and please report any problem you find!

ruby-debug 0.6.1 Released

Posted on January 27, 2007

ruby-debug 0.6.1 has been released!

    $ sudo gem install ruby-debug

ruby-debug is a C extension that implements the fast Ruby debugger.

Changes

  • Significant performance improvements.

  • Bug fixes.

Post-mortem debugging

Posted on December 20, 2006

ruby-debug 0.5.1 has been released! The installation as usual:

    $ sudo gem install ruby-debug

Below is the list of changes:

Post-mortem debugging

First of all, what the heck is post-mortem debugging? Let's say you run a script and instead of the expected result you get an exception trace. Wouldn't it be great if we can roll back to the point where this exception is raised and explore the state of your program (possibly, by moving up and down the frame stack)?

Note that it is different than setting a catch point. By setting a catch point you activate the debugger when an exception (of a specific class) is about to be raised and it doesn't matter whether it's going to be handled later in the code or not. In the post-mortem case you know that an exception's been raised and not handled as expected.

Now I'm going to demonstrate several ways of using this feature, starting from the simplest:

Debugging Rails application

Posted on September 14, 2006

In this article I am going to describe several ways you can debug your Rails application with ruby-debug starting from simplest and proceeding with more sophisticated setup.

Simplest of all

The simplest and most obvious way is to start your application with rdebug script:

    $ rdebug ./script/server webrick

This method doesn't work with lighttpd since the latter starts several child Rails processes.

When you execute this command you end up with debugger prompt where you can set up your breakpoints.

Debugging without rdebug script

First you need to activate the debugger in your development.rb file:

    ./config/environments/development.rb:

    ...
    require 'ruby-debug'
    Debugger.start

Now you can use Kernel#debugger method to activate debugger:

    def my_action
      debugger
      ...
    end

Remote debugging.

If for some reason you want to connect remotely to debugger, you can start it with remote debugging enabled:

    $ rdebug -s ./script/server webrick

and then connect to it with

    $ rdebug -c
    Connected.
    /usr/local/lib/ruby/1.8/webrick/server.rb:91: if svrs = IO.select(@listeners, nil, nil, 2.0)
    (rdb:1)

Once again, at this point you can add breakpoints and proceed.

Adding breakpoints directly in your code

What if you don't want to setup breakpoints all the time from the debugger's command prompt and you want to use Kernel#debugger method instead. In this case when you start a debugger, you should notify it not stop when a remote client is connected with -n option:

    $ rdebug -sn ./script/server webrick

Remote debugging without rdebug script

As always activate your debugger in development.rb file:

    ./config/environments/development.rb:

    ...
    require 'ruby-debug'
    Debugger.start_remote

Here you can control it with two options:

    ./config/environments/development.rb:

    ...
    require 'ruby-debug'
    Debugger.wait_connection = true
    Debugger.stop_on_connect = true
    Debugger.start_remote
  • Debugger.wait_connection options makes debugger wait until you connect to it remotely.

  • Debugger.stop_on_connect drop you to the debugger prompt as soon as you connect to it.

The method I use most of the time

All described so far methods suffer from one thing: the debugger is always activated. It can slow down your application significantly. What if I want to activate it only in a particular place in my code? As always there are two ways to do that. But first you should only require debugger in development.rb file without starting it:

    ./config/environments/development.rb:

    ...
    require 'ruby-debug'
  • Debugger#start takes a block:

    def my_action
      ...
      # at this point the debugger is still disabled
      Debugger.start do
        # here debugger is enabled
        # below goes the code you want to debug
        ...
      end
      # at this point the debugger is disabled
      ...
    end
    
  • Use Module#debug_method (available since version 0.4.2):

    class MyController < ApplicationController
      def my_action
        ...
      end
      debug_method :my_action
    end
    

    Now whenever you reach my_action method, the debugger is activated.

Control debugger from TextMate

Posted on August 27, 2006

Wouldn't it be great if I can set breakpoints right from within TextMate? I've been thinking about this myself for sometime.

And now I'd like to introduce a new Ruby Debug Bundle.

Note that in order to use it you must have the latest version (0.4.1 or higher) of ruby-debug installed.

How to use it?

Let's say you want to debug your Rails application.

  1. Start your application with the remote debugging enabled:

    $ rdebug -sn ./script/server webrick
    
  2. Connect to the debugger (in new terminal):

    $ rdebug -c
    Connected.
    
  3. In TextMate go to the line where you want to set a breakpoint and press ⇧⌘B and select Set Breakpoint at Current Line.

  4. Open your browser and start using your application until you reach the breakpoint.

That's it.

Currently, these commands are available:

  1. Set Breakpoint at Current Line
  2. Delete All Breakpoints.
  3. Show Breakpoints - show all breakpoints as a tooltip.
  4. Interrupt - interrupt the last debugged thread or, if there is no one, the main thread.
  5. Quit - quit application.

Breakpoint extension breakage

Posted on August 25, 2006

Many Rails users enjoyed this little gem developed by Florian Groß. But as Mauricio Fernandez noted in his blog, this extension is broken with the brand new Ruby 1.8.5. In order to make its magic possible, breakpoint library relied on the implementation of Binding.of_caller, which in turn relied on the bug in Ruby's implementation of trace calls. With new release, this bug has been patched.

Mauricio has started working of fixing this problem and proposed a new method binding_n(n), which would return a binding for any frame in the call stack. But you don't have to wait for his work to complete. ruby-debug has this functionality already implemented for you.

Consider this small snippet:

    require "rubygems"
    require 'ruby-debug'

    module Kernel
      def binding_n(n = 0)
        frame = Debugger.current_context.frames[n+1]
        raise "Unknown frame #{n}" unless frame
        frame.binding 
      end
    end

    def test
      puts eval("var", binding_n(1))
    end

    Debugger.start do
      var = 'Hello'
      test
    end

And most likely, I will add this method to the next version of ruby-debug. It can be handy sometimes.

ruby-debug 0.4

Posted on August 25, 2006

New release 0.4 is uploaded to gem server. Thanks to Pascal for providing a valuable feedback and suggestions for this release.

What's new?

  • sa[ve] FILE command saves current breakpoints and checkpoint to a script file.

  • sc[ript] FILE command runs a script file. Note that script file can contain only control commands: add/remove breakpoints, interrupt program or thread, etc.

  • l[ist] on/off command. I don't know about you, but when I step through my application I immediately need to use list command to see where I am in the source code. Now if you use list on, ruby-debug will start doing this for you. To turn it off, use list off.

  • e[val] on/off command. When it's on*, ruby-debug will evaluate your input if it doesn't recognize it as one of the commands. To turn it off, use *eval off.

  • tm[ate] n command now accepts a frame number.

Note that in my last announcement I forgot to mention that if you don't want to use rdebug script to start debugger and instead prefer to require 'ruby-debug', now you must activate debugger explicitly by running Debugger.start or Debugger.start_remote method. Also, if you want to debug just a small part of your application, you can wrap it in a block and pass it to Debugger.start method. When start method finishes, it disactivates debugger. For example:

    ...
    Debugger.start do
      # code you want to debug goes here
      ...
    end

Enjoy.

ruby-debug 0.3

Posted on August 15, 2006

OK, here's a new version of ruby-debug. Most changes are related to the remote debugging and improvements targeted for a possible GUI integration:

  • Wait for a client connection. With this option on, the debugger will wait for a connection, before returning control to the script. You can enable it with -w option of rdebug script or by calling Debugger.wait_connection = true.

  • Stop on connect. With this option on, the debugger will stop when a remote client establishes a connection. rdebug script has this parameter enabled by default, but you can disable it with -n option. In your own script you can use Debugger.stop_on_connect = true to activate it.

  • Controlling thread. When activating a remote debugger, a control thread is activated as well. It listens on the socket (port 8990, by default) and accepts a number of commands that you can use to control a debugger, such as add/delete breakpoints, interrupt an application, etc. This is quite helpful for a GUI frontend, where you can set breakpoints at any time while your application is running.

There are also a couple of additions to the debugger commands:

  • l[ist] = - displays the current exception point.

  • f[rame] n - switches to nth frame. You can see the list of frames and their numbers with frame or where command.

  • tm[ate] - if you are running Mac OSX and addicted to TextMate, this command opens the current file using this editor.

That's all for now. Enjoy.

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.

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.