[Top] | [Contents] | [Index] | [ ? ] |
This file describes ruby-debug, the Ruby Debugger, version 0.10.5
This is the 0.10.4 Edition, 21 February 2011
1. Summary of ruby-debug | Overview of Debugger with sample sessions | |
2. Installing ruby-debug | Getting this puppy installed | |
3. Getting in and out | ||
4. ruby-debug Command Reference | rdebug command reference | |
5. Post-Mortem Debugging | Debugging on an uncaught exception | |
6. The Debugger Module and Class | ruby-debug’s Debugger module and class | |
Appendix | ||
---|---|---|
A. Building and Installing from rubyforge’s Subversion Repository | ||
Indexes (nodes containing large menus) | ||
Class, Module Method Index | An item for Class, Module, and Methods. | |
Command Index | An item for each command name. | |
RDebug Command Flags Index | An item for each flags you can pass to rdebug. | |
General Index | An item for each concept. |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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.
1.1 The First Sample rdebug Session (list , display , print , and quit ) | A Simple Sample rdebug session
| |
1.2 Sample Session 2: Delving Deeper (where , frame , restart , autoeval , break , ps ) | Second Session Delving a little deeper rdebug session
| |
1.3 Using the debugger in unit testing (ruby-debug/debugger , Debugger.start ) | Using the debugger in unit testing | |
1.4 Using the Debugger.start with a block | Using the Debugger.start with a block | |
1.5 Connecting to the debugger from the Outside | Connecting to the debugger from the outside | |
1.6 How debugging Ruby may be different than debugging other Languages | How debugging Ruby may be different... |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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.
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] | [ ? ] |
Debugger.start
with a blockWe 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.1 Remote execution from the outset | ||
1.5.2 Going into Remote execution from inside a Ruby program |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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.
1.6.1 Stack Shows Scope Nesting | ||
1.6.2 More Frequent Evaluations per Line | ||
1.6.3 Bouncing Around in Blocks (e.g. Iterators) | ||
1.6.4 No Parameter Values in a Call Stack | ||
1.6.5 Lines You Can Stop At |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
ruby-debug
2.1 ruby-debug is not debug.rb | ||
2.2 Installing the gem |
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] | [ ? ] |
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] | [ ? ] |
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.1 Starting the debugger | How to enter the debugger | |
3.2 Command files | ||
3.3 Quitting the debugger | How to leave the debugger (quit, kill) | |
3.4 Calling the debugger from inside your Ruby program | Calling the debugger from inside your program |
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.1 Options you can pass to rdebug | ||
3.1.2 Options for Out-of-process execution | ||
3.1.3 How to Set Default Command-Line Options |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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:
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.
You can also request the execution of a command file with the
source
command, see section Running Debugger Commands (‘source’).
[ < ] | [ > ] | [ |