« Back to Index

Ruby stdlib debugger

View original Gist on GitHub

Ruby stdlib debugger.md

Program app.rb:

def foo(msg)
  result = bar
  msg
end

def bar
  thing = 123
  thing
end

require "debug" # this is what drops us into the debugger
p foo(:hai)

Run the program as normal:

ruby app.rb

We find ourselves not necessarily where we’d expect:

Debug.rb
Emacs support available.

/Users/M/.rbenv/versions/2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:57:        RUBYGEMS_ACTIVATION_MONITOR.enter
(rdb:1) 

So first thing to do is to check for any breakpoints:

b
=> No breakpoints

Let’s set a breakpoint for our foo method (e.g. whenever it’s called we’ll stop there):

b app.rb:foo
=> Set breakpoint 1 at app.rb:foo

Let’s check the breakpoint was set:

b
=> Breakpoints:
     1 app.rb:foo

Let’s also set a breakpoint within the foo method at line 3 (just after we call the bar method and assign its result to result):

b app.rb:3
=> Set breakpoint 2 at app.rb:3

Again, let’s check the breakpoint was set:

b
=> Breakpoints:
     1 app.rb:foo
     2 app.rb:3

Let’s remind ourselves where we are before continuing on:

w
=> #1 /Users/M/.rbenv/versions/2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:57:in `require'

If we forget any debugging commands we can look at the help:

h
=> Debugger help v.-0.002b
Commands
  b[reak] [file:|class:]<line|method>
  b[reak] [class.]<line|method>
                             set breakpoint to some position
  wat[ch] <expression>       set watchpoint to some expression
  cat[ch] (<exception>|off)  set catchpoint to an exception
  b[reak]                    list breakpoints
  cat[ch]                    show catchpoint
  del[ete][ nnn]             delete some or all breakpoints
  disp[lay] <expression>     add expression into display expression list
  undisp[lay][ nnn]          delete one particular or all display expressions
  c[ont]                     run until program ends or hit breakpoint
  s[tep][ nnn]               step (into methods) one line or till line nnn
  n[ext][ nnn]               go over one line or till line nnn
  w[here]                    display frames
  f[rame]                    alias for where
  l[ist][ (-|nn-mm)]         list program, - lists backwards
                             nn-mm lists given lines
  up[ nn]                    move to higher frame
  down[ nn]                  move to lower frame
  fin[ish]                   return to outer frame
  tr[ace] (on|off)           set trace mode of current thread
  tr[ace] (on|off) all       set trace mode of all threads
  q[uit]                     exit from debugger
  v[ar] g[lobal]             show global variables
  v[ar] l[ocal]              show local variables
  v[ar] i[nstance] <object>  show instance variables of object
  v[ar] c[onst] <object>     show constants of object
  m[ethod] i[nstance] <obj>  show methods of object
  m[ethod] <class|module>    show instance methods of class or module
  th[read] l[ist]            list all threads
  th[read] c[ur[rent]]       show current thread
  th[read] [sw[itch]] <nnn>  switch thread context to nnn
  th[read] stop <nnn>        stop thread nnn
  th[read] resume <nnn>      resume thread nnn
  pp expression              evaluate expression and pretty_print its value
  p expression               evaluate expression and print its value
  r[estart]                  restart program
  h[elp]                     print this help
  <everything else>          evaluate

Let’s just ‘continue’ to our first breakpoint (which should be the foo method being called):

c
=> Breakpoint 1, foo at app.rb:foo
   app.rb:1:def foo(msg)

To be sure where we are let’s use l again:

l
[-4, 5] in app.rb
=> 1  def foo(msg)
   2    result = bar
   3    msg
   4  end
   5  

If at this point I enter the expression msg (which is the argument incoming into the foo method), then I’ll get its value:

msg
=> :hai

If I press c to continue again we’ll reach our next breakpoint:

c
=> Breakpoint 2, foo at app.rb:3
   app.rb:3:  msg

If at this point I enter the expression result (which is the variable set within the foo method), then I’ll get its value that was provided by the bar method:

result
=> 123

I can check for any local variables scoped/available to me at this point of the program:

v l
  msg => :hai
  result => 123

Before we wrap up, let me restart the program so we can go through the process again but take a slightly different route:

r
=> app.rb:57:

Note: the line number 57 doesn’t actually make any sense to me to be honest

From here we can ‘continue’ c to get to our first breakpoint (like we did before), but this time instead of skipping over the call to bar, we’ll use s to ‘step’ into the method and then n to go through the next subsequent lines until our program completes):

(rdb:1) s
app.rb:2:  result = bar
(rdb:1) l
[-3, 6] in app.rb
   1  def foo(msg)
=> 2    result = bar
   3    msg
   4  end
   5  
   6  def bar
(rdb:1) s
app.rb:7:  thing = 123
(rdb:1) l
[2, 11] in app.rb
   2    result = bar
   3    msg
   4  end
   5  
   6  def bar
=> 7    thing = 123
   8    thing
   9  end
   10  
   11  require "debug"
(rdb:1) v l
  thing => nil
(rdb:1) n
app.rb:8:  thing
(rdb:1) v l
  thing => 123
(rdb:1) n
app.rb:3:  msg
(rdb:1) l
[-2, 7] in app.rb
   1  def foo(msg)
   2    result = bar
=> 3    msg
   4  end
   5  
   6  def bar
   7    thing = 123
(rdb:1) result
123
(rdb:1) v l
  msg => :hai
  result => 123
(rdb:1) n
:hai