diff --git a/ChangeLog b/ChangeLog index 8bc8fa3ae9..651d6306c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +Tue Jan 29 17:03:28 2013 Koichi Sasada + + * vm_backtrace.c: fix issue of rb_debug_inspector_open(). + The order of making binding should be stack (frame) top to bottom. + [Bug #7635] + And also fix issue of collecting klass. Collecting klass is same + as TracePoint#defined_class. + (previous version, it returns T_ICLASS (internal objects). + + * test/-ext-/debug/test_debug.rb: add a test. + + * ext/-test-/debug/extconf.rb, init.c, inspector.c: ditto. + + * vm_backtrace.c: remove magic number and add enum CALLER_BINDING_*. + + * vm_backtrace.c, include/ruby/debug.h: add new C api (experimental) + rb_debug_inspector_frame_self_get(). + + * vm.c, vm_core.h, vm_trace.c: move decl. of + rb_vm_control_frame_id_and_class() and constify first parameter. + Tue Jan 29 16:50:58 2013 Nobuyoshi Nakada * vm_trace.c (rb_tracepoint_enable, rb_tracepoint_disable): check safe diff --git a/ext/-test-/debug/extconf.rb b/ext/-test-/debug/extconf.rb new file mode 100644 index 0000000000..8f7922e1a6 --- /dev/null +++ b/ext/-test-/debug/extconf.rb @@ -0,0 +1,6 @@ +$srcs = Dir[File.join($srcdir, "*.{#{SRC_EXT.join(%q{,})}}")] +inits = $srcs.map {|s| File.basename(s, ".*")} +inits.delete("init") +inits.map! {|s|"X(#{s})"} +$defs << "-DTEST_INIT_FUNCS(X)=\"#{inits.join(' ')}\"" +create_makefile("-test-/debug") diff --git a/ext/-test-/debug/init.c b/ext/-test-/debug/init.c new file mode 100644 index 0000000000..4b10112144 --- /dev/null +++ b/ext/-test-/debug/init.c @@ -0,0 +1,11 @@ +#include "ruby.h" + +#define init(n) {void Init_##n(VALUE klass); Init_##n(klass);} + +void +Init_debug(void) +{ + VALUE mBug = rb_define_module("Bug"); + VALUE klass = rb_define_class_under(mBug, "Debug", rb_cModule); + TEST_INIT_FUNCS(init); +} diff --git a/ext/-test-/debug/inspector.c b/ext/-test-/debug/inspector.c new file mode 100644 index 0000000000..6212b2ed93 --- /dev/null +++ b/ext/-test-/debug/inspector.c @@ -0,0 +1,32 @@ +#include "ruby/ruby.h" +#include "ruby/debug.h" + +static VALUE +callback(const rb_debug_inspector_t *dbg_context, void *data) +{ + VALUE locs = rb_debug_inspector_backtrace_locations(dbg_context); + int i, len = RARRAY_LENINT(locs); + VALUE binds = rb_ary_new(); + for (i = 0; i < len; ++i) { + VALUE entry = rb_ary_new(); + rb_ary_push(binds, entry); + rb_ary_push(entry, rb_debug_inspector_frame_self_get(dbg_context, i)); + rb_ary_push(entry, rb_debug_inspector_frame_binding_get(dbg_context, i)); + rb_ary_push(entry, rb_debug_inspector_frame_class_get(dbg_context, i)); + rb_ary_push(entry, rb_debug_inspector_frame_iseq_get(dbg_context, i)); + rb_ary_push(entry, rb_ary_entry(locs, i)); + } + return binds; +} + +static VALUE +debug_inspector(VALUE self) +{ + return rb_debug_inspector_open(callback, NULL); +} + +void +Init_inspector(VALUE klass) +{ + rb_define_module_function(klass, "inspector", debug_inspector, 0); +} diff --git a/include/ruby/debug.h b/include/ruby/debug.h index 3cdf60a754..4ef45a8327 100644 --- a/include/ruby/debug.h +++ b/include/ruby/debug.h @@ -31,8 +31,9 @@ typedef struct rb_debug_inspector_struct rb_debug_inspector_t; typedef VALUE (*rb_debug_inspector_func_t)(const rb_debug_inspector_t *, void *); VALUE rb_debug_inspector_open(rb_debug_inspector_func_t func, void *data); -VALUE rb_debug_inspector_frame_binding_get(const rb_debug_inspector_t *dc, long index); +VALUE rb_debug_inspector_frame_self_get(const rb_debug_inspector_t *dc, long index); VALUE rb_debug_inspector_frame_class_get(const rb_debug_inspector_t *dc, long index); +VALUE rb_debug_inspector_frame_binding_get(const rb_debug_inspector_t *dc, long index); VALUE rb_debug_inspector_frame_iseq_get(const rb_debug_inspector_t *dc, long index); VALUE rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc); diff --git a/test/-ext-/debug/test_debug.rb b/test/-ext-/debug/test_debug.rb new file mode 100644 index 0000000000..4c45057b5d --- /dev/null +++ b/test/-ext-/debug/test_debug.rb @@ -0,0 +1,58 @@ +require 'test/unit' +require '-test-/debug' + +class TestDebug < Test::Unit::TestCase + + def binds_check binds + count = Hash.new(0) + assert_instance_of(Array, binds) + binds.each{|(_self, bind, klass, iseq, loc)| + if _self == self + count[:self] += 1 + end + + if bind + assert_instance_of(Binding, bind) + count[:bind] += 1 + end + + if klass + assert(klass.instance_of?(Module) || klass.instance_of?(Class)) + count[:class] += 1 + end + + if iseq + count[:iseq] += 1 + assert_instance_of(RubyVM::InstructionSequence, iseq) + + # check same location + assert_equal(loc.path, iseq.path) + assert_equal(loc.absolute_path, iseq.absolute_path) + assert_equal(loc.label, iseq.label) + assert_operator(loc.lineno, :>=, iseq.first_lineno) + end + + assert_instance_of(Thread::Backtrace::Location, loc) + + } + assert_operator(0, :<, count[:self]) + assert_operator(0, :<, count[:bind]) + assert_operator(0, :<, count[:iseq]) + assert_operator(0, :<, count[:class]) + end + + def test_inspector_open + binds = Bug::Debug.inspector + binds_check binds + end + + def inspector_in_eval + eval("Bug::Debug.inspector") + end + + def test_inspector_open_in_eval + bug7635 = '[ruby-core:51640]' + binds = inspector_in_eval + binds_check binds + end +end diff --git a/vm.c b/vm.c index e6a2a6d84f..c8f0a29187 100644 --- a/vm.c +++ b/vm.c @@ -1414,7 +1414,7 @@ rb_iseq_eval_main(VALUE iseqval) } int -rb_vm_control_frame_id_and_class(rb_control_frame_t *cfp, ID *idp, VALUE *klassp) +rb_vm_control_frame_id_and_class(const rb_control_frame_t *cfp, ID *idp, VALUE *klassp) { rb_iseq_t *iseq = cfp->iseq; if (!iseq && cfp->me) { diff --git a/vm_backtrace.c b/vm_backtrace.c index f22b95c704..15bf1633d9 100644 --- a/vm_backtrace.c +++ b/vm_backtrace.c @@ -1012,8 +1012,15 @@ struct rb_debug_inspector_struct { long backtrace_size; }; +enum { + CALLER_BINDING_SELF, + CALLER_BINDING_CLASS, + CALLER_BINDING_BINDING, + CALLER_BINDING_ISEQ, + CALLER_BINDING_CFP +}; + struct collect_caller_bindings_data { - rb_thread_t *th; VALUE ary; }; @@ -1023,37 +1030,82 @@ collect_caller_bindings_init(void *arg, size_t size) /* */ } +static VALUE +get_klass(const rb_control_frame_t *cfp) +{ + VALUE klass; + if (rb_vm_control_frame_id_and_class(cfp, 0, &klass)) { + if (RB_TYPE_P(klass, T_ICLASS)) { + return RBASIC(klass)->klass; + } + else { + return klass; + } + } + else { + return Qnil; + } +} + static void collect_caller_bindings_iseq(void *arg, const rb_control_frame_t *cfp) { struct collect_caller_bindings_data *data = (struct collect_caller_bindings_data *)arg; - rb_ary_push(data->ary, - rb_ary_new3(4, - cfp->klass, - rb_binding_new_with_cfp(data->th, cfp), - cfp->iseq ? cfp->iseq->self : Qnil, - GC_GUARDED_PTR(cfp))); + VALUE frame = rb_ary_new2(5); + + rb_ary_store(frame, CALLER_BINDING_SELF, cfp->self); + rb_ary_store(frame, CALLER_BINDING_CLASS, get_klass(cfp)); + rb_ary_store(frame, CALLER_BINDING_BINDING, GC_GUARDED_PTR(cfp)); /* create later */ + rb_ary_store(frame, CALLER_BINDING_ISEQ, cfp->iseq ? cfp->iseq->self : Qnil); + rb_ary_store(frame, CALLER_BINDING_CFP, GC_GUARDED_PTR(cfp)); + + rb_ary_push(data->ary, frame); } static void collect_caller_bindings_cfunc(void *arg, const rb_control_frame_t *cfp, ID mid) { struct collect_caller_bindings_data *data = (struct collect_caller_bindings_data *)arg; - rb_ary_push(data->ary, rb_ary_new3(2, cfp->klass, Qnil)); + VALUE frame = rb_ary_new2(5); + + rb_ary_store(frame, CALLER_BINDING_SELF, cfp->self); + rb_ary_store(frame, CALLER_BINDING_CLASS, get_klass(cfp)); + rb_ary_store(frame, CALLER_BINDING_BINDING, Qnil); /* not available */ + rb_ary_store(frame, CALLER_BINDING_ISEQ, Qnil); /* not available */ + rb_ary_store(frame, CALLER_BINDING_CFP, GC_GUARDED_PTR(cfp)); + + rb_ary_push(data->ary, frame); } static VALUE collect_caller_bindings(rb_thread_t *th) { struct collect_caller_bindings_data data; + VALUE result; + int i; + data.ary = rb_ary_new(); - data.th = th; + backtrace_each(th, collect_caller_bindings_init, collect_caller_bindings_iseq, collect_caller_bindings_cfunc, &data); - return rb_ary_reverse(data.ary); + + result = rb_ary_reverse(data.ary); + + /* bindings should be created from top of frame */ + for (i=0; icontexts, index); } +VALUE +rb_debug_inspector_frame_self_get(const rb_debug_inspector_t *dc, long index) +{ + VALUE frame = frame_get(dc, index); + return rb_ary_entry(frame, CALLER_BINDING_SELF); +} + VALUE rb_debug_inspector_frame_class_get(const rb_debug_inspector_t *dc, long index) { VALUE frame = frame_get(dc, index); - return rb_ary_entry(frame, 0); + return rb_ary_entry(frame, CALLER_BINDING_CLASS); } VALUE rb_debug_inspector_frame_binding_get(const rb_debug_inspector_t *dc, long index) { VALUE frame = frame_get(dc, index); - return rb_ary_entry(frame, 1); + return rb_ary_entry(frame, CALLER_BINDING_BINDING); } VALUE rb_debug_inspector_frame_iseq_get(const rb_debug_inspector_t *dc, long index) { VALUE frame = frame_get(dc, index); - return rb_ary_entry(frame, 2); + return rb_ary_entry(frame, CALLER_BINDING_ISEQ); } VALUE diff --git a/vm_core.h b/vm_core.h index 7526c2004e..2e501704e8 100644 --- a/vm_core.h +++ b/vm_core.h @@ -847,6 +847,7 @@ int rb_vm_get_sourceline(const rb_control_frame_t *); VALUE rb_name_err_mesg_new(VALUE obj, VALUE mesg, VALUE recv, VALUE method); void rb_vm_stack_to_heap(rb_thread_t *th); void ruby_thread_init_stack(rb_thread_t *th); +int rb_vm_control_frame_id_and_class(const rb_control_frame_t *cfp, ID *idp, VALUE *klassp); void rb_gc_mark_machine_stack(rb_thread_t *th); diff --git a/vm_trace.c b/vm_trace.c index dd68715693..93e6212909 100644 --- a/vm_trace.c +++ b/vm_trace.c @@ -694,8 +694,6 @@ rb_tracearg_event(rb_trace_arg_t *trace_arg) return ID2SYM(get_event_id(trace_arg->event)); } -int rb_vm_control_frame_id_and_class(rb_control_frame_t *cfp, ID *idp, VALUE *klassp); - static void fill_path_and_lineno(rb_trace_arg_t *trace_arg) {