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



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

def break_points
  @break_points
end

.context(thread = Thread.current) ⇒ Object



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

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



1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
# File 'lib/debug.rb', line 1054

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



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

def display
  @display
end

.get_thread(num) ⇒ Object



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

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

.interruptObject



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

def interrupt
  context(@last_thread).stop_next
end

.make_thread_listObject



1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
# File 'lib/debug.rb', line 1041

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



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

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



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

def set_last_thread(th)
  @last_thread = th
end

.set_trace(arg) ⇒ Object



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

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



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

def stdout
  @stdout
end

.stdout=(s) ⇒ Object

Sets the IO used as stdout. Defaults to STDOUT



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

def stdout=(s)
  @stdout = s
end

.suspendObject



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

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



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

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

Prints all threads in @thread_list to @stdout. Returns a sorted array of values from the @thread_list hash.

While in the debugger you can list all of the threads with: DEBUGGER__.thread_list_all

(rdb:1) DEBUGGER__.thread_list_all
+1 #<Thread:0x007fb2320c03f0 run> debug_me.rb.rb:3
 2 #<Thread:0x007fb23218a538@debug_me.rb.rb:3 sleep>
 3 #<Thread:0x007fb23218b0f0@debug_me.rb.rb:3 sleep>
[1, 2, 3]

Your current thread is indicated by a +

Additionally you can list all threads with th l

(rdb:1) th l
 +1 #<Thread:0x007f99328c0410 run>  debug_me.rb:3
  2 #<Thread:0x007f9932938230@debug_me.rb:3 sleep> debug_me.rb:3
  3 #<Thread:0x007f9932938e10@debug_me.rb:3 sleep> debug_me.rb:3

See DEBUGGER__ for more usage.



1035
1036
1037
1038
1039
# File 'lib/debug.rb', line 1035

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.



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

def waiting
  @waiting
end