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; }