# frozen_string_literal: true
# Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
# Copyright (C) 2000 Information-technology Promotion Agency, Japan
# Copyright (C) 2000-2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>
STDERR.print "-r debug.rb is not available in safe mode\n"
def Tracer.trace_func(*vars)
SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__ # :nodoc:
# This library provides debugging functionality to Ruby.
# To add a debugger to your code, start by requiring +debug+ in your
# This will cause Ruby to interrupt execution and show a prompt when the +say+
# Once you're inside the prompt, you can start debugging your program.
# You can get help at any time by pressing +h+.
# Debugger help v.-0.002b
# 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
# 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
# <everything else> evaluate
# The following is a list of common functionalities that the debugger
# === 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
# Let's look at an example:
# foo = get_foo if foo.nil?
# When you run this program, the debugger will kick in just before the
# 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
# 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:
# 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.
# Steps into method definition. In the previous example, +s+ would take you
# inside the method definition of +get_foo+.
# === Inspecting variables
# You can use the debugger to easily inspect both local and global variables.
# We've seen how to inspect local variables before:
# You can also pretty print the result of variables or expressions:
# (rdb:1) pp %w{a very long long array containing many words}
# You can list all local variables with +v l+:
# Similarly, you can show all global variables with +v g+:
# Finally, you can omit +p+ if you simply want to evaluate a variable or
# === 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
# Debug is not available in safe mode.
MUTEX = Thread::Mutex.new # :nodoc:
CONTINUATIONS_SUPPORTED = RUBY_ENGINE == 'ruby'
require 'continuation' if CONTINUATIONS_SUPPORTED
def readline(prompt, hist)
Readline::readline(prompt, hist)
def readline(prompt, hist)
if Thread.current == Thread.main
while MUTEX.synchronize {
DEBUGGER__.waiting.push Thread.current
DEBUGGER__.set_trace(arg)
DEBUGGER__.set_last_thread(th)
def debug_eval(str, binding)
rescue StandardError, ScriptError => e
at = eval("caller(1)", binding)
stdout.printf "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '')
stdout.printf "\tfrom %s\n", i
def debug_silent_eval(str, binding)
rescue StandardError, ScriptError
def var_list(ary, binding)
stdout.printf " %s => %s\n", v, eval(v.to_s, binding).inspect
def debug_variable_info(input, binding)
when /^\s*g(?:lobal)?\s*$/
var_list(global_variables, binding)
when /^\s*l(?:ocal)?\s*$/
var_list(eval("local_variables", binding), binding)
when /^\s*i(?:nstance)?\s+/
obj = debug_eval($', binding)
var_list(obj.instance_variables, obj.instance_eval{binding()})
when /^\s*c(?:onst(?:ant)?)?\s+/
obj = debug_eval($', binding)
unless obj.kind_of? Module
stdout.print "Should be Class/Module: ", $', "\n"
var_list(obj.constants, obj.module_eval{binding()})
def debug_method_info(input, binding)
obj = debug_eval($', binding)
for v in obj.methods.sort
obj = debug_eval(input, binding)
unless obj.kind_of? Module
stdout.print "Should be Class/Module: ", input, "\n"
for v in obj.instance_methods(false).sort
num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
DEBUGGER__.make_thread_list
num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
def debug_command(file, line, id, binding)
if CONTINUATIONS_SUPPORTED
unless defined?($debugger_restart) and $debugger_restart
callcc{|c| $debugger_restart = c}
set_last_thread(Thread.current)
stdout.printf "\032\032%s:%d:\n", binding_file, binding_line
stdout.printf "%s:%d:%s", binding_file, binding_line,
line_at(binding_file, binding_line)
@frames[0] = [binding, file, line, id]
display_expressions(binding)
while prompt and input = readline("(rdb:%d) "%thnum(), true)
next unless DEBUG_LAST_CMD[0]
input = DEBUG_LAST_CMD[0]
DEBUG_LAST_CMD[0] = input
when /^\s*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/
stdout.print "Trace on.\n"
stdout.print "Trace off.\n"
when /^\s*b(?:reak)?\s+(?:(.+):)?([^.:]+)$/
klass = debug_silent_eval($1, binding)
file = File.expand_path($1)
pname = pos = pos.intern.id2name
break_points.push [true, 0, klass || file, pos]
stdout.printf "Set breakpoint %d at %s:%s\n", break_points.size, klass || file, pname
when /^\s*b(?:reak)?\s+(.+)[#.]([^.:]+)$/
klass = debug_eval($1, binding)
break_points.push [true, 0, klass, pos]
stdout.printf "Set breakpoint %d at %s.%s\n", break_points.size, klass, pos
when /^\s*wat(?:ch)?\s+(.+)$/
break_points.push [true, 1, exp]
stdout.printf "Set watchpoint %d:%s\n", break_points.size, exp
if break_points.find{|b| b[1] == 0}
stdout.print "Breakpoints:\n"
stdout.printf " %d %s:%s\n", n, b[2], b[3]
if break_points.find{|b| b[1] == 1}
stdout.print "Watchpoints:\n"
stdout.printf " %d %s\n", n, b[2]
if break_points.size == 0
stdout.print "No breakpoints\n"
when /^\s*del(?:ete)?(?:\s+(\d+))?$/
input = readline("Clear all breakpoints? (y/n) ", false)
break_points[pos-1][0] = false