module Debugger

Summary

This is a singleton class allows controlling the debugger. Use it to start/stop debugger, set/remove breakpoints, etc.

Constants

DEFAULT_START_SETTINGS

Default options to ::start

VERSION

Attributes

handler[RW]

interface modules provide handler object

last_exception[RW]
reload_source_on_change[RW]

if true, checks the modification time of source files and reloads if it was modified

Public Class Methods

add_breakpoint(source, pos, condition = nil) → breakpoint click to toggle source

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;
}
catchpoint(string) → string click to toggle source

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;
}
breakpoints → array click to toggle source

Returns an array of breakpoints.

static VALUE
debug_breakpoints(VALUE self)
{
    debug_check_started();

    return rdebug_breakpoints;
}
catchpoints → hash click to toggle source

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;
}
contexts → array click to toggle source

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;
}
current_context → context click to toggle source

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;
}
debug_at_exit { block } → proc click to toggle source

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;
}
debug_load(file, stop = false, increment_start = false) → nil click to toggle source

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;
}
handle_post_mortem(exp) click to toggle source
# 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
interrupt() click to toggle source

Interrupts the current thread

# File lib/ruby-debug-base.rb, line 79
def interrupt
  current_context.interrupt
end
interrupt_last() click to toggle source

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
keep_frame_binding = bool click to toggle source

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;
}
keep_frame_binding? → bool click to toggle source

Returns true if the debugger will collect frame bindings.

static VALUE
debug_keep_frame_binding(VALUE self)
{
    return keep_frame_binding;
}
last_interrupted → context click to toggle source

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;
}
post_mortem() { || ... } click to toggle source

Activates the post-mortem mode. There are two ways of using it:

Global post-mortem mode

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.

Local 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
post_mortem = bool click to toggle source

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;
}
post_mortem? → bool click to toggle source

Returns true if post-moterm debugging is enabled.

static VALUE
debug_post_mortem(VALUE self)
{
    return post_mortem;
}
remove_breakpoint(id) → breakpoint click to toggle source

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;
}
resume → Debugger click to toggle source

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(), &current, 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;
}
skip { block } → obj or nil click to toggle source

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);
}
source_reload() click to toggle source
# File lib/ruby-debug-base.rb, line 94
def source_reload
  LineCache::clear_file_cache
end
start(options={}, &block) click to toggle source

::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
start_ → bool click to toggle source
start_ { ... } → bool

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;
}
started? → bool click to toggle source

Returns true the debugger is started.

static VALUE
debug_is_started(VALUE self)
{
    return IS_STARTED ? Qtrue : Qfalse;
}
stop → bool click to toggle source

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;
}
suspend → Debugger click to toggle source

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(), &current, 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;
}
thread_context(thread) → context click to toggle source

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;
}
tracing → bool click to toggle source

Returns true if the global tracing is activated.

static VALUE
debug_tracing(VALUE self)
{
    return tracing;
}
tracing = bool click to toggle source

Sets the global tracing flag.

static VALUE
debug_set_tracing(VALUE self, VALUE value)
{
    tracing = RTEST(value) ? Qtrue : Qfalse;
    return value;
}
track_frame_args = bool click to toggle source

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;
}
track_fame_args? → bool click to toggle source

Returns true if the debugger track frame argument values on calls.

static VALUE
debug_track_frame_args(VALUE self)
{
    return track_frame_args;
}