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"
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
The following is a list of common functionalities that the debugger provides.
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.
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
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
.
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.
Returns the list of break points where execution will be stopped.
See DEBUGGER__
for more usage
# File debug.rb, line 921 def break_points @break_points end
# File debug.rb, line 975 def context(thread=Thread.current) c = thread[:__debugger_data__] unless c thread[:__debugger_data__] = c = Context.new end c end
# File 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
Returns the display expression list
See DEBUGGER__
for more usage
# File debug.rb, line 914 def display @display end
# File 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
# File debug.rb, line 983 def interrupt context(@last_thread).stop_next end
# File 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
# File 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
# File debug.rb, line 943 def set_last_thread(th) @last_thread = th end
# File 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
Returns the IO used as stdout. Defaults to STDOUT
# File debug.rb, line 902 def stdout @stdout end
Sets the IO used as stdout. Defaults to STDOUT
# File debug.rb, line 907 def stdout=(s) @stdout = s end
# File 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
# File 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
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.
# File debug.rb, line 1035 def thread_list_all for th in @thread_list.values.sort thread_list(th) end end