ext/ruby_debug.c in ruby-debug-base-0.10.0-mswin32 vs ext/ruby_debug.c in ruby-debug-base-0.10.1
- old
+ new
@@ -1,14 +1,16 @@
+#include "ruby_debug.h"
+
#include <stdio.h>
-#include <ruby.h>
#include <node.h>
#include <rubysig.h>
#include <st.h>
#include <version.h>
-#define DEBUG_VERSION "0.10.0"
+#define DEBUG_VERSION "0.10.1"
+
#ifdef _WIN32
struct FRAME {
VALUE self;
int argc;
ID last_func;
@@ -41,103 +43,23 @@
RUBY_EXTERN struct RVarmap *ruby_dyna_vars;
#else
#include <env.h>
#endif
-#define CTX_FL_SUSPEND (1<<1)
-#define CTX_FL_TRACING (1<<2)
-#define CTX_FL_SKIPPED (1<<3)
-#define CTX_FL_IGNORE (1<<4)
-#define CTX_FL_DEAD (1<<5)
-#define CTX_FL_WAS_RUNNING (1<<6)
-#define CTX_FL_ENABLE_BKPT (1<<7)
-#define CTX_FL_STEPPED (1<<8)
-#define CTX_FL_FORCE_MOVE (1<<9)
-
-#define CTX_FL_TEST(c,f) ((c)->flags & (f))
-#define CTX_FL_SET(c,f) do { (c)->flags |= (f); } while (0)
-#define CTX_FL_UNSET(c,f) do { (c)->flags &= ~(f); } while (0)
-
-#define IS_STARTED (threads_tbl != Qnil)
#define FRAME_N(n) (&debug_context->frames[debug_context->stack_size-(n)-1])
#define GET_FRAME (FRAME_N(check_frame_number(debug_context, frame)))
#ifndef min
#define min(x,y) ((x) < (y) ? (x) : (y))
#endif
#define STACK_SIZE_INCREMENT 128
typedef struct {
- int argc; /* Number of arguments a frame should have. */
- VALUE binding;
- ID id;
- ID orig_id;
- int line;
- const char * file;
- short dead;
- VALUE self;
- VALUE arg_ary;
- union {
- struct {
- struct FRAME *frame;
- struct SCOPE *scope;
- struct RVarmap *dyna_vars;
- } runtime;
- struct {
- VALUE args;
- VALUE locals;
- VALUE arg_ary;
- } copy;
- } info;
-} debug_frame_t;
-
-enum ctx_stop_reason {CTX_STOP_NONE, CTX_STOP_STEP, CTX_STOP_BREAKPOINT, CTX_STOP_CATCHPOINT};
-
-typedef struct {
- VALUE thread_id;
- int thnum;
- int flags;
- enum ctx_stop_reason stop_reason;
- int stop_next;
- int dest_frame;
- int stop_line;
- int stop_frame;
- int stack_size;
- int stack_len;
- debug_frame_t *frames;
- const char * last_file;
- int last_line;
- VALUE breakpoint;
-} debug_context_t;
-
-enum bp_type {BP_POS_TYPE, BP_METHOD_TYPE};
-enum hit_condition {HIT_COND_NONE, HIT_COND_GE, HIT_COND_EQ, HIT_COND_MOD};
-
-typedef struct {
- int id;
- enum bp_type type;
- VALUE source;
- union
- {
- int line;
- ID mid;
- } pos;
- VALUE expr;
- VALUE enabled;
- int hit_count;
- int hit_value;
- enum hit_condition hit_condition;
-} debug_breakpoint_t;
-
-typedef struct {
st_table *tbl;
} threads_table_t;
-static VALUE threads_tbl = Qnil;
-static VALUE breakpoints = Qnil;
-static VALUE catchpoint = Qnil;
static VALUE tracing = Qfalse;
static VALUE locker = Qnil;
static VALUE post_mortem = Qfalse;
static VALUE keep_frame_binding = Qfalse;
static VALUE debug = Qfalse;
@@ -145,24 +67,25 @@
static VALUE last_context = Qnil;
static VALUE last_thread = Qnil;
static debug_context_t *last_debug_context = NULL;
-static VALUE mDebugger;
+VALUE rdebug_threads_tbl = Qnil; /* Context for each of the threads */
+VALUE mDebugger; /* Ruby Debugger Module object */
+
static VALUE cThreadsTable;
static VALUE cContext;
-static VALUE cBreakpoint;
static VALUE cDebugThread;
static VALUE rb_mObjectSpace;
-static ID idAtLine;
static ID idAtBreakpoint;
static ID idAtCatchpoint;
+static ID idAtLine;
+static ID idAtReturn;
static ID idAtTracing;
static ID idList;
-static ID idEval;
static int start_count = 0;
static int thnum_max = 0;
static int bkp_count = 0;
static int last_debugged_thnum = -1;
@@ -184,10 +107,21 @@
} locked_thread_t;
static locked_thread_t *locked_head = NULL;
static locked_thread_t *locked_tail = NULL;
+/* "Step", "Next" and "Finish" do their work by saving information
+ about where to stop next. reset_stopping_points removes/resets this
+ information. */
+inline static void
+reset_stepping_stop_points(debug_context_t *debug_context)
+{
+ debug_context->dest_frame = -1;
+ debug_context->stop_line = -1;
+ debug_context->stop_next = -1;
+}
+
inline static VALUE
real_class(VALUE klass)
{
if (klass) {
if (TYPE(klass) == T_ICLASS) {
@@ -377,11 +311,11 @@
static void
check_thread_contexts()
{
threads_table_t *threads_table;
- Data_Get_Struct(threads_tbl, threads_table_t, threads_table);
+ Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
st_foreach(threads_table->tbl, threads_table_check_i, 0);
}
/*
* call-seq:
@@ -394,19 +328,10 @@
{
return IS_STARTED ? Qtrue : Qfalse;
}
static void
-debug_check_started()
-{
- if(!IS_STARTED)
- {
- rb_raise(rb_eRuntimeError, "Debugger.start is not called yet.");
- }
-}
-
-static void
debug_context_mark(void *data)
{
debug_frame_t *frame;
int i;
@@ -503,11 +428,11 @@
if(debug_context)
*debug_context = last_debug_context;
return;
}
thread_id = ref2id(thread);
- Data_Get_Struct(threads_tbl, threads_table_t, threads_table);
+ Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
if(!st_lookup(threads_table->tbl, thread_id, context))
{
*context = debug_context_create(thread);
st_insert(threads_table->tbl, thread_id, *context);
}
@@ -539,10 +464,30 @@
args = rb_ary_new3(3, context, file, line);
return rb_protect(call_at_line_unprotected, args, 0);
}
+static VALUE
+call_at_return_unprotected(VALUE args)
+{
+ VALUE context;
+ context = *RARRAY(args)->ptr;
+ return rb_funcall2(context, idAtReturn, RARRAY(args)->len - 1, RARRAY(args)->ptr + 1);
+}
+
+static VALUE
+call_at_return(VALUE context, debug_context_t *debug_context, VALUE file, VALUE line)
+{
+ VALUE args;
+
+ last_debugged_thnum = debug_context->thnum;
+ save_current_position(debug_context);
+
+ args = rb_ary_new3(3, context, file, line);
+ return rb_protect(call_at_return_unprotected, args, 0);
+}
+
static void
save_call_frame(rb_event_t event, VALUE self, char *file, int line, ID mid, debug_context_t *debug_context)
{
VALUE binding;
debug_frame_t *debug_frame;
@@ -577,11 +522,11 @@
#define isdirsep(x) ((x) == '/' || (x) == '\\')
#else
#define isdirsep(x) ((x) == '/')
#endif
-static int
+int
filename_cmp(VALUE source, char *file)
{
char *source_ptr, *file_ptr;
int s_len, f_len, min_len;
int s,f;
@@ -604,137 +549,10 @@
return 0;
}
return 1;
}
-inline static int
-classname_cmp(VALUE name, VALUE klass)
-{
- VALUE class_name = (Qnil == name) ? rb_str_new2("main") : name;
- return (klass != Qnil
- && rb_str_cmp(class_name, rb_mod_name(klass)) == 0);
-}
-
-static int
-check_breakpoint_hit_condition(VALUE breakpoint)
-{
- debug_breakpoint_t *debug_breakpoint;
-
- if(breakpoint == Qnil)
- return 0;
- Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
-
- debug_breakpoint->hit_count++;
- if (!Qtrue == debug_breakpoint->enabled) return 0;
- switch(debug_breakpoint->hit_condition)
- {
- case HIT_COND_NONE:
- return 1;
- case HIT_COND_GE:
- {
- if(debug_breakpoint->hit_count >= debug_breakpoint->hit_value)
- return 1;
- break;
- }
- case HIT_COND_EQ:
- {
- if(debug_breakpoint->hit_count == debug_breakpoint->hit_value)
- return 1;
- break;
- }
- case HIT_COND_MOD:
- {
- if(debug_breakpoint->hit_count % debug_breakpoint->hit_value == 0)
- return 1;
- break;
- }
- }
- return 0;
-}
-
-static int
-check_breakpoint_by_pos(VALUE breakpoint, char *file, int line)
-{
- debug_breakpoint_t *debug_breakpoint;
-
- if(breakpoint == Qnil)
- return 0;
- Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
- if (!Qtrue == debug_breakpoint->enabled) return 0;
- if(debug_breakpoint->type != BP_POS_TYPE)
- return 0;
- if(debug_breakpoint->pos.line != line)
- return 0;
- if(filename_cmp(debug_breakpoint->source, file))
- return 1;
- return 0;
-}
-
-static int
-check_breakpoint_by_method(VALUE breakpoint, VALUE klass, ID mid)
-{
- debug_breakpoint_t *debug_breakpoint;
-
- if(breakpoint == Qnil)
- return 0;
- Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
- if (!Qtrue == debug_breakpoint->enabled) return 0;
- if(debug_breakpoint->type != BP_METHOD_TYPE)
- return 0;
- if(debug_breakpoint->pos.mid != mid)
- return 0;
- if(classname_cmp(debug_breakpoint->source, klass))
- return 1;
- return 0;
-}
-
-static VALUE
-check_breakpoints_by_pos(debug_context_t *debug_context, char *file, int line)
-{
- VALUE breakpoint;
- int i;
-
- if(!CTX_FL_TEST(debug_context, CTX_FL_ENABLE_BKPT))
- return Qnil;
-
- if(check_breakpoint_by_pos(debug_context->breakpoint, file, line))
- return debug_context->breakpoint;
-
- if(RARRAY(breakpoints)->len == 0)
- return Qnil;
- for(i = 0; i < RARRAY(breakpoints)->len; i++)
- {
- breakpoint = rb_ary_entry(breakpoints, i);
- if(check_breakpoint_by_pos(breakpoint, file, line))
- return breakpoint;
- }
- return Qnil;
-}
-
-static VALUE
-check_breakpoints_by_method(debug_context_t *debug_context, VALUE klass, ID mid)
-{
- VALUE breakpoint;
- int i;
-
- if(!CTX_FL_TEST(debug_context, CTX_FL_ENABLE_BKPT))
- return Qnil;
-
- if(check_breakpoint_by_method(debug_context->breakpoint, klass, mid))
- return debug_context->breakpoint;
-
- if(RARRAY(breakpoints)->len == 0)
- return Qnil;
- for(i = 0; i < RARRAY(breakpoints)->len; i++)
- {
- breakpoint = rb_ary_entry(breakpoints, i);
- if(check_breakpoint_by_method(breakpoint, klass, mid))
- return breakpoint;
- }
- return Qnil;
-}
-
/*
* This is a NASTY HACK. For some reasons rb_f_binding is declared
* static in eval.c. So we create a cons up call to binding in C.
*/
static VALUE
@@ -748,31 +566,10 @@
f_binding = (bind_func_t)ruby_method_ptr(rb_mKernel, rb_intern("binding"));
}
return f_binding(self);
}
-static VALUE
-eval_expression(VALUE args)
-{
- return rb_funcall2(rb_mKernel, idEval, 2, RARRAY(args)->ptr);
-}
-
-inline static int
-check_breakpoint_expression(VALUE breakpoint, VALUE binding)
-{
- debug_breakpoint_t *debug_breakpoint;
- VALUE args, expr_result;
-
- Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
- if(NIL_P(debug_breakpoint->expr))
- return 1;
-
- args = rb_ary_new3(2, debug_breakpoint->expr, binding);
- expr_result = rb_protect(eval_expression, args, 0);
- return RTEST(expr_result);
-}
-
inline static debug_frame_t *
get_top_frame(debug_context_t *debug_context)
{
if(debug_context->stack_size == 0)
return NULL;
@@ -997,16 +794,13 @@
}
else
debug_context->breakpoint = Qnil;
}
- /* reset all pointers */
- debug_context->dest_frame = -1;
- debug_context->stop_line = -1;
- debug_context->stop_next = -1;
-
- call_at_line(context, debug_context, rb_str_new2(file), INT2FIX(line));
+ reset_stepping_stop_points(debug_context);
+ call_at_line(context, debug_context, rb_str_new2(file),
+ INT2FIX(line));
}
break;
}
case RUBY_EVENT_CALL:
{
@@ -1054,20 +848,26 @@
case RUBY_EVENT_RETURN:
case RUBY_EVENT_END:
{
if(debug_context->stack_size == debug_context->stop_frame)
{
- debug_context->stop_next = 1;
- debug_context->stop_frame = 0;
+ if(debug_context->stack_size == 0)
+ save_call_frame(event, self, file, line, mid, debug_context);
+ else
+ set_frame_source(event, debug_context, self, file, line, mid);
+ binding = self? create_binding(self) : Qnil;
+ save_top_binding(debug_context, binding);
+ call_at_return(context, debug_context, rb_str_new2(file),
+ INT2FIX(line));
+ debug_context->dest_frame = -1;
}
while(debug_context->stack_size > 0)
{
debug_context->stack_size--;
if(debug_context->frames[debug_context->stack_size].orig_id == mid)
break;
}
- CTX_FL_SET(debug_context, CTX_FL_ENABLE_BKPT);
break;
}
case RUBY_EVENT_CLASS:
{
reset_frame_mid(debug_context);
@@ -1090,35 +890,69 @@
rb_ivar_set(ruby_errinfo, rb_intern("@__debug_binding"), binding);
rb_ivar_set(ruby_errinfo, rb_intern("@__debug_context"), debug_context_dup(debug_context));
}
expn_class = rb_obj_class(ruby_errinfo);
+
+ /* This code goes back to the earliest days of ruby-debug. It
+ tends to disallow catching an exception via the
+ "catchpoint" command. To address this one possiblilty is to
+ move this after testing for catchponts. Kent however thinks
+ there may be a misfeature in Ruby's eval.c: the problem was
+ in the fact that Ruby doesn't reset exception flag on the
+ current thread before it calls a notification handler.
+
+ See also the #ifdef'd code below as well.
+ */
+#ifdef NORMAL_CODE
if( !NIL_P(rb_class_inherited_p(expn_class, rb_eSystemExit)) )
{
debug_stop(mDebugger);
break;
}
+#endif
- if(catchpoint == Qnil)
+ if (rdebug_catchpoints == Qnil ||
+ RHASH(rdebug_catchpoints)->tbl->num_entries == 0)
break;
ancestors = rb_mod_ancestors(expn_class);
for(i = 0; i < RARRAY(ancestors)->len; i++)
{
- aclass = rb_ary_entry(ancestors, i);
- if(rb_str_cmp(rb_mod_name(aclass), catchpoint) == 0)
+ VALUE mod_name;
+ VALUE hit_count;
+
+ aclass = rb_ary_entry(ancestors, i);
+ mod_name = rb_mod_name(aclass);
+ hit_count = rb_hash_aref(rdebug_catchpoints, mod_name);
+ if(hit_count != Qnil)
{
+ hit_count = INT2FIX(FIX2INT(rb_hash_aref(rdebug_catchpoints,
+ mod_name)+1));
+ rb_hash_aset(rdebug_catchpoints, mod_name, hit_count);
debug_context->stop_reason = CTX_STOP_CATCHPOINT;
rb_funcall(context, idAtCatchpoint, 1, ruby_errinfo);
if(self && binding == Qnil)
binding = create_binding(self);
save_top_binding(debug_context, binding);
call_at_line(context, debug_context, rb_str_new2(file), INT2FIX(line));
break;
}
}
+ /* If we stop the debugger, we may not be able to trace into
+ code that has an exception handler wrapped around it. So
+ the alternative is to force the user to do his own
+ Debugger.stop. */
+#ifdef NORMAL_CODE_MOVING_AFTER_
+ if( !NIL_P(rb_class_inherited_p(expn_class, rb_eSystemExit)) )
+ {
+ debug_stop(mDebugger);
+ break;
+ }
+#endif
+
break;
}
}
cleanup:
@@ -1148,20 +982,24 @@
return Qnil;
}
/*
* call-seq:
- * Debugger.start -> bool
- * Debugger.start { ... } -> obj
+ * Debugger.start_ -> bool
+ * Debugger.start_ { ... } -> obj
*
- * This method activates the debugger.
- * 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 Debugger.stop method.
+ * This method is internal and activates the debugger. Use
+ * Debugger.start (from ruby-debug-base.rb) instead.
*
- * <i>Note that if you want to stop debugger, you must call Debugger.stop as many time as you
- * called Debugger.start method.</i>
+ * 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 Debugger.stop method.
+ *
+ * <i>Note that if you want to stop debugger, you must call
+ * Debugger.stop as many time as you called Debugger.start
+ * method.</i>
*/
static VALUE
debug_start(VALUE self)
{
VALUE result;
@@ -1169,13 +1007,14 @@
if(IS_STARTED)
result = Qfalse;
else
{
- breakpoints = rb_ary_new();
- locker = Qnil;
- threads_tbl = threads_table_create();
+ 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);
result = Qtrue;
}
@@ -1204,160 +1043,17 @@
if(start_count)
return Qfalse;
rb_remove_event_hook(debug_event_hook);
- locker = Qnil;
- breakpoints = Qnil;
- threads_tbl = Qnil;
+ locker = Qnil;
+ rdebug_breakpoints = Qnil;
+ rdebug_threads_tbl = Qnil;
return Qtrue;
}
-static void
-breakpoint_mark(void *data)
-{
- debug_breakpoint_t *breakpoint;
- breakpoint = (debug_breakpoint_t *)data;
- rb_gc_mark(breakpoint->source);
- rb_gc_mark(breakpoint->expr);
-}
-
-static VALUE
-create_breakpoint_from_args(int argc, VALUE *argv, int id)
-{
- VALUE source, pos, expr;
- debug_breakpoint_t *breakpoint;
- int type;
-
- if(rb_scan_args(argc, argv, "21", &source, &pos, &expr) == 2)
- {
- expr = Qnil;
- }
- type = FIXNUM_P(pos) ? BP_POS_TYPE : BP_METHOD_TYPE;
- if(type == BP_POS_TYPE)
- source = StringValue(source);
- else
- pos = StringValue(pos);
- breakpoint = ALLOC(debug_breakpoint_t);
- breakpoint->id = id;
- breakpoint->source = source;
- breakpoint->type = type;
- if(type == BP_POS_TYPE)
- breakpoint->pos.line = FIX2INT(pos);
- else
- breakpoint->pos.mid = rb_intern(RSTRING(pos)->ptr);
- breakpoint->enabled = Qtrue;
- breakpoint->expr = NIL_P(expr) ? expr: StringValue(expr);
- breakpoint->hit_count = 0;
- breakpoint->hit_value = 0;
- breakpoint->hit_condition = HIT_COND_NONE;
- return Data_Wrap_Struct(cBreakpoint, breakpoint_mark, xfree, breakpoint);
-}
-
-/*
- * call-seq:
- * Debugger.add_breakpoint(source, pos, condition = nil) -> breakpoint
- *
- * Adds a new breakpoint.
- * <i>source</i> is a name of a file or a class.
- * <i>pos</i> is a line number or a method name if <i>source</i> is a class name.
- * <i>condition</i> 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(breakpoints, result);
- return result;
-}
-
-/*
- * call-seq:
- * Debugger.remove_breakpoint(id) -> breakpoint
- *
- * Removes breakpoint by its id.
- * <i>id</i> is an identificator of a breakpoint.
- */
-static VALUE
-debug_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(breakpoints)->len; i += 1 )
- {
- breakpoint = rb_ary_entry(breakpoints, i);
- Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
- if(debug_breakpoint->id == id)
- {
- rb_ary_delete_at(breakpoints, i);
- return breakpoint;
- }
- }
- return Qnil;
-}
-
-/*
- * call-seq:
- * Debugger.breakpoints -> array
- *
- * Returns an array of breakpoints.
- */
-static VALUE
-debug_breakpoints(VALUE self)
-{
- debug_check_started();
-
- return breakpoints;
-}
-
-/*
- * call-seq:
- * Debugger.checkpoint -> string
- *
- * Returns a current checkpoint, which is a name of exception that will
- * trigger a debugger when raised.
- */
-static VALUE
-debug_catchpoint(VALUE self)
-{
- debug_check_started();
-
- return catchpoint;
-}
-
-/*
- * call-seq:
- * Debugger.checkpoint = string -> string
- *
- * Sets checkpoint.
- */
-static VALUE
-debug_set_catchpoint(VALUE self, VALUE value)
-{
- debug_check_started();
-
- if (!NIL_P(value) && TYPE(value) != T_STRING) {
- rb_raise(rb_eTypeError, "value of checkpoint must be String");
- }
- if(NIL_P(value))
- catchpoint = Qnil;
- else
- catchpoint = rb_str_dup(value);
- return value;
-}
-
static int
find_last_context_func(VALUE key, VALUE value, VALUE *result)
{
debug_context_t *debug_context;
Data_Get_Struct(value, debug_context_t, debug_context);
@@ -1381,11 +1077,11 @@
VALUE result = Qnil;
threads_table_t *threads_table;
debug_check_started();
- Data_Get_Struct(threads_tbl, threads_table_t, threads_table);
+ 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;
}
@@ -1449,12 +1145,12 @@
{
thread = rb_ary_entry(list, i);
thread_context_lookup(thread, &context, NULL);
rb_ary_push(new_list, context);
}
- threads_table_clear(threads_tbl);
- Data_Get_Struct(threads_tbl, threads_table_t, threads_table);
+ threads_table_clear(rdebug_threads_tbl);
+ Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
for(i = 0; i < RARRAY(new_list)->len; 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);
@@ -1674,22 +1370,29 @@
static VALUE
debug_debug_load(int argc, VALUE *argv, VALUE self)
{
VALUE file, stop, context;
debug_context_t *debug_context;
-
+ int state = 0;
+
if(rb_scan_args(argc, argv, "11", &file, &stop) == 1)
stop = Qfalse;
debug_start(self);
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;
- rb_load(file, 0);
-
+ rb_load_protect(file, 0, &state);
+ if (0 != state) {
+ VALUE errinfo = ruby_errinfo;
+ debug_suspend(self);
+ reset_stepping_stop_points(debug_context);
+ ruby_errinfo = Qnil;
+ return errinfo;
+ }
debug_stop(self);
return Qnil;
}
static VALUE
@@ -1834,11 +1537,11 @@
/*
* call-seq:
* context.stop_frame(frame)
*
- * Stops when a frame with number +frame+ is activated. Implements +up+ and +down+ commands.
+ * Stops when a frame with number +frame+ is activated. Implements +finish+ and +next+ commands.
*/
static VALUE
context_stop_frame(VALUE self, VALUE frame)
{
debug_context_t *debug_context;
@@ -2395,54 +2098,10 @@
return CTX_FL_TEST(debug_context, CTX_FL_DEAD) ? Qtrue : Qfalse;
}
/*
* call-seq:
- * context.breakpoint -> breakpoint
- *
- * Returns a context-specific temporary Breakpoint object.
- */
-static VALUE
-context_breakpoint(VALUE self)
-{
- debug_context_t *debug_context;
-
- debug_check_started();
-
- Data_Get_Struct(self, debug_context_t, debug_context);
- return debug_context->breakpoint;
-}
-
-/*
- * call-seq:
- * context.set_breakpoint(source, pos, condition = nil) -> breakpoint
- *
- * Sets a context-specific temporary breakpoint, which can be used to implement
- * 'Run to Cursor' debugger function. When this breakpoint is reached, it will be
- * cleared out.
- *
- * <i>source</i> is a name of a file or a class.
- * <i>pos</i> is a line number or a method name if <i>source</i> is a class name.
- * <i>condition</i> is a string which is evaluated to +true+ when this breakpoint
- * is activated.
- */
-static VALUE
-context_set_breakpoint(int argc, VALUE *argv, VALUE self)
-{
- VALUE result;
- debug_context_t *debug_context;
-
- debug_check_started();
-
- Data_Get_Struct(self, debug_context_t, debug_context);
- result = create_breakpoint_from_args(argc, argv, 0);
- debug_context->breakpoint = result;
- return result;
-}
-
-/*
- * call-seq:
* context.stop_reason -> sym
*
* Returns the reason for the stop. It maybe of the following values:
* :initial, :step, :breakpoint, :catchpoint, :post-mortem
*/
@@ -2477,260 +2136,10 @@
return ID2SYM(rb_intern(sym_name));
}
/*
- * call-seq:
- * breakpoint.enabled?
- *
- * Returns whether breakpoint is enabled or not.
- */
-static VALUE
-breakpoint_enabled(VALUE self)
-{
- debug_breakpoint_t *breakpoint;
-
- Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
- return breakpoint->enabled;
-}
-
-/*
- * call-seq:
- * breakpoint.enabled = bool
- *
- * Enables or disables breakpoint.
- */
-static VALUE
-breakpoint_set_enabled(VALUE self, VALUE bool)
-{
- debug_breakpoint_t *breakpoint;
-
- Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
- return breakpoint->enabled = bool;
-}
-
-/*
- * call-seq:
- * breakpoint.source -> string
- *
- * Returns a source of the breakpoint.
- */
-static VALUE
-breakpoint_source(VALUE self)
-{
- debug_breakpoint_t *breakpoint;
-
- Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
- return breakpoint->source;
-}
-
-/*
- * call-seq:
- * breakpoint.source = string
- *
- * Sets the source of the breakpoint.
- */
-static VALUE
-breakpoint_set_source(VALUE self, VALUE value)
-{
- debug_breakpoint_t *breakpoint;
-
- Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
- breakpoint->source = StringValue(value);
- return value;
-}
-
-/*
- * call-seq:
- * breakpoint.pos -> string or int
- *
- * Returns a position of this breakpoint.
- */
-static VALUE
-breakpoint_pos(VALUE self)
-{
- debug_breakpoint_t *breakpoint;
-
- Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
- if(breakpoint->type == BP_METHOD_TYPE)
- return rb_str_new2(rb_id2name(breakpoint->pos.mid));
- else
- return INT2FIX(breakpoint->pos.line);
-}
-
-/*
- * call-seq:
- * breakpoint.pos = string or int
- *
- * Sets the position of this breakpoint.
- */
-static VALUE
-breakpoint_set_pos(VALUE self, VALUE value)
-{
- debug_breakpoint_t *breakpoint;
-
- Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
- if(breakpoint->type == BP_METHOD_TYPE)
- {
- breakpoint->pos.mid = rb_to_id(StringValue(value));
- }
- else
- breakpoint->pos.line = FIX2INT(value);
- return value;
-}
-
-/*
- * call-seq:
- * breakpoint.expr -> string
- *
- * Returns a codition expression when this breakpoint should be activated.
- */
-static VALUE
-breakpoint_expr(VALUE self)
-{
- debug_breakpoint_t *breakpoint;
-
- Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
- return breakpoint->expr;
-}
-
-/*
- * call-seq:
- * breakpoint.expr = string
- *
- * Sets the codition expression when this breakpoint should be activated.
- */
-static VALUE
-breakpoint_set_expr(VALUE self, VALUE value)
-{
- debug_breakpoint_t *breakpoint;
-
- Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
- breakpoint->expr = StringValue(value);
- return value;
-}
-
-/*
- * call-seq:
- * breakpoint.id -> int
- *
- * Returns id of the breakpoint.
- */
-static VALUE
-breakpoint_id(VALUE self)
-{
- debug_breakpoint_t *breakpoint;
-
- Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
- return INT2FIX(breakpoint->id);
-}
-
-/*
- * call-seq:
- * breakpoint.hit_count -> int
- *
- * Returns the hit count of the breakpoint.
- */
-static VALUE
-breakpoint_hit_count(VALUE self)
-{
- debug_breakpoint_t *breakpoint;
-
- Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
- return INT2FIX(breakpoint->hit_count);
-}
-
-/*
- * call-seq:
- * breakpoint.hit_value -> int
- *
- * Returns the hit value of the breakpoint.
- */
-static VALUE
-breakpoint_hit_value(VALUE self)
-{
- debug_breakpoint_t *breakpoint;
-
- Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
- return INT2FIX(breakpoint->hit_value);
-}
-
-/*
- * call-seq:
- * breakpoint.hit_value = int
- *
- * Sets the hit value of the breakpoint.
- */
-static VALUE
-breakpoint_set_hit_value(VALUE self, VALUE value)
-{
- debug_breakpoint_t *breakpoint;
-
- Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
- breakpoint->hit_value = FIX2INT(value);
- return value;
-}
-
-/*
- * call-seq:
- * breakpoint.hit_condition -> symbol
- *
- * Returns the hit condition of the breakpoint:
- *
- * +nil+ if it is an unconditional breakpoint, or
- * :greater_or_equal, :equal, :modulo
- */
-static VALUE
-breakpoint_hit_condition(VALUE self)
-{
- debug_breakpoint_t *breakpoint;
-
- Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
- switch(breakpoint->hit_condition)
- {
- case HIT_COND_GE:
- return ID2SYM(rb_intern("greater_or_equal"));
- case HIT_COND_EQ:
- return ID2SYM(rb_intern("equal"));
- case HIT_COND_MOD:
- return ID2SYM(rb_intern("modulo"));
- case HIT_COND_NONE:
- default:
- return Qnil;
- }
-}
-
-/*
- * call-seq:
- * breakpoint.hit_condition = symbol
- *
- * Sets the hit condition of the breakpoint which must be one of the following values:
- *
- * +nil+ if it is an unconditional breakpoint, or
- * :greater_or_equal(:ge), :equal(:eq), :modulo(:mod)
- */
-static VALUE
-breakpoint_set_hit_condition(VALUE self, VALUE value)
-{
- debug_breakpoint_t *breakpoint;
- ID id_value;
-
- Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
- id_value = rb_to_id(value);
-
- if(rb_intern("greater_or_equal") == id_value || rb_intern("ge") == id_value)
- breakpoint->hit_condition = HIT_COND_GE;
- else if(rb_intern("equal") == id_value || rb_intern("eq") == id_value)
- breakpoint->hit_condition = HIT_COND_EQ;
- else if(rb_intern("modulo") == id_value || rb_intern("mod") == id_value)
- breakpoint->hit_condition = HIT_COND_MOD;
- else
- rb_raise(rb_eArgError, "Invalid condition parameter");
- return value;
-}
-
-/*
* Document-class: Context
*
* == Summary
*
* Debugger keeps a single instance of this class for each Ruby thread.
@@ -2762,43 +2171,52 @@
rb_define_method(cContext, "frame_locals", context_frame_locals, -1);
rb_define_method(cContext, "frame_method", context_frame_id, -1);
rb_define_method(cContext, "frame_self", context_frame_self, -1);
rb_define_method(cContext, "stack_size", context_stack_size, 0);
rb_define_method(cContext, "dead?", context_dead, 0);
- rb_define_method(cContext, "breakpoint", context_breakpoint, 0);
- rb_define_method(cContext, "set_breakpoint", context_set_breakpoint, -1);
+ rb_define_method(cContext, "breakpoint",
+ context_breakpoint, 0); /* in breakpoint.c */
+ rb_define_method(cContext, "set_breakpoint",
+ context_set_breakpoint, -1); /* in breakpoint.c */
}
/*
- * Document-class: Breakpoint
+ * call-seq:
+ * Debugger.breakpoints -> array
*
- * == Summary
- *
- * This class represents a breakpoint. It defines position of the breakpoint and
- * condition when this breakpoint should be triggered.
+ * Returns an array of breakpoints.
*/
-static void
-Init_breakpoint()
+static VALUE
+debug_breakpoints(VALUE self)
{
- cBreakpoint = rb_define_class_under(mDebugger, "Breakpoint", rb_cObject);
- rb_define_method(cBreakpoint, "id", breakpoint_id, 0);
- rb_define_method(cBreakpoint, "source", breakpoint_source, 0);
- rb_define_method(cBreakpoint, "source=", breakpoint_set_source, 1);
- rb_define_method(cBreakpoint, "enabled?", breakpoint_enabled, 0);
- rb_define_method(cBreakpoint, "enabled=", breakpoint_set_enabled, 1);
- rb_define_method(cBreakpoint, "pos", breakpoint_pos, 0);
- rb_define_method(cBreakpoint, "pos=", breakpoint_set_pos, 1);
- rb_define_method(cBreakpoint, "expr", breakpoint_expr, 0);
- rb_define_method(cBreakpoint, "expr=", breakpoint_set_expr, 1);
- rb_define_method(cBreakpoint, "hit_count", breakpoint_hit_count, 0);
- rb_define_method(cBreakpoint, "hit_value", breakpoint_hit_value, 0);
- rb_define_method(cBreakpoint, "hit_value=", breakpoint_set_hit_value, 1);
- rb_define_method(cBreakpoint, "hit_condition", breakpoint_hit_condition, 0);
- rb_define_method(cBreakpoint, "hit_condition=", breakpoint_set_hit_condition, 1);
+ debug_check_started();
+
+ return rdebug_breakpoints;
}
+/*
+ * call-seq:
+ * Debugger.add_breakpoint(source, pos, condition = nil) -> breakpoint
+ *
+ * Adds a new breakpoint.
+ * <i>source</i> is a name of a file or a class.
+ * <i>pos</i> is a line number or a method name if <i>source</i> is a class name.
+ * <i>condition</i> 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;
+}
+
/*
* Document-class: Debugger
*
* == Summary
*
@@ -2811,19 +2229,22 @@
void
Init_ruby_debug()
{
mDebugger = rb_define_module("Debugger");
rb_define_const(mDebugger, "VERSION", rb_str_new2(DEBUG_VERSION));
- rb_define_module_function(mDebugger, "start", debug_start, 0);
+ rb_define_module_function(mDebugger, "start_", debug_start, 0);
rb_define_module_function(mDebugger, "stop", debug_stop, 0);
rb_define_module_function(mDebugger, "started?", debug_is_started, 0);
rb_define_module_function(mDebugger, "breakpoints", debug_breakpoints, 0);
rb_define_module_function(mDebugger, "add_breakpoint", debug_add_breakpoint, -1);
rb_define_module_function(mDebugger, "remove_breakpoint",
- debug_remove_breakpoint, 1);
- rb_define_module_function(mDebugger, "catchpoint", debug_catchpoint, 0);
- rb_define_module_function(mDebugger, "catchpoint=", debug_set_catchpoint, 1);
+ rdebug_remove_breakpoint,
+ 1); /* in breakpoint.c */
+ rb_define_module_function(mDebugger, "add_catchpoint",
+ rdebug_add_catchpoint, 1); /* in breakpoint.c */
+ rb_define_module_function(mDebugger, "catchpoints",
+ debug_catchpoints, 0); /* in breakpoint.c */
rb_define_module_function(mDebugger, "last_context", debug_last_interrupted, 0);
rb_define_module_function(mDebugger, "contexts", debug_contexts, 0);
rb_define_module_function(mDebugger, "current_context", debug_current_context, 0);
rb_define_module_function(mDebugger, "thread_context", debug_thread_context, 1);
rb_define_module_function(mDebugger, "suspend", debug_suspend, 0);
@@ -2833,38 +2254,41 @@
rb_define_module_function(mDebugger, "debug_load", debug_debug_load, -1);
rb_define_module_function(mDebugger, "skip", debug_skip, 0);
rb_define_module_function(mDebugger, "debug_at_exit", debug_at_exit, 0);
rb_define_module_function(mDebugger, "post_mortem?", debug_post_mortem, 0);
rb_define_module_function(mDebugger, "post_mortem=", debug_set_post_mortem, 1);
- rb_define_module_function(mDebugger, "keep_frame_binding?", debug_keep_frame_binding, 0);
- rb_define_module_function(mDebugger, "keep_frame_binding=", debug_set_keep_frame_binding, 1);
+ rb_define_module_function(mDebugger, "keep_frame_binding?",
+ debug_keep_frame_binding, 0);
+ rb_define_module_function(mDebugger, "keep_frame_binding=",
+ debug_set_keep_frame_binding, 1);
rb_define_module_function(mDebugger, "track_frame_args?",
debug_track_frame_args, 0);
rb_define_module_function(mDebugger, "track_frame_args=",
debug_set_track_frame_args, 1);
rb_define_module_function(mDebugger, "debug", debug_debug, 0);
rb_define_module_function(mDebugger, "debug=", debug_set_debug, 1);
cThreadsTable = rb_define_class_under(mDebugger, "ThreadsTable", rb_cObject);
cDebugThread = rb_define_class_under(mDebugger, "DebugThread", rb_cThread);
- rb_define_singleton_method(cDebugThread, "inherited", debug_thread_inherited, 1);
+ rb_define_singleton_method(cDebugThread, "inherited",
+ debug_thread_inherited, 1);
Init_context();
Init_breakpoint();
- idAtLine = rb_intern("at_line");
idAtBreakpoint = rb_intern("at_breakpoint");
idAtCatchpoint = rb_intern("at_catchpoint");
+ idAtLine = rb_intern("at_line");
+ idAtReturn = rb_intern("at_return");
idAtTracing = rb_intern("at_tracing");
- idEval = rb_intern("eval");
idList = rb_intern("list");
rb_mObjectSpace = rb_const_get(rb_mKernel, rb_intern("ObjectSpace"));
- rb_global_variable(&threads_tbl);
- rb_global_variable(&breakpoints);
- rb_global_variable(&catchpoint);
- rb_global_variable(&locker);
rb_global_variable(&last_context);
rb_global_variable(&last_thread);
+ rb_global_variable(&locker);
+ rb_global_variable(&rdebug_breakpoints);
+ rb_global_variable(&rdebug_catchpoints);
+ rb_global_variable(&rdebug_threads_tbl);
}