[Top] [Contents] [Index] [ ? ]

Debugging with ruby-debug

This file describes ruby-debug, the Ruby Debugger, version 0.10.5

This is the 0.10.4 Edition, 21 February 2011


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1. Summary of ruby-debug

The purpose of a debugger such as ruby-debug is to allow you to see what is going on “inside” a Ruby program while it executes.

rdebug can do four main kinds of things (plus other things in support of these) to help you catch bugs in the act:

Although you can use rdebug to invoke your Ruby programs via a debugger at the outset, there are other ways to use and enter the debugger.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.1 The First Sample rdebug Session (list, display, print, and quit)

You can use this manual at your leisure to read all about ruby-debug. However, a handful of commands are enough to get started using the debugger. The following sections illustrates these commands.

Below is Ruby code to compute a triangle number of a given length.(1)

 
$ rdebug triangle.rb
triangle.rb:4 def hanoi(n,a,b,c)
(rdb:1) list
[-1, 8] in ./triangle.rb
   1  #!/usr/bin/env ruby
   2  # Compute the n'th triangle number - the hard way
   3  # triangle(n) == (n * (n+1)) / 2
=> 4  def triangle(n)
   5    tri = 0
   6    0.upto(n) do |i|
   7      tri += i
   8    end
(rdb:1) l
[9, 18] in ./triangle.rb
   9    return tri
   10  end
   11  
   12  puts triangle(3)
(rdb:1) list 1,100
[1, 100] in ./triangle.rb
   1  #!/usr/bin/env ruby
   2  # Compute the n'th triangle number - the hard way
   3  # triangle(n) == (n * (n+1)) / 2
=> 4  def triangle(n)
   5    tri = 0
   6    0.upto(n) do |i|
   7      tri += i
   8    end
   9    return tri
   10  end
   11  
   12  puts triangle(3)
(rdb:1) 

There are lots of command options, but we don’t need them for now. See Options you can pass to rdebug for a full list of command options.

Position information consists of a filename and line number, e.g. triangle.rb:4. We are currently stopped before the first executable line of the program; this is line 4 of triangle.rb. If you are used to less dynamic languages and have used debuggers for more statically compiled languages like C, C++, or Java, it may seem odd to be stopped before a function definition. But in Ruby line 4 is executed, the name triangle (probably) does not exist so issuing a method call of triangle will raise a “method not found” error.

ruby-debug’s prompt is (rdb:n). The n is the thread number. Here it is 1 which is usually the case for the main thread. If the program has died and you are in post-mortem debugging, there is no thread number. In this situation, the string post-mortem is used in place of a thread number. If the program has terminated normally, the string this position will be ctrl. The commands which are available change depending on the program state.

The first command, list (see section Examining Program Source Files (‘list’)), prints 10 lines centered around the current line; the current line here is line 4 and is marked by =>, so the range the debugger would like to show is -1..8. However since there aren’t 5 lines before the current line, those additional lines—“lines” -1 and 0—are dropped and we print the remaining 8 lines. The list command can be abbreviated with l which is what we use next. Notice that when we use this a second time, we continue listing from the place we last left off. The desired range of lines this time is lines 9 to 18; but since the program ends as line 12, only the remaining 4 lines are shown.

If you want to set how many lines to print by default rather than use the initial number of lines, 10, use the set listsize command (see section Set/Show Number of Lines Shown in a List Command). To see the entire program in one shot, we gave an explicit starting and ending line number.

If you want a list command to run every time the debugger stops, use set autolist (see section Execute “list” Command on Every Stop).

If you use a front-end to the debugger such as the Emacs interface, you probably won’t use list all that much.

Now let us step through the program.

 
(rdb:1) step
triangle.rb:12
puts triangle(3)
(rdb:1) <<RET>>
triangle.rb:5
tri = 0
(rdb:1) p tri
nil
(rdb:1) step
triangle.rb:6
0.upto(n) do |i|
(rdb:1) p tri
0

The first step command (see section Step (‘step’)) runs the script one executable unit. The second command we entered was just hitting the return key; rdebug remembers the last command you entered was step, so it runs that last command again.

One way to print the values of variables uses p. (Of course, there are of course lots of other ways too.). When we look at the value of tri the first time, we see it is nil. Again we are stopped before the assignment on line 5, and this variable hasn’t been set previously. However after issuing another “step” command we see that the value is 0 as expected. You could issue the step and print comman in one shot:

However if every time we stop we want to see the value of tri to see how things are going stop, there is a better way by setting a display expression (see section Executing expressions on stop (‘display’, ‘undisplay’)).

 
(rdb:1) display tri
1: tri = 0

Now let us run the program until we return from the function. However we’ll want to see which lines get run.

 
(rdb:1) display i
2: i =
(rdb:1) set linetrace on
line tracing is on.
(rdb:1) finish
Tracing(1):triangle.rb:7 tri += i
1: tri = 0
2: i = 0
Tracing(1):triangle.rb:7 tri += i
1: tri = 0
2: i = 1
Tracing(1):triangle.rb:7 tri += i
1: tri = 1
2: i = 2
Tracing(1):triangle.rb:7 tri += i
1: tri = 3
2: i = 3
Tracing(1):triangle.rb:9 return tri
1: tri = 6
2: i =
(rdb:1) quit
Really quit? (y/n) y

So far, so good. A you can see from the above to get out of the debugger, one can issue a quit command. (q and exit are just as good. If you want to quit without being prompted, suffix the command with an exclamation mark, e.g. q!.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.2 Sample Session 2: Delving Deeper (where, frame, restart, autoeval, break, ps)

In this section we’ll introduce breakpoints, the call stack and restarting. So far we’ve been doing pretty good in that we’ve not encountered a bug to fix. Let’s try another simple example. Okay here’s the program.

Below we will debug a simple Ruby program to solve the classic Towers of Hanoi puzzle. It is augmented by the bane of programming: some command-parameter processing with error checking.

 
$ rdebug hanoi.rb
hanoi.rb:3 def hanoi(n,a,b,c)
(rdb:1) list 1,100
[1, 100] in ./hanoi.rb
   1  #!/usr/bin/ruby
   2
=> 3  def hanoi(n,a,b,c)
   4      if n-1 > 0
   5         hanoi(n-1, a, c, b)
   6      end
   7      puts "Move disk %s to %s" % [a, b]
   8      if n-1 > 0
   9         hanoi(n-1, c, b, a)
   10      end
   11  end
   12
   13  i_args=ARGV.length
   14  if i_args > 1
   15      puts "*** Need number of disks or no parameter"
   16      exit 1
   17  end
   18
   19  n=3
   20
   21  if i_args > 0
   22      begin
   23        n = ARGV[0].to_i
   24      rescue ValueError, msg:
   25          print "** Expecting an integer, got: %s" % ARGV[0].to_s
   26        exit 2
   27      end
   28  end
   29
   30  if n < 1 or n > 100
   31      puts "*** number of disks should be between 1 and 100"
   32      exit 2
   33  end
   34
   35  hanoi(n, :a, :b, :c)
(rdb:1)

Recall in the first section I said that before the def is run the method it names is undefined. Let’s check that out. First let’s see what private methods we can call before running def hanoi

 
(rdb:1) set autoeval on
autoeval is on.
(rdb:1) private_methods
["select", "URI", "local_variables", "lambda", "chomp", ... 

The set autoeval (see section Set/Show auto-eval) command causes any commands that are not normally understood to be debugger commands to get evaluated as though they were Ruby commands. I use this a lot, so I set this by putting it the command file .rdebugrc, see section Command files, that gets read when ruby-debug starts.

As showing the list output of private_methods, I find this kind of list unwieldy. What you are supposed to notice here is that method hanoi is not in this list. When you ask ruby-debug for a list of method names via method instance, it doesn’t show output in this way; ruby-debug can sort and put into columns lists like this using the print command, ps.

 
(rdb:1) ps private_methods
Array                    exit!                 puts                        warn
Float                    fail                  raise                       y
Integer                  fork                  rand
Rational                 format                readline
String                   gem_original_require  readlines
URI                      getc                  remove_instance_variable
`                        gets                  scan
abort                    global_variables      select
active_gem_with_options  gsub                  set_trace_func
at_exit                  gsub!                 singleton_method_added
autoload                 initialize            singleton_method_removed
autoload?                initialize_copy       singleton_method_undefined
binding                  iterator?             sleep
block_given?             lambda                split
callcc                   load                  sprintf
caller                   local_variables       srand
catch                    location_of_caller    sub
chomp                    loop                  sub!
chomp!                   method_missing        syscall
chop                     open                  system
chop!                    p                     test
dbg_print                pp                    throw
dbg_puts                 print                 timeout
eval                     printf                trace_var
exec                     proc                  trap
exit                     putc                  untrace_var

Now let’s see what happens after stepping

 
(rdb:1) private.methods.member?("hanoi")
false
(rdb:1) step
hanoi.rb:13
i_args=ARGV.length
(rdb:1) private_methods.member?("hanoi")
true
(rdb:1)

Okay, now where were we?

 
(rdb:1) list
[8, 17] in ./hanoi.rb
   8      if n-1 > 0
   9         hanoi(n-1, c, b, a)
   10      end
   11  end
   12
=> 13  i_args=ARGV.length
   14  if i_args > 1
   15      puts "*** Need number of disks or no parameter"
   16      exit 1
   17  end
(rdb:1) ARGV
[]

Ooops. We forgot to specify any parameters to this program. Let’s try again. We can use the restart command here.

 
(rdb:1) restart 3
Re exec'ing:
        /usr/bin/rdebug hanoi.rb 3
hanoi.rb:3
def hanoi(n,a,b,c)
(rdb:1) break 4
Breakpoint 1 file hanoi.rb, line 4
(rdb:1) continue
Breakpoint 1 at hanoi.rb:4
./hanoi.rb:4 if n-1 > 0
(rdb:1) display n
1: n = 3
(rdb:1) display a
2: a = a
(rdb:1) undisplay 2
(rdb:1) display a.inspect
3: a.inspect = :a
(rdb:1) display b.inspect
4: b.inspect = :b
(rdb:1) continue
Breakpoint 1 at hanoi.rb:4
./hanoi.rb:4 
if n-1 > 0
1: n = 2
3: a.inspect = :a
4: b.inspect = :c
(rdb:1) c
Breakpoint 1 at hanoi.rb:4
./hanoi.rb:4 
if n-1 > 0
1: n = 1
3: a.inspect = :a
4: b.inspect = :b
(rdb:1) where
--> #0 Object.hanoi(n#Fixnum, a#Symbol, b#Symbol, c#Symbol) at line hanoi.rb:4
    #1 Object.-(n#Fixnum, a#Symbol, b#Symbol, c#Symbol) at line hanoi.rb:5
    #2 Object.-(n#Fixnum, a#Symbol, b#Symbol, c#Symbol) at line hanoi.rb:5
    #3 at line hanoi.rb:35
(rdb:1) 

In the above we added a new command, break (see section Breakpoints (‘break’, ‘catch’, ‘delete’)) which indicates to go into the debugger just before that line of code is run. And continue resumes execution. Notice the difference between display a and display a.inspect. An implied string conversion is performed on the expression after it is evaluated. To remove a display expression we used undisplay is used. If we give a display number, just that display expression is removed.

Above we also used a new command where (see section Backtraces (‘where’) to show the call stack. In the above situation, starting from the bottom line we see we called the hanoi from line 35 of the file hanoi.rb and the hanoi method called itself two more times at line 5.

In the call stack we show the file line position in the same format when we stop at a line. Also we see the names of the parameters and the types that those parameters currently have. It’s possible that when the program was called the parameter had a different type, since the types of variables can change dynamically. You alter the style of what to show in the trace (see section Set/Show Call Style).

Let’s explore a little more. Now were were we?

 
(rdb:1) list
   1  #!/usr/bin/ruby
   2
   3  def hanoi(n,a,b,c)
=> 4      if n-1 > 0
   5         hanoi(n-1, a, c, b)
   6      end
   7      puts "Move disk %s to %s" % [a, b]
   8      if n-1 > 0
(rdb:1) undisplay
Clear all expressions? (y/n) y
(rdb:1) i_args
NameError Exception: undefined local variable or method `i_args' for main:Object
(rdb:1) frame -1
#3 at line hanoi.rb:35
(rdb:1) i_args
1
(rdb:1) p n
3
(rdb:1) down 2
#2 Object.-(n#Fixnum, a#Symbol, b#Symbol, c#Symbol) at line hanoi.rb:5
(rdb:1) p n
2

Notice in the above to get the value of variable n, I have to use a print command like p n; If I entered just n, that would be taken to mean the debugger command “next”. In the current scope, variable i_args is not defined. However I can change to the top-most frame by using the frame command. Just as with arrays, -1 means the last one. Alternatively using frame number 3 would have been the same thing; so would issuing up 3.

Note that in the outside frame 3, the value of i_args can be shown. Also note that the value of variable n is different.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.3 Using the debugger in unit testing (ruby-debug/debugger, Debugger.start)

In the previous sessions we’ve been calling the debugger right at the outset. I confess that this mode of operation is usually not how I use the debugger.

There are a number of situations where calling the debugger at the outset is impractical for a couple of reasons.

  1. The debugger just doesn’t work when run at the outset. By necessity any debugging changes to the behavior or the program in slight and subtle ways, and sometimes this can hinder finding the bugs.
  2. There’s a lot of code which that needs to get run before the part you want to inspect. Running this code takes time and you don’t the overhead of the debugger in this first part.

In this section we’ll delve show how to enter the code in the middle of your program, while delving more into the debugger operation.

In this section we will also use unit testing. Using unit tests will greatly reduce the amount of debugging needed while at the same time increase the quality of your program.

What we’ll do is take the triangle code from the first session and write a unit test for that. In a sense we did write a mini-test for the program which was basically the last line where we printed the value of triangle(3). This test however wasn’t automated: the implication is that someone would look at the output and verify that what was printed is what was expected.

And before we can turn that into something that can be required, we probably want to remove that output. However I like to keep in that line so that when I look at the file, I have an example of how to run it. Therefore we will conditionally run this line if that file is invoked directly, but skip it if it is not.(2)

 
  if __FILE__ == $0 
    puts triangle(3)
  end

Let’s call this file tri2.rb.

Okay, we’re now ready to write our unit test. We’ll use "test/unit" which comes with the standard Ruby distribution. Here’s the test code:

 
  #!/usr/bin/env ruby
  require 'test/unit'
  require 'tri2.rb'

  class TestTri < Test::Unit::TestCase
    def test_basic
     solutions = []
     0.upto(5) do |i|
        solutions << triangle(i)
      end
      assert_equal([0, 1, 3, 6, 10, 15], solutions,
                   'Testing the first 5 triangle numbers')
    end
  end

If you run it will work. However if you run rdebug initially, you will not get into the test, because test/unit wants to be the main program. So here is a situation where one may need to modify the program to add an explicit entry into the debugger.(3)

One way to do this is to add the following before the place you want to stop:

 
  require 'rubygems'
  require 'ruby-debug/debugger'

The line require "rubygems" is needed if ruby-debug is installed as a Ruby gem.

Let’s add this code just after entering test_basic:

 
  ... 
  def test_basic
    require "rubygems"
    require "ruby-debug/debugger"
    solutions = []
   ...

Now we run the program..

 
  $ ruby test-tri.rb
  Loaded suite test-tri
  Started
  test-tri.rb:9
  solutions = []
  (rdb:1)

and we see that we are stopped at line 9 just before the initialization of the list solutions.

Now let’s see where we are...

 
(rdb:1) where
--> #0 TestTri.test_basic at line /home/rocky/ruby/test-tri.rb:9
(rdb:1) 

Something seems wrong here; TestTri.test_basic indicates that we are in class TestTri in method test_basic. However we don’t see the call to this like we did in the last example when we used the where command. This is because the debugger really didn’t spring into existence until after we already entered that method, and Ruby doesn’t keep call stack information around in a way that will give the information we show when running where.

If we want call stack information, we have to turn call-stack tracking on beforehand. This is done by adding Debugger.start.

Here’s what our test program looks like so after we modify it to start tracking calls from the outset

 
#!/usr/bin/env ruby
require 'test/unit'
require 'tri2.rb'
require 'rubygems'
Debugger.start

class TestTri < Test::Unit::TestCase
  def test_basic
    debugger
    solutions = []
    0.upto(5) do |i|
      solutions << triangle(i)
    end
    assert_equal([0, 1, 3, 6, 10, 15], solutions,
                 "Testing the first 5 triangle numbers")
  end
end

Now when we run this:

 
$ ruby test-tri2.rb
Loaded suite test-tri2
Started
test-tri2.rb:11
solutions = []
(rdb:1) where
--> #0 TestTri.test_basic at line test-tri2.rb:11
    #1 Kernel.__send__(result#Test::Unit::TestResult) 
       at line /usr/lib/ruby/1.8/test/unit/testcase.rb:70
    #2 Test::Unit::TestCase.run(result#Test::Unit::TestResult) 
       at line /usr/lib/ruby/1.8/test/unit/testcase.rb:70
...
    #11 Test::Unit::AutoRunner.run 
       at line /usr/lib/ruby/1.8/test/unit/autorunner.rb:200
    #12 Test::Unit::AutoRunner.run(force_standalone#FalseClass, ...
       at line /usr/lib/ruby/1.8/test/unit/autorunner.rb:13
    #13 at line /usr/lib/ruby/1.8/test/unit.rb:285
(rdb:1) 

Much better. But again let me emphasize that the parameter types are those of the corresponding variables that currently exist, and this might have changed since the time when the call was made. Even so and even though we only have types listed, it’s a pretty good bet that when Test::Unit was first called, shown above as frame 12, that the values of its two parameters were false and nil.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.4 Using the Debugger.start with a block

We saw that Debugger.start() and Debugger.stop() allow fine-grain control over where the debugger tracking should occur.

Rather than use an explicit stop(), you can also pass a block to the start() method. This causes start() to run and then yield to that block. When the block is finished, stop() is run. In other words, this wraps a Debugger.start() and Debugger.stop() around the block of code. But it also has a side benefit of ensuring that in the presence of an uncaught exception stop is run, without having to explicitly use begin ... ensure Debugger.stop() end.

For example, in Ruby Rails you might want to debug code in one of the controllers without causing any slowdown to any other code. And this can be done by wrapping the controller in a start() with a block; when the method wrapped this way finishes the debugger is turned off, and the application proceeds at regular speed.

Of course, inside the block you will probably want to enter the debugger using Debugger.debugger(), otherwise there would little point in using the start. For example, you can do this in irb:

 
$ irb
irb(main):001:0> require 'rubygems'; require 'ruby-debug'
=> true
irb(main):002:0> def foo
irb(main):003:1> x=1
irb(main):004:1> puts 'foo'
irb(main):005:1> end
=> nil
irb(main):006:0> Debugger.start{debugger; foo}
(irb):6
(rdb:1) s
(irb):3
(rdb:1) p x
nil
(rdb:1) s
(irb):4
(rdb:1) p x
1
(rdb:1) s
foo
=> true
irb(main):007:0> 

There is a counter inside of Debugger.start method to make sure that this works when another Debugger.start method is called inside of outer one. However if you are stopped inside the debugger, issuing another debugger call will not have any effect even if it is nested inside another Debugger.start.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.5 Connecting to the debugger from the Outside


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.5.1 Remote execution from the outset

It is possible to set up debugging so that you can issue debugger commands from outside of the process running the Ruby code. In fact, you might even be on a different computer than the one running the Ruby program.

To do this one sets up the to debug a program a “server” mode. For this use the ‘--server’ option on a rdebug invocation. We will use one other option below: ‘--wait’:

 
$ rdebug --server --wait tri3.rb
# Nothing happens

Without ‘--wait’ the program would run and probably terminate before we have a chance to connect to it. Next, in a different window on the same machine, run the “client” side. This will connect to this waiting program in “server” mode:

 
$ rdebug --client
Connected.
(rdb:1) bt
--> #0 at line triangle.rb:4

Other options related to remote debugging on both the client and server side are the ‘--host’, ‘--port’ options. By setting these options appropriately, is possible that to debug the program over a TCP/IP connection.

Note however that program output is still going to the place it normally would go; output is not redirected across the debugger connection.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.5.2 Going into Remote execution from inside a Ruby program

As with the case where you are debugging on the same machine, often you might not want call the debugger initially, but only after something happens. It is possible to call the debugger and arrange it to start out in server mode.

To do this, arrange for the following code to get executed.

 
  require 'rubygems'; require 'ruby-debug';

  # Omit the following assignment statment line if don't want to wait
  # for a client before continuing...
  Debugger.wait_connection = true
  Debugger.start_remote

The above only needs to happen one sometime before you code gets run that needs the debugger. As before, you probably want to put this before too many methods get nested in the call chain. Otherwise you won’t be able to inspect frames of those that were created before the “start_remote” above.

Next, at a place of program execution which gets run before you might ever want a debugger stop, add a call to “debugger” as was done without remote execution:

 
   # work, work, work....  
   debugger 
   some ruby code  # debugger will stop before this is run

If you are running for example a web service, you might create a URL that basically invokes “debugger”. And although, I’ve not tried this, I think you could but the call “debugger” inside a signal handler. When the signal handler returns you would be at the place you interrupted the Ruby program.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.6 How debugging Ruby may be different than debugging other Languages

If you are used to debugging in other languages like C, C++, Perl, Java or even Bash(4), there may be a number of things that seem or feel a little bit different and may confuse you. A number of these things aren’t oddities of the debugger per see, so much as a difference in how Ruby works compared to those other languages. Because Ruby works a little differently from those other languages, writing a debugger has to also be a little different as well if it is to be useful.

In this respect, using the debugger may help you understand Ruby better.

We’ve already seen two examples of such differences. One difference is the fact that we stop on method definitions or def’s and that’s because these are in fact executable statements. In other compiled languages this would not happen because that’s already been done when you compile the program (or in Perl when it scans in the program). The other difference we saw was in our inability to show call stack parameter types without having made arrangements for the debugger to track this. In other languages call stack information is usually available without asking assistance of the debugger.(5)

In this section we’ll consider some other things that might throw off new users to Ruby who are familiar with other languages and debugging in them.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.6.1 Stack Shows Scope Nesting

In a backtrace, you will find more stack frames than you might in say C.

Consider another way to write the triangle program of see section The First Sample rdebug Session (list, display, print, and quit).

 
  1 #!/usr/bin/env ruby
  2 def triangle(n) 
  3   (0..n).inject do |sum, i| 
  4    sum +=i 
  5   end
  6 end
  7 puts triangle(3)

Let’s stop inside the inject block:

 
$ rdebug tri3.rb
(rdb:1) c 4
tri3.rb:4
sum +=i 
(rdb:1) where
--> #0 Range.triangle at line tri3.rb:4
    #1 Enumerable.inject at line tri3.rb:3
    #2 Object.triangle(n#Fixnum) at line tri3.rb:3
    #3 at line tri3.rb:7
(rdb:1)

Because a new scope was entered, it appears as a stack frame. Probably “scope” frame would be a more appropriate name.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.6.2 More Frequent Evaluations per Line

Consider this simple program to compute the Greatest Common Divisor of two numbers:

 
 1 #!/usr/bin/env ruby
 2 # GCD. We assume positive numbers
 3
 4 def gcd(a, b)
 5   # Make: a <= b
 6  if a > b
 7     a, b = [b, a]
 8  end
 9
10  return nil if a <= 0
11 
12  if a == 1 or b-a == 0
13    return a
14   end
15   return gcd(b-a, a)
16 end
17
18 a, b = ARGV[0..1].map {|arg| arg.to_i}
19 puts "The GCD of %d and %d is %d" % [a, b, gcd(a, b)]

Now let’s try tracing a portion of the program to see what we get.

 
$ rdebug gcd.rb 3 5
gcd.rb:4
def gcd(a, b)
(rdb:1) step
gcd.rb:18
a, b = ARGV[0..1].map {|arg| arg.to_i}
(rdb:1) step
gcd.rb:18
a, b = ARGV[0..1].map {|arg| arg.to_i}
(rdb:1) step
gcd.rb:18
a, b = ARGV[0..1].map {|arg| arg.to_i}
(rdb:1) step
(rdb:1) break Object.gcd
Breakpoint 1 at Object::gcd
(rdb:1) continue
Breakpoint 1 at Object:gcd
gcd.rb:4
def gcd(a, b)
(rdb:1) set linetrace on
line tracing is on.
(rdb:1) continue
Tracing(1):gcd.rb:6 if a > b
Tracing(1):gcd.rb:6 if a > b
Tracing(1):gcd.rb:10 return nil if a <= 0
Tracing(1):gcd.rb:10 return nil if a <= 0
Tracing(1):gcd.rb:12 if a == 1 or b-a == 0
Tracing(1):gcd.rb:12 if a == 1 or b-a == 0
Tracing(1):gcd.rb:15 return gcd(b-a, a)
Breakpoint 1 at Object:gcd
gcd.rb:4
def gcd(a, b)
(rdb:1) 

The thing to note here is that we see lots of lines duplicated. For example, the first line:

 
Tracing(1):gcd.rb:18 a, b = ARGV[0..1].map {|arg| arg.to_i}

appears three times. If we were to break this line into the equivalent multi-line expression:

 
a, b = ARGV[0..1].map do |arg| 
  arg.to_i
end

we would find one stop at the first line before running map and two listings of arg.to_i, once for each value of arg which here is 0 and then 1. Perhaps this is is not surprising because we have a loop here which gets run in this situation 3 times. A similar command next, can also be used to skip over loops and method calls.

But what about all the duplicated if statements in gcd? Each one is listed twice whether or not we put the if at the beginning or the end. You will find this to be the case for any conditional statement such as until or while.

Each statement appears twice because we stop once before the expression is evaluated and once after the expression is evaluated but before the if statement takes hold. There is a bug in Ruby up to version 1.8.6 in that we stop a second time before the evaluation, so examining values that may have changed during the expression evaluation doesn’t work in these versions.

If you are issuing a step command one at a time, the repetitive nature can be little cumbersome if not annoying. So ruby-debug offers a variant called step+ which forces a new line on every step. Let’s try that.

 
(rdb:1) R
Re exec'ing:
	/usr/bin/rdebug gcd.rb 3 5
gcd.rb:4
def gcd(a, b)
(rdb:1) step+
gcd.rb:18
a, b = ARGV[0..1].map {|arg| arg.to_i}
(rdb:1) step+
gcd.rb:19
puts "The GCD of %d and %d is %d" % [a, b, gcd(a, b)]
(rdb:1) break Object.gcd
Breakpoint 1 at Object:gcd
(rdb:1) c
Breakpoint 1 at Object:gcd
gcd.rb:4
def gcd(a, b)
(rdb:1) set linetrace+
line tracing style is different consecutive lines.
(rdb:1) set linetrace on
line tracing is on.
(rdb:1) c
Tracing(1):gcd.rb:6 if a > b
Tracing(1):gcd.rb:10 return nil if a <= 0
Tracing(1):gcd.rb:12 if a == 1 or b-a == 0
Tracing(1):gcd.rb:15 return gcd(b-a, a)
Breakpoint 1 at Object:gcd
gcd.rb:4
def gcd(a, b)

If you want step+ to be the default behavior when stepping, issue the command set different on, (see section Set/Show Different Line Forcing on Step/Next). I generally put this in my start-up file .rdebugrc.

Similar to the difference between step+ and step is set linetrace+. This removes duplicate consecutive line tracing.

One last thing to note above is the use of a method name to set a breakpoint position, rather than a file and line number. Because method gcd is in the outermost scope, we use Object as the class name.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.6.3 Bouncing Around in Blocks (e.g. Iterators)

When debugging languages with coroutines like Python and Ruby, a method call may not necessarily go to the first statement after the method header. It’s possible the call will continue after a yield statement from a prior call.

 
 1 #!/usr/bin/env ruby
 2 # Enumerator for primes
 3 class SievePrime
 4   @@odd_primes = []
 5   def self.next_prime(&block)
 6     candidate = 2
 7     yield candidate
 8     not_prime = false
 9     candidate += 1
10     while true do
11       @@odd_primes.each do |p|
12         not_prime = (0 == (candidate % p))
13         break if not_prime
14       end
15       unless not_prime
16         @@odd_primes << candidate
17         yield candidate 
18       end
19       candidate += 2
20     end
21   end
22 end
23 SievePrime.next_prime do |prime|
24   puts prime
25   break if prime > 10
26 end
 
$ rdebug primes.rb
primes.rb:3
class SievePrime
(rdb:1) set linetrace on
line tracing is on.
(rdb:1) step 10
Tracing(1):primes.rb:4 @odd_primes = []
Tracing(1):primes.rb:5 def self.next_prime(&block)
Tracing(1):primes.rb:23 SievePrime.next_prime do |prime|
Tracing(1):primes.rb:6 candidate = 2
Tracing(1):primes.rb:7 yield candidate
Tracing(1):primes.rb:24 puts prime
2
Tracing(1):primes.rb:25 break if prime > 10
Tracing(1):primes.rb:25 break if prime > 10
Tracing(1):primes.rb:8 not_prime = false
Tracing(1):primes.rb:9 candidate += 1
primes.rb:9
candidate += 1
(rdb:1) 

The loop between lines 23–26 gets interleaved between those of Sieve::next_prime, lines 6–19 above.

A similar kind of thing can occur in debugging programs with many threads.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.6.4 No Parameter Values in a Call Stack

In traditional debuggers in a call stack you can generally see the names of the parameters and the values that were passed in.

Ruby is a very dynamic language and it tries to be efficient within the confines of the language definition. Values generally aren’t taken out of a variable or expression and pushed onto a stack. Instead a new scope created and the parameters are given initial values. Parameter passing is by reference, not by value as it is say Algol, C, or Perl. During the execution of a method, parameter values can change—and often do. In fact even the class of the object can change.

So at present, the name of the parameter shown. The call-style setting see section Set/Show Call Style can be used to set whether the name is shown or the name and the current class of the object.

It has been contemplated that a style might be added which saves on call shorter “scalar” types of values and the class name.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.6.5 Lines You Can Stop At

As with the duplicate stops per control (e.g. if statement), until tools like debuggers get more traction among core ruby developers there are going to be weirdness. Here we describe the stopping locations which effects the breakpoint line numbers you can stop at.

Consider the following little Ruby program.

 
'Yes it does' =~ /
(Yes) \s+
it  \s+
does
/ix 
puts $1

The stopping points that Ruby records are the last two lines, lines 5 and 6. If you run ruby -rtracer on this file you’ll see that this is so:

 
$ ruby -rtracer lines.rb
#0:lines.rb:5::-: /ix 
#0:lines.rb:6::-: puts $1
#0:lines.rb:6:Kernel:>: puts $1
#0:lines.rb:6:IO:>: puts $1
Yes#0:lines.rb:6:IO:<: puts $1
#0:lines.rb:6:IO:>: puts $1

#0:lines.rb:6:IO:<: puts $1
#0:lines.rb:6:Kernel:<: puts $1

Inside ruby-debug you an get a list of stoppable lines for a file using the info file command with the attribute breakpoints.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

2. Installing ruby-debug

There are a number of pitfalls that one can run into in trying to install ruby-debug. We will try to go over them here.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

2.1 ruby-debug is not debug.rb

One problem that comes up a bit is a confusion of this gem package, ruby-debug, with the Ruby library program debug.rb. The latter comes distributed with Ruby.

When you run ruby -r debug test.rb or without the space between the -r and the debug:

 
   $ ruby -rdebug test.rb  # This is something not described here

you are getting the program that comes installed with Ruby. Although what we describe here, ruby-debug, has commands which are largely a superset of the debug.rb commands, ruby-debug is something vastly different.

ruby-debug is a packaged as a gem. When installed, the program bin/rdebug is installed. This allows you to invoke the debugger from the outset. But don’t confuse the example above with

 
   $ rdebug test.rb  # not the same as the above!

The Pickaxe books talk about debug.rb, not the ruby-debug gem. I think it safe to say that most folks who use any sort of debugger are nowadays using ruby-debug. (Even the IDE’s like the ones from Eclipse, JetBrains, or Aptana use common code from ruby-debug and none import any code from debug.rb)

I realize all of this is confusing. Unfortunately it is a bit too late to change this in any significant way.

In the next round of debuggers, the “trepanning” debuggers — http://github.com/rocky/rb-trepanning and http://github.com/rocky/rbx-trepanning — I’ve tried to make the distinction more clear. The command-line invocation is no longer rdebug. And the prompt which had been inside the debugger (rdb) is now different.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

2.2 Installing the gem

In the simplest case, one should be able to run:

 
  gem install ruby-debug  

If however you need root permission to install gems (which is not the case if your Ruby is installed via rvm (http://rvm.beginrescueend.com), then:

 
  sudo gem install ruby-debug # Note: nuke the 'sudo' if using rvm!

Ruby debug uses a number of packages, namely: ruby-debug-base, columnize, and linecache. But those gems should get pulled in automatically. The gem ruby-debug-base is a C extension. To install C extensions you need to make sure you have Ruby’s C header files for compiling extension modules. This includes a file called ruby.h. On Debain/Ubuntu the name of Debian package is called ruby1.8-dev and is not installed when you just install Ruby 1.8.

If you are using an IDE or the Ruby you use is JRuby, then instead of installing ruby-debug, you want the gem ruby-debug-ide.

Mark Moseley ported ruby-debug and ruby-debug-ide for Ruby 1.9. Those gems are called ruby-debug19 and ruby-debug-ide19 respectively.

I (rocky) have however been rewriting ruby-debug from scratch to address a number of failings folks have encountered in ruby-debug. Those debuggers are called the “trepanning” debuggers. For Ruby 1.9.2 you need a patched version of Ruby. See https://github.com/rocky/rb-trepanning/wiki/How-to-Install-rb-trepanning. For Rubinius the gem to use is rbx-trepanning. No patched Ruby is needed, however you will need a recent version of Rubinius.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

3. Getting in and out

It is also possible to enter the debugger when you have an uncaught exception. See See also Post-Mortem Debugging.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

3.1 Starting the debugger

Although one can enter ruby-debug via Emacs (described in a later section) and possibly others interfaces, probably the most familiar thing to do is invoke the debugger from a command line.

A wrapper shell script called rdebug basically require’s the gem package ruby-debug and then loads rdebug.

 
rdebug [rdebug-options] [--] ruby-script ruby-script-arguments...

If you don’t need to pass dash options to your program which might get confused with the debugger options, then you don’t need to add the ‘--’.

To get a brief list of options and descriptions, use the --help option.

 
$ rdebug --help
rdebug 0.10.5
Usage: rdebug [options] <script.rb> -- <script.rb parameters>

Options:
    -A, --annotate LEVEL             Set annotation level
    -c, --client                     Connect to remote debugger
        --cport PORT                 Port used for control commands
    -d, --debug                      Set $DEBUG=true
        --emacs LEVEL                Activates full Emacs support at annotation level LEVEL
        --emacs-basic                Activates basic Emacs mode
    -h, --host HOST                  Host name used for remote debugging
    -I, --include PATH               Add PATH to $LOAD_PATH
        --keep-frame-binding         Keep frame bindings
    -m, --post-mortem                Activate post-mortem mode
        --no-control                 Do not automatically start control thread
        --no-quit                    Do not quit when script finishes
        --no-rewrite-program         Do not set $0 to the program being debugged
        --no-stop                    Do not stop when script is loaded
    -nx                              Not run debugger initialization files (e.g. .rdebugrc
    -p, --port PORT                  Port used for remote debugging
    -r, --require SCRIPT             Require the library, before executing your script
        --script FILE                Name of the script file to run
    -s, --server                     Listen for remote connections
    -w, --wait                       Wait for a client connection, implies -s option
    -x, --trace                      Turn on line tracing

Common options:
        --verbose                    Turn on verbose mode
        --help                       Show this message
        --version                    Print the version
    -v                               Print version number, then turn on verbose mode

Options for the rdebug are shown in the following list.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

3.1.1 Options you can pass to rdebug

You can run ruby-debug in various alternative modes—for example, as a program that interacts directly with the program in the same process on the same computer or via a socket to another process possibly on a different computer.

Many options appear as a long option name, such as ‘--help’, and a short one letter option name, such as ‘-h’. A double dash (‘--’ is used to separate options which go to rdebug from options that are intended to go to your Ruby script. Options (if any) to rdebug should come first. If there is no possibility of the Ruby script to be debugged getting confused with rdebug’s option the double dash can be omitted.

--help

This option causes rdebug to print some basic help and exit.

-v | --version

This option causes rdebug to print its version number and exit.

-A | --annotate level

Set gdb-style annotation level, a number. Additional information is output automatically when program state is changed. This can be used by front-ends such as GNU Emacs to post this updated information without having to poll for it.

-c | --client

Connect to remote debugger. The remote debugger should have been set up previously our you will get a connection error and rdebug will terminate.

--cport port

Port used for control commands.

--debug

Set $DEBUG to true. This option is compatible with Ruby’s.

--emacs

Activates GNU Emacs mode. Debugger output is tagged in such a way to allow GNU Emacs to track where you are in the code.

--emacs-basic

Activates full GNU Emacs mode. This is the equivalent of setting the options --emacs-basic, annotate=3, --no-stop, -no-control and --post-mortem.

-h | --host host-address

Connect host address for remote debugging.

-I --include PATH

Add PATH to $LOAD_PATH

--keep-frame-binding

Bindings are used to set the proper environment in evaluating expression inside the debugger. Under normal circumstances, I don’t believe most people will ever need this option.

By default, the debugger doesn’t create binding object for each frame when the frame is created, i.e. when a call is performed. Creating a binding is an expensive operation and has been a major source of performance problems.

Instead, the debugger creates a binding when there is a need to evaluate expressions. The artificial binding that is created might be different from the real one. In particular, in performing constant and module name resolution.

However it’s still possible to restore the old, slower behavior by using this option or by setting Debugger.keep_frame_binding = true. There are two possibilities for which you might want to use this option.

First, if you think there’s a bug in the evaluation of variables, you might want to set this to see if this corrects things.

Second, since the internal structures that are used here FRAME and SCOPE are not part of the Ruby specification, it is possible they can change with newer releases; so here this option this may offer a remedy. (But you’ll probably also have to hack the C code since it’s likely under this scenario that ruby-debug will no longer compile.) In fact, in Ruby 1.9 these structures have changed and that is partly why this debugger doesn’t work on Ruby 1.9.

-m | --post-mortem

If your program raises an exception that isn’t caught you can enter the debugger for inspection of what went wrong. You may also want to use this option in conjunction with --no-stop. See also Post-Mortem Debugging.

--no-control

Do not automatically start control thread.

--no-quit

Restart the debugger when your program terminates normally.

--no-rewrite-program

Normally rdebug will reset the program name $0 from its name to the debugged program, and set the its name in variable $RDEBUG_0. In the unlikely even you don’t want this use this option.

--no-stop

Normally the rdebug stops before executing the first statement. If instead you want it to start running initially and will perhaps break it later in the running, use this options.

-p | --port port

Port used for remote debugging.

-r | --require library

Require the library, before executing your script. However if the library happened to be debug, we’ll just ignore the require (since we’re already a debugger). This option is compatible with Ruby’s.

--script file

Require the library, before executing your script. However if the library hap-pend to be debug, we’ll just ignore the require (since we’re already a debugger). This option is compatible with Ruby’s.

-s | --server

Debug the program but listen for remote connections on the default port or port set up via the --port option. See also --wait.

-w | --wait

Debug the program but stop waiting for a client connection first. This option automatically sets --server option.

-x | --trace

Turn on line tracing. Running rdebug --trace rubyscript.rb is much like running: ruby -rtracer rubyscript.rb

If all you want to do however is get a linetrace, tracer, not rdebug, may be faster:

 
$ time ruby -rtracer gcd.rb 34 21 > /dev/null

real	0m0.266s
user	0m0.008s
sys	0m0.000s
$ time rdebug --trace gcd.rb 34 21 > /dev/null

real	0m0.875s
user	0m0.448s
sys	0m0.056s
$

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

3.1.2 Options for Out-of-process execution

Option ‘--wait’ (pause execution until a client connects to the program) is only meaningful in “server” mode or with the ‘--server’ option.

With option ‘--client’ you don’t give the name of a program to debug. That was done when running the server.

With both the ‘--client’ and ‘--server’ options, you can specify the interface name to listen on (for server) or connect to (for client) as either a DNS name or as an IP address. Likewise one can specify a port number to listen on or connect to. As with any other TCP/IP connection the port names and interface name (as either an IP or a name) much correspond for the client to connect to a program in server mode. In the simple cases, the port names and host names will be the same. However due to port forwarding and NAT translation that sometimes occurs behind firewalls, it is possible these may be different.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

3.1.3 How to Set Default Command-Line Options

ruby-debug has many command-line options; it seems that some people want to set them differently from the our defaults. For example, some people may want ‘--no-quit --no-control’ to be the default behavior. One could write a wrapper script or set a shell alias to handle this. ruby-debug has another way to do this as well. Before processing command options if the file $HOME/.rdboptrc is found it is loaded. If you want to set the defaults in some other way, you can put Ruby code here and set variable options which is an OpenStruct. For example here’s how you’d set ‘-no-quit’ and change the default control port to 5000.

 
# This file contains how you want the default options to ruby-debug
# to be set. Any Ruby code can be put here.
#
# debugger # Uncomment if you want to debug rdebug!
options.control = false
options.port = 5000
puts "rocky's rdboptrc run"

Here are the default values in options

 
#<OpenStruct server=false, client=false, frame_bind=false, cport=8990, tracing=false, nx=false, post_mortem=false, port=8989, verbose_long=false, annotate=nil, control=true, restart_script=nil, quit=true, no_rewrite_program=false, stop=true, script=nil, host=nil, wait=false>

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

3.2 Command files

A command file for ruby-debug is a file of lines that are ruby-debug commands. Comments (lines starting with #) may also be included. An empty line in a command file does nothing; it does not mean to repeat the last command, as it would from the terminal.

When you start ruby-debug, it automatically executes commands from its init files, normally called ‘.rdebugrc’.

On some configurations of ruby-debug, the init file may be known by a different name. In particular on MS-Windows (but not cygwin) ‘rdebug.ini’ is used.

During startup, ruby-debug does the following:

  1. Processes command line options and operands.
  2. Reads the init file in your current directory, if any, and failing that the home directory. The home directory is the directory named in the HOME or HOMEPATH environment variable.

    Thus, you can have more than one init file, one generic in your home directory, and another, specific to the program you are debugging, in the directory where you invoke ruby-debug.

  3. Reads command files specified by the ‘--script’ option.

You can also request the execution of a command file with the source command, see section Running Debugger Commands (‘source’).


[ < ] [ > ]   [