ruby-changes:26918
From: ko1 <ko1@a...>
Date: Tue, 29 Jan 2013 17:25:43 +0900 (JST)
Subject: [ruby-changes:26918] ko1:r38970 (trunk): * vm_backtrace.c: fix issue of rb_debug_inspector_open().
ko1 2013-01-29 17:25:32 +0900 (Tue, 29 Jan 2013) New Revision: 38970 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=38970 Log: * 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. Added directories: trunk/ext/-test-/debug/ trunk/test/-ext-/debug/ Added files: trunk/ext/-test-/debug/extconf.rb trunk/ext/-test-/debug/init.c trunk/ext/-test-/debug/inspector.c trunk/test/-ext-/debug/test_debug.rb Modified files: trunk/ChangeLog trunk/include/ruby/debug.h trunk/vm.c trunk/vm_backtrace.c trunk/vm_core.h trunk/vm_trace.c Index: include/ruby/debug.h =================================================================== --- include/ruby/debug.h (revision 38969) +++ include/ruby/debug.h (revision 38970) @@ -31,8 +31,9 @@ typedef struct rb_debug_inspector_struct https://github.com/ruby/ruby/blob/trunk/include/ruby/debug.h#L31 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); Index: ChangeLog =================================================================== --- ChangeLog (revision 38969) +++ ChangeLog (revision 38970) @@ -1,3 +1,24 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Tue Jan 29 17:03:28 2013 Koichi Sasada <ko1@a...> + + * 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 <nobu@r...> * vm_trace.c (rb_tracepoint_enable, rb_tracepoint_disable): check safe Index: vm_core.h =================================================================== --- vm_core.h (revision 38969) +++ vm_core.h (revision 38970) @@ -847,6 +847,7 @@ int rb_vm_get_sourceline(const rb_contro https://github.com/ruby/ruby/blob/trunk/vm_core.h#L847 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); Index: vm_backtrace.c =================================================================== --- vm_backtrace.c (revision 38969) +++ vm_backtrace.c (revision 38970) @@ -1012,8 +1012,15 @@ struct rb_debug_inspector_struct { https://github.com/ruby/ruby/blob/trunk/vm_backtrace.c#L1012 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, https://github.com/ruby/ruby/blob/trunk/vm_backtrace.c#L1030 /* */ } +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; i<RARRAY_LEN(result); i++) { + VALUE entry = rb_ary_entry(result, i); + VALUE cfp_val = rb_ary_entry(entry, CALLER_BINDING_BINDING); + + if (!NIL_P(cfp_val)) { + rb_control_frame_t *cfp = GC_GUARDED_PTR_REF(cfp_val); + rb_ary_store(entry, CALLER_BINDING_BINDING, rb_binding_new_with_cfp(th, cfp)); + } + } + + return result; } /* @@ -1100,24 +1152,31 @@ frame_get(const rb_debug_inspector_t *dc https://github.com/ruby/ruby/blob/trunk/vm_backtrace.c#L1152 } 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 Index: ext/-test-/debug/init.c =================================================================== --- ext/-test-/debug/init.c (revision 0) +++ ext/-test-/debug/init.c (revision 38970) @@ -0,0 +1,11 @@ https://github.com/ruby/ruby/blob/trunk/ext/-test-/debug/init.c#L1 +#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); +} Index: ext/-test-/debug/inspector.c =================================================================== --- ext/-test-/debug/inspector.c (revision 0) +++ ext/-test-/debug/inspector.c (revision 38970) @@ -0,0 +1,32 @@ https://github.com/ruby/ruby/blob/trunk/ext/-test-/debug/inspector.c#L1 +#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); +} Index: ext/-test-/debug/extconf.rb =================================================================== --- ext/-test-/debug/extconf.rb (revision 0) +++ ext/-test-/debug/extconf.rb (revision 38970) @@ -0,0 +1,6 @@ https://github.com/ruby/ruby/blob/trunk/ext/-test-/debug/extconf.rb#L1 +$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") Index: vm_trace.c =================================================================== --- vm_trace.c (revision 38969) +++ vm_trace.c (revision 38970) @@ -694,8 +694,6 @@ rb_tracearg_event(rb_trace_arg_t *trace_ https://github.com/ruby/ruby/blob/trunk/vm_trace.c#L694 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) { Index: vm.c =================================================================== --- vm.c (revision 38969) +++ vm.c (revision 38970) @@ -1414,7 +1414,7 @@ rb_iseq_eval_main(VALUE iseqval) https://github.com/ruby/ruby/blob/trunk/vm.c#L1414 } 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) { Index: test/-ext-/debug/test_debug.rb =================================================================== --- test/-ext-/debug/test_debug.rb (revision 0) +++ test/-ext-/debug/test_debug.rb (revision 38970) @@ -0,0 +1,58 @@ https://github.com/ruby/ruby/blob/trunk/test/-ext-/debug/test_debug.rb#L1 +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 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/