Class: DEBUGGER__

Inherits:
Object
  • Object
show all
Defined in:
lib/debug.rb

Overview

This library provides debugging functionality to Ruby.

To add a debugger to your code, start by requiring debug in your program:

def say(word)
  require 'debug'
  puts word
end

This will cause Ruby to interrupt execution and show a prompt when the say method is run.

Once you’re inside the prompt, you can start debugging your program.

(rdb:1) p word
"hello"

Getting help

You can get help at any time by pressing h.

(rdb:1) 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
  p expression               evaluate expression and print its value
  h[elp]                     print this help
  <everything else>          evaluate

Usage

The following is a list of common functionalities that the debugger provides.

Navigating through your code

In general, a debugger is used to find bugs in your program, which often means pausing execution and inspecting variables at some point in time.

Let’s look at an example:

def my_method(foo)
  require 'debug'
  foo = get_foo if foo.nil?
  raise if foo.nil?
end

When you run this program, the debugger will kick in just before the foo assignment.

(rdb:1) p foo
nil

In this example, it’d be interesting to move to the next line and inspect the value of foo again. You can do that by pressing n:

(rdb:1) n # goes to next line
(rdb:1) p foo
nil

You now know that the original value of foo was nil, and that it still was nil after calling get_foo.

Other useful commands for navigating through your code are:

c

Runs the program until it either exists or encounters another breakpoint. You usually press c when you are finished debugging your program and want to resume its execution.

s

Steps into method definition. In the previous example, s would take you inside the method definition of get_foo.

r

Restart the program.

q

Quit the program.

Inspecting variables

You can use the debugger to easily inspect both local and global variables. We’ve seen how to inspect local variables before:

(rdb:1) p my_arg
42

You can also pretty print the result of variables or expressions:

(rdb:1) pp %w{a very long long array containing many words}
["a",
 "very",
 "long",
 ...
]

You can list all local variables with v l:

(rdb:1) v l
  foo => "hello"

Similarly, you can show all global variables with v g:

(rdb:1) v g
  all global variables

Finally, you can omit p if you simply want to evaluate a variable or expression

(rdb:1) 5**2
25

Going beyond basics

Ruby Debug provides more advanced functionalities like switching between threads, setting breakpoints and watch expressions, and more. The full list of commands is available at any time by pressing h.

Staying out of trouble

Make sure you remove every instance of require ‘debug’ before shipping your code. Failing to do so may result in your program hanging unpredictably.

Debug is not available in safe mode.

Defined Under Namespace

Classes: Context

Constant Summary collapse

MUTEX =

:nodoc:

Mutex.new

Class Method Summary collapse

Class Method Details

.break_pointsObject

Returns the list of break points where execution will be stopped.

See DEBUGGER__ for more usage



920
921
922
# File 'lib/debug.rb', line 920

def break_points
  @break_points
end

.context(thread = Thread.current) ⇒ Object



974
975
976
977
978
979
980
# File 'lib/debug.rb', line 974

def context(thread=Thread.current)
  c = thread[:__debugger_data__]
  unless c
    thread[:__debugger_data__] = c = Context.new
  end
  c
end

.debug_thread_info(input, binding) ⇒ Object



1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
# File 'lib/debug.rb', line 1030

def debug_thread_info(input, binding)
  case input
  when /^l(?:ist)?/
    make_thread_list
    thread_list_all

  when /^c(?:ur(?:rent)?)?$/
    make_thread_list
    thread_list(@thread_list[Thread.current])

  when /^(?:sw(?:itch)?\s+)?(\d+)/
    make_thread_list
    th = get_thread($1.to_i)
    if th == Thread.current
      @stdout.print "It's the current thread.\n"
    else
      thread_list(@thread_list[th])
      context(th).stop_next
      th.run
      return :cont
    end

  when /^stop\s+(\d+)/
    make_thread_list
    th = get_thread($1.to_i)
    if th == Thread.current
      @stdout.print "It's the current thread.\n"
    elsif th.stop?
      @stdout.print "Already stopped.\n"
    else
      thread_list(@thread_list[th])
      context(th).suspend
    end

  when /^resume\s+(\d+)/
    make_thread_list
    th = get_thread($1.to_i)
    if th == Thread.current
      @stdout.print "It's the current thread.\n"
    elsif !th.stop?
      @stdout.print "Already running."
    else
      thread_list(@thread_list[th])
      th.run
    end
  end
end

.displayObject

Returns the display expression list

See DEBUGGER__ for more usage



913
914
915
# File 'lib/debug.rb', line 913

def display
  @display
end

.get_thread(num) ⇒ Object



986
987
988
989
990
991
992
993
# File 'lib/debug.rb', line 986

def get_thread(num)
  th = @thread_list.key(num)
  unless th
    @stdout.print "No thread ##{num}\n"
    throw :debug_error
  end
  th
end

.interruptObject



982
983
984
# File 'lib/debug.rb', line 982

def interrupt
  context(@last_thread).stop_next
end

.make_thread_listObject



1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
# File 'lib/debug.rb', line 1017

def make_thread_list
  hash = {}
  for th in Thread::list
    if @thread_list.key? th
      hash[th] = @thread_list[th]
    else
      @max_thread += 1
      hash[th] = @max_thread
    end
  end
  @thread_list = hash
end

.resumeObject



958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
# File 'lib/debug.rb', line 958

def resume
  MUTEX.synchronize do
    make_thread_list
    @thread_list.each do |th,|
      next if th == Thread.current
      context(th).clear_suspend
    end
    waiting.each do |th|
      th.run
    end
    waiting.clear
  end
  # Schedule other threads to restart as soon as possible.
  Thread.pass
end

.set_last_thread(th) ⇒ Object



942
943
944
# File 'lib/debug.rb', line 942

def set_last_thread(th)
  @last_thread = th
end

.set_trace(arg) ⇒ Object



932
933
934
935
936
937
938
939
940
# File 'lib/debug.rb', line 932

def set_trace( arg )
  MUTEX.synchronize do
    make_thread_list
    for th, in @thread_list
      context(th).set_trace arg
    end
  end
  arg
end

.stdoutObject

Returns the IO used as stdout. Defaults to STDOUT



901
902
903
# File 'lib/debug.rb', line 901

def stdout
  @stdout
end

.stdout=(s) ⇒ Object

Sets the IO used as stdout. Defaults to STDOUT



906
907
908
# File 'lib/debug.rb', line 906

def stdout=(s)
  @stdout = s
end

.suspendObject



946
947
948
949
950
951
952
953
954
955
956
# File 'lib/debug.rb', line 946

def suspend
  MUTEX.synchronize do
    make_thread_list
    for th, in @thread_list
      next if th == Thread.current
      context(th).set_suspend
    end
  end
  # Schedule other threads to suspend as soon as possible.
  Thread.pass
end

.thread_list(num) ⇒ Object



995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
# File 'lib/debug.rb', line 995

def thread_list(num)
  th = get_thread(num)
  if th == Thread.current
    @stdout.print "+"
  else
    @stdout.print " "
  end
  @stdout.printf "%d ", num
  @stdout.print th.inspect, "\t"
  file = context(th).instance_eval{@file}
  if file
    @stdout.print file,":",context(th).instance_eval{@line}
  end
  @stdout.print "\n"
end

.thread_list_allObject



1011
1012
1013
1014
1015
# File 'lib/debug.rb', line 1011

def thread_list_all
  for th in @thread_list.values.sort
    thread_list(th)
  end
end

.waitingObject

Returns the list of waiting threads.

When stepping through the traces of a function, thread gets suspended, to be resumed later.



928
929
930
# File 'lib/debug.rb', line 928

def waiting
  @waiting
end