ext/ruby_debug.c in ruby-debug-0.1.2 vs ext/ruby_debug.c in ruby-debug-0.1.3

- old
+ new

@@ -2,11 +2,11 @@ #include <ruby.h> #include <node.h> #include <rubysig.h> #include <st.h> -#define DEBUG_VERSION "0.1.2" +#define DEBUG_VERSION "0.1.3" typedef struct { int thnum; int stop_next; int dest_frame; @@ -27,11 +27,11 @@ } debug_frame_t; typedef struct { VALUE source; VALUE pos; - VALUE disabled; + VALUE expr; } debug_breakpoint_t; static VALUE threads_tbl = Qnil; static VALUE breakpoints = Qnil; static VALUE catchpoint = Qnil; @@ -108,12 +108,12 @@ debug_check_started(); context = rb_hash_aref(threads_tbl, thread); if(context == Qnil) { - context = debug_context_create(thread); - rb_hash_aset(threads_tbl, thread, context); + context = debug_context_create(thread); + rb_hash_aset(threads_tbl, thread, context); } return context; } static void @@ -140,16 +140,15 @@ return result; } static VALUE -call_debug_command(VALUE context, int thnum, VALUE self, ID mid, VALUE file, VALUE line) +call_debug_command(VALUE context, int thnum, VALUE binding, ID mid, VALUE file, VALUE line) { - VALUE id, binding; + VALUE id; id = mid ? ID2SYM(mid) : Qnil; - binding = self? create_binding(self) : Qnil; last_debugged_thnum = thnum; debug_suspend(mDebugger); return rb_funcall(context, idDebugCommand, 4, file, line, id, binding); } @@ -160,14 +159,14 @@ VALUE frame; debug_frame_t *top_frame; if(RARRAY(debug_context->frames)->len > 0) { - frame = *RARRAY(debug_context->frames)->ptr; - Data_Get_Struct(frame, debug_frame_t, top_frame); - top_frame->file = file; - top_frame->line = line; + frame = *RARRAY(debug_context->frames)->ptr; + Data_Get_Struct(frame, debug_frame_t, top_frame); + top_frame->file = file; + top_frame->line = line; } } static void save_call_frame(VALUE self, VALUE file, VALUE line, ID mid, debug_context_t *debug_context) @@ -178,37 +177,30 @@ frame = debug_frame_create(file, line, binding, mid); rb_ary_unshift(debug_context->frames, frame); } static int -check_breakpoints(VALUE context, VALUE file, VALUE klass, VALUE pos, ID id) +check_breakpoints(VALUE context, VALUE file, VALUE klass, VALUE pos) { VALUE breakpoint; debug_breakpoint_t *debug_breakpoint; int i; - int result = 0; if(RARRAY(breakpoints)->len == 0) - return 0; + return -1; for(i = 0; i < RARRAY(breakpoints)->len; i++) { - breakpoint = rb_ary_entry(breakpoints, i); - Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint); - if(debug_breakpoint->pos != pos && !(TYPE(pos) == T_STRING && - TYPE(debug_breakpoint->pos) == T_STRING && rb_str_cmp(debug_breakpoint->pos, pos) == 0)) - continue; - if(TYPE(debug_breakpoint->source) == T_STRING && - rb_str_cmp(debug_breakpoint->source, file) == 0 || - klass != Qnil && debug_breakpoint->source == klass) - { - result = 1; - rb_funcall(context, idAtBreakpoint, 2, breakpoint, id ? rb_str_new2(rb_id2name(id)) : Qnil); - continue; - } - + breakpoint = rb_ary_entry(breakpoints, i); + Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint); + if(debug_breakpoint->pos != pos && !(TYPE(pos) == T_STRING && + TYPE(debug_breakpoint->pos) == T_STRING && rb_str_cmp(debug_breakpoint->pos, pos) == 0)) + continue; + if(rb_str_cmp(debug_breakpoint->source, file) == 0 || + klass != Qnil && rb_str_cmp(debug_breakpoint->source, rb_mod_name(klass)) == 0) + return i; } - return result; + return -1; } /* * This is a NASTY HACK. For some reasons rb_f_binding is decalred * static in eval.c @@ -219,43 +211,71 @@ typedef VALUE (*bind_func_t)(VALUE); static bind_func_t f_binding = NULL; if(f_binding == NULL) { - ID idBinding = rb_intern("binding"); - NODE *body, *method; - st_lookup(RCLASS(rb_mKernel)->m_tbl, idBinding, (st_data_t *)&body); - method = (NODE *)body->u2.value; - f_binding = (bind_func_t)method->u1.value; + ID idBinding = rb_intern("binding"); + NODE *body, *method; + st_lookup(RCLASS(rb_mKernel)->m_tbl, idBinding, (st_data_t *)&body); + method = (NODE *)body->u2.value; + f_binding = (bind_func_t)method->u1.value; } return f_binding(self); } static void check_suspend(debug_context_t *debug_context) { if(rb_thread_critical == Qtrue) - return; + return; while(1) { - rb_thread_critical = Qtrue; - if(!debug_context->suspend) - break; - rb_ary_push(waiting, rb_thread_current()); - debug_context->suspend = 0; - rb_thread_stop(); + rb_thread_critical = Qtrue; + if(!debug_context->suspend) + break; + rb_ary_push(waiting, rb_thread_current()); + debug_context->suspend = 0; + rb_thread_stop(); } rb_thread_critical = Qfalse; } +static VALUE +get_breakpoint_at(int index) +{ + return rb_ary_entry(breakpoints, index); +} + +static VALUE +eval_expression(VALUE args) +{ + return rb_funcall(rb_mKernel, rb_intern("eval"), 2, RARRAY(args)->ptr[0], RARRAY(args)->ptr[1]); +} + +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); +} + static void debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass) { - VALUE thread; - VALUE context; + VALUE thread, context, binding, breakpoint; debug_context_t *debug_context; + debug_breakpoint_t *debug_breakpoint; VALUE file = Qnil, line = Qnil; + int breakpoint_index = -1; static int debugging = 0; if(debugging) return; @@ -266,127 +286,146 @@ Data_Get_Struct(context, debug_context_t, debug_context); check_suspend(debug_context); if(node) { - file = rb_str_new2(node->nd_file); - line = INT2FIX(nd_line(node)); + file = rb_str_new2(node->nd_file); + line = INT2FIX(nd_line(node)); } switch(event) { - case RUBY_EVENT_LINE: - { - set_frame_source(debug_context, file, line); + case RUBY_EVENT_LINE: + { + set_frame_source(debug_context, file, line); - if(RTEST(tracing) || debug_context->tracing ) - { - rb_funcall(context, idAtTracing, 2, file, line); - } + if(RTEST(tracing) || debug_context->tracing ) + { + rb_funcall(context, idAtTracing, 2, file, line); + } - if(debug_context->dest_frame == -1 || - RARRAY(debug_context->frames)->len == debug_context->dest_frame) - { - debug_context->stop_next--; - if(debug_context->stop_next < 0) - debug_context->stop_next = -1; - /* we check that we actualy moved to another line */ - if(line != Qnil && debug_context->src_line != FIX2INT(line)) - { - debug_context->stop_line--; - } - } - else if(RARRAY(debug_context->frames)->len < debug_context->dest_frame) - { - debug_context->stop_next = 0; - } + if(debug_context->dest_frame == -1 || + RARRAY(debug_context->frames)->len == debug_context->dest_frame) + { + debug_context->stop_next--; + if(debug_context->stop_next < 0) + debug_context->stop_next = -1; + /* we check that we actualy moved to another line */ + if(line != Qnil && debug_context->src_line != FIX2INT(line)) + { + debug_context->stop_line--; + } + } + else if(RARRAY(debug_context->frames)->len < debug_context->dest_frame) + { + debug_context->stop_next = 0; + } - if(RARRAY(debug_context->frames)->len == 0) - { - save_call_frame(self, file, line, mid, debug_context); - } + if(RARRAY(debug_context->frames)->len == 0) + { + save_call_frame(self, file, line, mid, debug_context); + } - if(debug_context->stop_next == 0 || debug_context->stop_line == 0 || - check_breakpoints(context, file, klass, line, mid)) - { - /* reset all pointers */ - debug_context->dest_frame = -1; - debug_context->src_line = -1; - debug_context->stop_line = -1; - debug_context->stop_next = -1; + if(debug_context->stop_next == 0 || debug_context->stop_line == 0 || + (breakpoint_index = check_breakpoints(context, file, klass, line)) != -1) + { + binding = self? create_binding(self) : Qnil; + /* check breakpoint expression */ + if(breakpoint_index != -1) + { + breakpoint = get_breakpoint_at(breakpoint_index); + if(check_breakpoint_expression(breakpoint, binding)) + rb_funcall(context, idAtBreakpoint, 2, breakpoint, mid ? rb_str_new2(rb_id2name(mid)) : Qnil); + else + break; + } - call_debug_command(context, debug_context->thnum, self, mid, file, line); - } - break; - } - case RUBY_EVENT_C_CALL: - { - if(node) - { - set_frame_source(debug_context, file, line); - } - break; - } - case RUBY_EVENT_CALL: - { - save_call_frame(self, file, line, mid, debug_context); - if(check_breakpoints(context, file, klass, rb_str_new2(rb_id2name(mid)), mid)) - { - call_debug_command(context, debug_context->thnum, self, mid, file, line); - } - break; - } - case RUBY_EVENT_RETURN: - case RUBY_EVENT_END: - { - if(RARRAY(debug_context->frames)->len == debug_context->stop_frame) - { - debug_context->stop_next = 1; - debug_context->stop_frame = 0; - } - rb_ary_shift(debug_context->frames); - break; - } - case RUBY_EVENT_CLASS: - { - save_call_frame(self, file, line, mid, debug_context); - break; - } - case RUBY_EVENT_RAISE: - { - VALUE ancestors; - VALUE expn_class, aclass; - int i, found = 0; + /* reset all pointers */ + debug_context->dest_frame = -1; + debug_context->src_line = -1; + debug_context->stop_line = -1; + debug_context->stop_next = -1; - expn_class = rb_obj_class(ruby_errinfo); - if( !NIL_P(rb_class_inherited_p(expn_class, rb_eSystemExit)) ) - { - debug_stop(mDebugger); - rb_exit(0); - } + call_debug_command(context, debug_context->thnum, binding, mid, file, line); + } + break; + } + case RUBY_EVENT_C_CALL: + { + if(node) + { + set_frame_source(debug_context, file, line); + } + break; + } + case RUBY_EVENT_CALL: + { + save_call_frame(self, file, line, mid, debug_context); + breakpoint_index = check_breakpoints(context, file, klass, rb_str_new2(rb_id2name(mid))); + if(breakpoint_index != -1) + { + binding = self? create_binding(self) : Qnil; + breakpoint = get_breakpoint_at(breakpoint_index); + if(check_breakpoint_expression(breakpoint, binding)) + { + rb_funcall(context, idAtBreakpoint, 2, breakpoint, mid ? rb_str_new2(rb_id2name(mid)) : Qnil); + call_debug_command(context, debug_context->thnum, binding, mid, file, line); + } + } + break; + } + case RUBY_EVENT_RETURN: + case RUBY_EVENT_END: + { + if(RARRAY(debug_context->frames)->len == debug_context->stop_frame) + { + debug_context->stop_next = 1; + debug_context->stop_frame = 0; + } + rb_ary_shift(debug_context->frames); + break; + } + case RUBY_EVENT_CLASS: + { + save_call_frame(self, file, line, mid, debug_context); + break; + } + case RUBY_EVENT_RAISE: + { + VALUE ancestors; + VALUE expn_class, aclass; + int i, found = 0; - if(catchpoint == Qnil) - break; + expn_class = rb_obj_class(ruby_errinfo); + if( !NIL_P(rb_class_inherited_p(expn_class, rb_eSystemExit)) ) + { + debug_stop(mDebugger); + rb_exit(0); + } - 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) - { - rb_funcall(context, idAtCatchpoint, 0); - call_debug_command(context, debug_context->thnum, self, mid, file, line); - break; - } - } + if(catchpoint == Qnil) + break; - break; - } - case RUBY_EVENT_C_RETURN: - { - 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) + { + rb_funcall(context, idAtCatchpoint, 0); + binding = self? create_binding(self) : Qnil; + call_debug_command(context, debug_context->thnum, binding, mid, file, line); + break; + } + } + + break; + } + case RUBY_EVENT_C_RETURN: + { + break; + } } debugging--; } @@ -408,14 +447,14 @@ threads_tbl = rb_hash_new(); breakpoints = rb_ary_new(); waiting = rb_ary_new(); rb_add_event_hook(debug_event_hook, - RUBY_EVENT_LINE | RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN | - RUBY_EVENT_CALL | RUBY_EVENT_RETURN | RUBY_EVENT_CLASS | - RUBY_EVENT_END | RUBY_EVENT_RAISE - ); + RUBY_EVENT_LINE | RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN | + RUBY_EVENT_CALL | RUBY_EVENT_RETURN | RUBY_EVENT_CLASS | + RUBY_EVENT_END | RUBY_EVENT_RAISE + ); return Qnil; } static VALUE debug_stop(VALUE self) @@ -435,24 +474,31 @@ { debug_breakpoint_t *breakpoint; breakpoint = (debug_breakpoint_t *)data; rb_gc_mark(breakpoint->source); rb_gc_mark(breakpoint->pos); + rb_gc_mark(breakpoint->expr); } static VALUE -debug_add_breakpoint(VALUE self, VALUE source, VALUE pos) +debug_add_breakpoint(int argc, VALUE *argv, VALUE self) { + VALUE source, pos, expr; VALUE result; debug_breakpoint_t *breakpoint; debug_check_started(); + + if(rb_scan_args(argc, argv, "21", &source, &pos, &expr) == 2) + { + expr = Qnil; + } breakpoint = ALLOC(debug_breakpoint_t); - breakpoint->source = source; + breakpoint->source = StringValue(source); breakpoint->pos = pos; - breakpoint->disabled = Qfalse; + breakpoint->expr = NIL_P(expr) ? expr: StringValue(expr); result = Data_Wrap_Struct(cBreakpoint, breakpoint_mark, xfree, breakpoint); rb_ary_push(breakpoints, result); return result; } @@ -476,16 +522,16 @@ 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"); + rb_raise(rb_eTypeError, "value of checkpoint must be String"); } if(NIL_P(value)) - catchpoint = Qnil; + catchpoint = Qnil; else - catchpoint = rb_str_dup(value); + catchpoint = rb_str_dup(value); return value; } static VALUE debug_interrupt(VALUE self) @@ -508,12 +554,12 @@ { debug_context_t *debug_context; Data_Get_Struct(value, debug_context_t, debug_context); if(debug_context->thnum == last_debugged_thnum) { - *result = value; - return ST_STOP; + *result = value; + return ST_STOP; } return ST_CONTINUE; } static VALUE @@ -533,12 +579,12 @@ debug_check_started(); context = find_last_context(); if(context != Qnil) { - Data_Get_Struct(context, debug_context_t, debug_context); - debug_context->stop_next = 1; + Data_Get_Struct(context, debug_context_t, debug_context); + debug_context->stop_next = 1; } return Qnil; } @@ -568,23 +614,23 @@ new_list = rb_ary_new(); list = rb_funcall(rb_cThread, rb_intern("list"), 0); for(i = 0; i < RARRAY(list)->len; i++) { - thread = rb_ary_entry(list, i); - context = thread_context_lookup(thread); - rb_ary_push(new_list, context); + thread = rb_ary_entry(list, i); + context = thread_context_lookup(thread); + rb_ary_push(new_list, context); } /* * I wonder why rb_hash_clear is declared static? */ rb_funcall(threads_tbl, rb_intern("clear"), 0); 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); - rb_hash_aset(threads_tbl, debug_context->thread, context); + context = rb_ary_entry(new_list, i); + Data_Get_Struct(context, debug_context_t, debug_context); + rb_hash_aset(threads_tbl, debug_context->thread, context); } return new_list; } @@ -604,20 +650,20 @@ context_list = debug_contexts(self); current = thread_context_lookup(rb_thread_current()); for(i = 0; i < RARRAY(context_list)->len; i++) { - context = rb_ary_entry(context_list, i); - if(current == context) - continue; - Data_Get_Struct(context, debug_context_t, debug_context); - debug_context->suspend = 1; + context = rb_ary_entry(context_list, i); + if(current == context) + continue; + Data_Get_Struct(context, debug_context_t, debug_context); + debug_context->suspend = 1; } rb_thread_critical = saved_crit; if(rb_thread_critical == Qfalse) - rb_thread_schedule(); + rb_thread_schedule(); return context; } static VALUE @@ -637,20 +683,20 @@ context_list = debug_contexts(self); current = thread_context_lookup(rb_thread_current()); for(i = 0; i < RARRAY(context_list)->len; i++) { - context = rb_ary_entry(context_list, i); - if(current == context) - continue; - Data_Get_Struct(context, debug_context_t, debug_context); - debug_context->suspend = 0; + context = rb_ary_entry(context_list, i); + if(current == context) + continue; + Data_Get_Struct(context, debug_context_t, debug_context); + debug_context->suspend = 0; } for(i = 0; i < RARRAY(waiting)->len; i++) { - thread = rb_ary_entry(waiting, i); - rb_thread_run(thread); + thread = rb_ary_entry(waiting, i); + rb_thread_run(thread); } rb_ary_clear(waiting); rb_thread_critical = saved_crit; rb_thread_schedule(); @@ -678,11 +724,11 @@ debug_check_started(); Data_Get_Struct(self, debug_context_t, debug_context); if(FIX2INT(steps) < 0) - rb_raise(rb_eRuntimeError, "Steps argument can't be negative."); + rb_raise(rb_eRuntimeError, "Steps argument can't be negative."); debug_context->stop_next = FIX2INT(steps); return steps; } @@ -695,44 +741,44 @@ debug_check_started(); Data_Get_Struct(self, debug_context_t, debug_context); if(RARRAY(debug_context->frames)->len == 0) - rb_raise(rb_eRuntimeError, "No frames collected."); + rb_raise(rb_eRuntimeError, "No frames collected."); rb_scan_args(argc, argv, "11", &lines, &frame); debug_context->stop_line = FIX2INT(lines); if(argc == 1) { - debug_context->dest_frame = RARRAY(debug_context->frames)->len; + debug_context->dest_frame = RARRAY(debug_context->frames)->len; } else { - if(FIX2INT(frame) < 0 && FIX2INT(frame) >= RARRAY(debug_context->frames)->len) - rb_raise(rb_eRuntimeError, "Destination frame is out of range."); - debug_context->dest_frame = FIX2INT(frame); + if(FIX2INT(frame) < 0 && FIX2INT(frame) >= RARRAY(debug_context->frames)->len) + rb_raise(rb_eRuntimeError, "Destination frame is out of range."); + debug_context->dest_frame = FIX2INT(frame); } - + cur_frame = *RARRAY(debug_context->frames)->ptr; Data_Get_Struct(cur_frame, debug_frame_t, debug_frame); debug_context->src_line = FIX2INT(debug_frame->line); - + return Qnil; } static VALUE context_stop_frame(VALUE self, VALUE frame) { debug_context_t *debug_context; debug_check_started(); - + Data_Get_Struct(self, debug_context_t, debug_context); if(FIX2INT(frame) < 0 && FIX2INT(frame) >= RARRAY(debug_context->frames)->len) - rb_raise(rb_eRuntimeError, "Stop frame is out of range."); + rb_raise(rb_eRuntimeError, "Stop frame is out of range."); debug_context->stop_frame = FIX2INT(frame); - + return frame; } static VALUE context_frames(VALUE self) @@ -860,10 +906,20 @@ Data_Get_Struct(self, debug_breakpoint_t, breakpoint); return breakpoint->pos; } +static VALUE +breakpoint_expr(VALUE self) +{ + debug_breakpoint_t *breakpoint; + + Data_Get_Struct(self, debug_breakpoint_t, breakpoint); + return breakpoint->expr; +} + + #if defined(_WIN32) __declspec(dllexport) #endif void Init_ruby_debug() @@ -873,11 +929,11 @@ 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, "thnum", debug_thnum, 0); rb_define_module_function(mDebugger, "breakpoints", debug_breakpoints, 0); - rb_define_module_function(mDebugger, "add_breakpoint", debug_add_breakpoint, 2); + rb_define_module_function(mDebugger, "add_breakpoint", debug_add_breakpoint, -1); rb_define_module_function(mDebugger, "catchpoint", debug_catchpoint, 0); rb_define_module_function(mDebugger, "catchpoint=", debug_set_catchpoint, 1); rb_define_module_function(mDebugger, "interrupt", debug_interrupt, 0); rb_define_module_function(mDebugger, "interrupt_last", debug_interrupt_last, 0); rb_define_module_function(mDebugger, "contexts", debug_contexts, 0); @@ -906,9 +962,10 @@ rb_define_method(cFrame, "id", frame_id, 0); cBreakpoint = rb_define_class_under(mDebugger, "Breakpoint", rb_cObject); rb_define_method(cBreakpoint, "source", breakpoint_source, 0); rb_define_method(cBreakpoint, "pos", breakpoint_pos, 0); + rb_define_method(cBreakpoint, "expr", breakpoint_expr, 0); idDebugCommand = rb_intern("debug_command"); idAtBreakpoint = rb_intern("at_breakpoint"); idAtCatchpoint = rb_intern("at_catchpoint"); idAtTracing = rb_intern("at_tracing");