This is a singleton class allows controlling the debugger. Use it to start/stop debugger, set/remove breakpoints, etc.
Default options to ::start
interface modules provide handler object
if true, checks the modification time of source files and
reloads if it was modified
Adds a new breakpoint. source is a name of a file or a class.
pos is a line number or a method name if source is a
class name. condition is a string which is evaluated to
true when this breakpoint is activated.
static VALUE
debug_add_breakpoint(int argc, VALUE *argv, VALUE self)
{
VALUE result;
debug_check_started();
result = create_breakpoint_from_args(argc, argv, ++bkp_count);
rb_ary_push(rdebug_breakpoints, result);
return result;
}
Sets catchpoint. Returns the string passed.
VALUE
rdebug_add_catchpoint(VALUE self, VALUE value)
{
debug_check_started();
if (TYPE(value) != T_STRING) {
rb_raise(rb_eTypeError, "value of a catchpoint must be String");
}
rb_hash_aset(rdebug_catchpoints, rb_str_dup(value), INT2FIX(0));
return value;
}
Returns an array of breakpoints.
static VALUE
debug_breakpoints(VALUE self)
{
debug_check_started();
return rdebug_breakpoints;
}
Returns a current catchpoints, which is a hash exception names that will trigger a debugger when raised. The values are the number of times taht catchpoint was hit, initially 0.
VALUE
debug_catchpoints(VALUE self)
{
debug_check_started();
return rdebug_catchpoints;
}
Returns an array of all contexts.
static VALUE
debug_contexts(VALUE self)
{
volatile VALUE list;
volatile VALUE new_list;
VALUE thread, context;
threads_table_t *threads_table;
debug_context_t *debug_context;
int i;
debug_check_started();
new_list = rb_ary_new();
list = rb_funcall(rb_cThread, idList, 0);
for(i = 0; i < RARRAY_LEN(list); i++)
{
thread = rb_ary_entry(list, i);
thread_context_lookup(thread, &context, NULL, 1);
rb_ary_push(new_list, context);
}
threads_table_clear(rdebug_threads_tbl);
Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
for(i = 0; i < RARRAY_LEN(new_list); i++)
{
context = rb_ary_entry(new_list, i);
Data_Get_Struct(context, debug_context_t, debug_context);
st_insert(threads_table->tbl, debug_context->thread_id, context);
}
return new_list;
}
Returns current context. Note: ::current_context.thread == Thread.current
static VALUE
debug_current_context(VALUE self)
{
VALUE thread, context;
debug_check_started();
thread = rb_thread_current();
thread_context_lookup(thread, &context, NULL, 1);
return context;
}
Register at_exit hook which is escaped from the debugger. FOR
INTERNAL USE ONLY.
static VALUE
debug_at_exit(VALUE self)
{
VALUE proc;
if (!rb_block_given_p())
rb_raise(rb_eArgError, "called without a block");
proc = rb_block_proc();
rb_set_end_proc(debug_at_exit_i, proc);
return proc;
}
Same as Kernel#load but resets current context's frames. stop
parameter forces the debugger to stop at the first line of code in the
file increment_start determines if start_count
should be incremented. When
control threads are used, they have to be set up before loading the debugger; so here +increment_start+ will be false.
FOR INTERNAL USE ONLY.
static VALUE
debug_debug_load(int argc, VALUE *argv, VALUE self)
{
VALUE file, stop, context, increment_start;
debug_context_t *debug_context;
int state = 0;
if(rb_scan_args(argc, argv, "12", &file, &stop, &increment_start) == 1)
{
stop = Qfalse;
increment_start = Qtrue;
}
debug_start(self);
if (Qfalse == increment_start) start_count--;
context = debug_current_context(self);
Data_Get_Struct(context, debug_context_t, debug_context);
debug_context->stack_size = 0;
if(RTEST(stop))
debug_context->stop_next = 1;
/* Initializing $0 to the script's path */
ruby_script(RSTRING_PTR(file));
rb_load_protect(file, 0, &state);
if (0 != state)
{
VALUE errinfo = rb_errinfo();
debug_suspend(self);
reset_stepping_stop_points(debug_context);
rb_set_errinfo(Qnil);
return errinfo;
}
/* We should run all at_exit handler's in order to provide,
* for instance, a chance to run all defined test cases */
rb_exec_end_proc();
/* We could have issued a Debugger.stop inside the debug
session. */
if (start_count > 0)
debug_stop(self);
return Qnil;
}
# File lib/ruby-debug-base.rb, line 149 def handle_post_mortem(exp) return if !exp || !exp.__debug_context || exp.__debug_context.stack_size == 0 Debugger.suspend orig_tracing = Debugger.tracing, Debugger.current_context.tracing Debugger.tracing = Debugger.current_context.tracing = false Debugger.last_exception = exp handler.at_line(exp.__debug_context, exp.__debug_file, exp.__debug_line) ensure Debugger.tracing, Debugger.current_context.tracing = orig_tracing Debugger.resume end
Interrupts the current thread
# File lib/ruby-debug-base.rb, line 79 def interrupt current_context.interrupt end
Interrupts the last debugged thread
# File lib/ruby-debug-base.rb, line 86 def interrupt_last if context = last_context return nil unless context.thread.alive? context.interrupt end context end
Setting to true will make the debugger create frame bindings.
static VALUE
debug_set_keep_frame_binding(VALUE self, VALUE value)
{
keep_frame_binding = RTEST(value) ? Qtrue : Qfalse;
return value;
}
Returns true if the debugger will collect frame bindings.
static VALUE
debug_keep_frame_binding(VALUE self)
{
return keep_frame_binding;
}
Returns last debugged context.
static VALUE
debug_last_interrupted(VALUE self)
{
VALUE result = Qnil;
threads_table_t *threads_table;
debug_check_started();
Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
st_foreach(threads_table->tbl, find_last_context_func, (st_data_t)&result);
return result;
}
Activates the post-mortem mode. There are two ways of using it:
By calling ::post_mortem method without a block, you install at_exit hook that intercepts any unhandled by your script exceptions and enables post-mortem mode.
If you know that a particular block of code raises an exception you can enable post-mortem mode by wrapping this block with ::post_mortem, e.g.
def offender raise 'error' end Debugger.post_mortem do ... offender ... end
# File lib/ruby-debug-base.rb, line 128 def post_mortem if block_given? old_post_mortem = self.post_mortem? begin self.post_mortem = true yield rescue Exception => exp handle_post_mortem(exp) raise ensure self.post_mortem = old_post_mortem end else return if post_mortem? self.post_mortem = true debug_at_exit do handle_post_mortem($!) if $! && post_mortem? end end end
Sets post-moterm flag. FOR INTERNAL USE ONLY.
static VALUE
debug_set_post_mortem(VALUE self, VALUE value)
{
debug_check_started();
post_mortem = RTEST(value) ? Qtrue : Qfalse;
return value;
}
Returns true if post-moterm debugging is enabled.
static VALUE
debug_post_mortem(VALUE self)
{
return post_mortem;
}
Removes breakpoint by its id. id is an identificator of a breakpoint.
VALUE
rdebug_remove_breakpoint(VALUE self, VALUE id_value)
{
int i;
int id;
VALUE breakpoint;
debug_breakpoint_t *debug_breakpoint;
id = FIX2INT(id_value);
for( i = 0; i < RARRAY_LEN(rdebug_breakpoints); i += 1 )
{
breakpoint = rb_ary_entry(rdebug_breakpoints, i);
Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
if(debug_breakpoint->id == id)
{
rb_ary_delete_at(rdebug_breakpoints, i);
return breakpoint;
}
}
return Qnil;
}
Resumes all contexts.
static VALUE
debug_resume(VALUE self)
{
VALUE current, context;
VALUE context_list;
debug_context_t *debug_context;
int i;
debug_check_started();
context_list = debug_contexts(self);
thread_context_lookup(rb_thread_current(), ¤t, NULL, 1);
for(i = 0; i < RARRAY_LEN(context_list); i++)
{
context = rb_ary_entry(context_list, i);
if(current == context)
continue;
Data_Get_Struct(context, debug_context_t, debug_context);
context_resume_0(debug_context);
}
rb_thread_schedule();
return self;
}
The code inside of the block is escaped from the debugger.
static VALUE
debug_skip(VALUE self)
{
if (!rb_block_given_p()) {
rb_raise(rb_eArgError, "called without a block");
}
if(!IS_STARTED)
return rb_yield(Qnil);
set_current_skipped_status(Qtrue);
return rb_ensure(rb_yield, Qnil, set_current_skipped_status, Qfalse);
}
# File lib/ruby-debug-base.rb, line 94 def source_reload LineCache::clear_file_cache end
::start -> bool ::start { ... } -> obj
If it's called without a block it returns true, unless
debugger was already started. If a block is given, it starts debugger and
yields to block. When the block is finished executing it stops the debugger
with ::stop method.
If a block is given, it starts debugger and yields to block. When the block is finished executing it stops the debugger with ::stop method. Inside the block you will probably want to have a call to Debugger.debugger. For example:
Debugger.start{debugger; foo} # Stop inside of foo
Also, ruby-debug only allows one invocation of debugger at a time; nested ::start's have no effect and you can't use this inside the debugger itself.
Note that if you want to stop debugger, you must call ::stop as many time as you called ::start method.
options is a hash used to set various debugging options. Set
:init true if you want to save ARGV and some variables which make a
debugger restart possible. Only the first time :init is set true will
values get set. Since ARGV is saved, you should make sure it hasn't been
changed before the (first) call. Set :::post_mortem true if you
want to enter post-mortem debugging on an uncaught exception. Once
post-mortem debugging is set, it can't be unset.
# File lib/ruby-debug-base.rb, line 201 def start(options={}, &block) options = Debugger::DEFAULT_START_SETTINGS.merge(options) if options[:init] Debugger.const_set('ARGV', ARGV.clone) unless defined? Debugger::ARGV Debugger.const_set('PROG_SCRIPT', $0) unless defined? Debugger::PROG_SCRIPT Debugger.const_set('INITIAL_DIR', Dir.pwd) unless defined? Debugger::INITIAL_DIR end Debugger.tracing = options[:tracing] unless options[:tracing].nil? retval = Debugger.started? ? block && block.call(self) : Debugger.start_(&block) if options[:post_mortem] post_mortem end return retval end
This method is internal and activates the debugger. Use ::start (from
lib/ruby-debug-base.rb) instead.
The return value is the value of !::started? before
issuing the start; That is, true is returned,
unless debugger was previously started.
If a block is given, it starts debugger and yields to block. When the block is finished executing it stops the debugger with ::stop method. Inside the block you will probably want to have a call to Debugger.debugger. For example:
Debugger.start{debugger; foo} # Stop inside of foo
Also, ruby-debug only allows one invocation of debugger at a time; nested ::start's have no effect and you can't use this inside the debugger itself.
Note that if you want to completely remove the debugger hook, you must call ::stop as many times as you called ::start method.
static VALUE
debug_start(VALUE self)
{
VALUE result;
start_count++;
if(IS_STARTED)
result = Qfalse;
else
{
locker = Qnil;
rdebug_breakpoints = rb_ary_new();
rdebug_catchpoints = rb_hash_new();
rdebug_threads_tbl = threads_table_create();
rb_add_event_hook(debug_event_hook, RUBY_EVENT_ALL, Qnil);
result = Qtrue;
}
if(rb_block_given_p())
rb_ensure(rb_yield, self, debug_stop_i, self);
return result;
}
Returns true the debugger is started.
static VALUE
debug_is_started(VALUE self)
{
return IS_STARTED ? Qtrue : Qfalse;
}
This method disables the debugger. It returns true if the
debugger is disabled, otherwise it returns false.
Note that if you want to complete remove the debugger hook, you must call ::stop as many times as you called ::start method.
static VALUE
debug_stop(VALUE self)
{
debug_check_started();
start_count--;
if(start_count)
return Qfalse;
rb_remove_event_hook(debug_event_hook);
locker = Qnil;
rdebug_breakpoints = Qnil;
rdebug_threads_tbl = Qnil;
return Qtrue;
}
Suspends all contexts.
static VALUE
debug_suspend(VALUE self)
{
VALUE current, context;
VALUE context_list;
debug_context_t *debug_context;
int i;
debug_check_started();
context_list = debug_contexts(self);
thread_context_lookup(rb_thread_current(), ¤t, NULL, 1);
for(i = 0; i < RARRAY_LEN(context_list); i++)
{
context = rb_ary_entry(context_list, i);
if(current == context)
continue;
Data_Get_Struct(context, debug_context_t, debug_context);
context_suspend_0(debug_context);
}
return self;
}
Returns context of the thread passed as an argument.
static VALUE
debug_thread_context(VALUE self, VALUE thread)
{
VALUE context;
debug_check_started();
thread_context_lookup(thread, &context, NULL, 1);
return context;
}
Returns true if the global tracing is activated.
static VALUE
debug_tracing(VALUE self)
{
return tracing;
}
Sets the global tracing flag.
static VALUE
debug_set_tracing(VALUE self, VALUE value)
{
tracing = RTEST(value) ? Qtrue : Qfalse;
return value;
}
Setting to true will make the debugger save argument info on
calls.
static VALUE
debug_set_track_frame_args(VALUE self, VALUE value)
{
track_frame_args = RTEST(value) ? Qtrue : Qfalse;
return value;
}
Returns true if the debugger track frame argument values on
calls.
static VALUE
debug_track_frame_args(VALUE self)
{
return track_frame_args;
}