From: Mauricio Fernandez Date: 2006-07-06T19:32:25+09:00 Subject: Re: debugging proposal --oyUTqETQ0mS9luUI Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Thu, Jul 06, 2006 at 04:24:41PM +0900, Max Muermann wrote: > Thanks! Somehow I did not spot this... > > I'll give it another go then. I went ahead and simplified the code + made an extension (you can find it attached). [Florian, are you reading this?] BTW, it now works with ruby 1.8 too (just remove the File.expand_path calls in dbg.rb). $ ruby19 -rrdebug dbg.rb [["/home/batsman/mess/2006/27/rdebug/debugee.rb", 7], ["/home/batsman/mess/2006/27/rdebug/debugee.rb", 3]] Dbg.rb Breakpoint /home/batsman/mess/2006/27/rdebug/debugee.rb:3 (Debugee#hello) (Dbg):caller ["(eval):1:in `Debugee#hello'", "dbg.rb:36"] (Dbg):c Dbg.rb Breakpoint /home/batsman/mess/2006/27/rdebug/debugee.rb:7 (Debugee#say) Local variables: what: "World" (Dbg):what = "foooo" "foooo" (Dbg):c "Hello foooo" $ cat debugee.rb class Debugee def hello say "World" end def say( what ) p "Hello #{what}" end end $ cat dbg.rb require "debugee" set_breakpoint_func proc { |event, file, line, id, binding, klass, *rest| puts "Dbg.rb" prompt = true puts "Breakpoint #{file}:#{line} (#{klass}##{id})" lvars = eval("local_variables", binding) unless lvars.empty? puts "Local variables:" lvars.each{|x| val = eval(x, binding); puts "#{x}: #{val.inspect}" } end print "(Dbg):" while prompt and input = readline.chomp next if input == "" case input when "c" prompt = false else begin res =eval(input,binding) p res rescue Exception, StandardError, ScriptError => e p e nil end end print "(Dbg):" if prompt end } add_breakpoint File.expand_path("./debugee.rb"), 3 add_breakpoint File.expand_path("./debugee.rb"), 7 p breakpoints c = Debugee.new c.hello -- Mauricio Fernandez - http://eigenclass.org - singular Ruby --oyUTqETQ0mS9luUI Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="extconf.rb" require 'mkmf' create_makefile 'rdebug' --oyUTqETQ0mS9luUI Content-Type: text/x-csrc; charset=us-ascii Content-Disposition: attachment; filename="rdebug.c" #include #include #include static VALUE breakpoint_func = 0; static int debugging = 1; static void call_breakpoint_func(rb_event_t,NODE*,VALUE,ID,VALUE); static int breakpoint_event_hook_set = 0; typedef struct breakpoint { char* file; int line; struct breakpoint *next; } rb_breakpoint_t; static rb_breakpoint_t *rb_breakpoints; /* * Missing stuff, defined in eval.c and just copied or adapted * */ static VALUE rb_obj_is_proc(proc) VALUE proc; { /* no access to blk_free :-| */ /* if (TYPE(proc) == T_DATA && RDATA(proc)->dfree == (RUBY_DATA_FUNC)blk_free) { return Qtrue; } */ return rb_obj_is_kind_of(proc, rb_cProc); } /* * actual code */ VALUE add_breakpoint( int argc, VALUE *argv, VALUE self ) { if (0 == argc) rb_raise(rb_eArgError, "at least one argument required"); rb_breakpoint_t *bp; bp = ALLOC(rb_breakpoint_t); if (1==argc) { bp->file = ruby_sourcefile; bp->line = FIX2INT(argv[0]); } else { bp->file = StringValuePtr(argv[0]); bp->line = FIX2INT(argv[1]); } bp->next = rb_breakpoints; rb_breakpoints = bp; return Qnil; } VALUE remove_breakpoint( VALUE self, VALUE file, VALUE line ) { if (!rb_breakpoints) return Qfalse; char * cfile = STR2CSTR(file); int iline = FIX2INT(line); rb_breakpoint_t *prev = rb_breakpoints; // trivial case if (is_breakpoint( cfile, iline, prev)) { rb_breakpoints = prev->next; free(prev); return Qtrue; } rb_breakpoint_t *bp = prev->next; while (bp) { if (is_breakpoint( cfile, iline, bp)) { prev->next = bp->next; free(bp); return Qtrue; } prev = bp; bp = prev->next; } // no matching breakpoint found return Qfalse; } VALUE breakpoints() { VALUE bps = rb_ary_new(); rb_breakpoint_t *bp = rb_breakpoints; while(bp) { VALUE bparr = rb_ary_new(); rb_ary_push( bparr, rb_str_new2(bp->file)); rb_ary_push( bparr, INT2FIX(bp->line)); rb_ary_push(bps, bparr); bp = bp->next; } return bps; } int is_breakpoint( char *file, int line, rb_breakpoint_t *bp) { return ( line == bp->line && 0 == strcmp( bp->file, file)); } static VALUE set_breakpoint_func(VALUE obj, VALUE breakpoint) { /* rb_event_hook_t *hook; */ rb_secure(4); if (NIL_P(breakpoint)) { /* trace_func = 0; */ rb_remove_event_hook(call_breakpoint_func); return Qnil; } if (!rb_obj_is_proc(breakpoint)) { rb_raise(rb_eTypeError, "breakpoint_func needs to be Proc"); } breakpoint_func = breakpoint; if (breakpoint_event_hook_set) return; rb_add_event_hook(call_breakpoint_func, RUBY_EVENT_LINE); breakpoint_event_hook_set = 1; return breakpoint; } static VALUE enable_debugging(VALUE args) { debugging = 1; return Qnil; } static VALUE do_call_breakpoint_func(VALUE args) { return rb_funcall2(breakpoint_func, rb_intern("call"), 6, (VALUE *)args); } static void call_breakpoint_func(rb_event_t event, NODE *node, VALUE self, ID id, VALUE klass /* OK */) { VALUE args[6]; void *args2[2]; const char *event_name; char *sourcefile = NULL; int sourceline = 0; if (debugging == 0) return; if (event != RUBY_EVENT_LINE) return; if (!breakpoint_func) return; if (id == ID_ALLOCATOR) return; if (!node && ruby_sourceline == 0) return; if(node) { sourceline = nd_line(node); sourcefile = node->nd_file; } // check wether we have a breakpoint for this file/line rb_breakpoint_t *bp = rb_breakpoints; while (bp) { if (sourceline == bp->line && 0==strcmp(sourcefile, bp->file)) break; bp = bp->next; } if (!bp) return; if (klass) { if (TYPE(klass) == T_ICLASS) { klass = RBASIC(klass)->klass; } else if (FL_TEST(klass, FL_SINGLETON)) { klass = rb_iv_get(klass, "__attached__"); } } // turn breakpoints off while calling breakpoint function debugging = 0; args[0] = rb_str_new2("LINE"); args[1] = rb_str_new2(sourcefile ? sourcefile : "(ruby)"); args[2] = INT2FIX(sourceline); args[3] = id ? ID2SYM(id) : Qnil; args[4] = rb_eval_string("binding"); /* rb_f_binding(ruby_frame->self); */ args[5] = klass ? klass : Qnil; rb_ensure(do_call_breakpoint_func, (VALUE)args, enable_debugging, (VALUE)0); // turn breakpoints back on enable_debugging(0); } void Init_rdebug() { breakpoint_func = 0; rb_define_global_function("set_breakpoint_func", set_breakpoint_func, 1); rb_global_variable(&breakpoint_func); rb_define_global_function("add_breakpoint", add_breakpoint, -1); rb_define_global_function("remove_breakpoint", remove_breakpoint, 2); rb_define_global_function("breakpoints", breakpoints, 0); } /* vim: set ts=8 sw=4 sts=4 expandtab: */ --oyUTqETQ0mS9luUI--