ruby-changes:53790
From: ko1 <ko1@a...>
Date: Tue, 27 Nov 2018 05:16:20 +0900 (JST)
Subject: [ruby-changes:53790] ko1:r66008 (trunk): `TracePoint#enable(target_line:)` is supported. [Feature #15289]
ko1 2018-11-27 05:16:14 +0900 (Tue, 27 Nov 2018) New Revision: 66008 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=66008 Log: `TracePoint#enable(target_line:)` is supported. [Feature #15289] * vm_trace.c: `TracePoint#enable(target_line:)` is supported. This option enables a hook only at specified target_line. target_line should be combination with target and :line event. Modified files: trunk/iseq.c trunk/iseq.h trunk/prelude.rb trunk/test/ruby/test_settracefunc.rb trunk/vm_core.h trunk/vm_trace.c Index: vm_trace.c =================================================================== --- vm_trace.c (revision 66007) +++ vm_trace.c (revision 66008) @@ -40,6 +40,7 @@ typedef struct rb_event_hook_struct { https://github.com/ruby/ruby/blob/trunk/vm_trace.c#L40 struct { rb_thread_t *th; + unsigned int target_line; } filter; } rb_event_hook_t; @@ -107,7 +108,8 @@ alloc_event_hook(rb_event_hook_func_t fu https://github.com/ruby/ruby/blob/trunk/vm_trace.c#L108 hook->data = data; /* no filters */ - hook->filter.th = 0; + hook->filter.th = NULL; + hook->filter.target_line = 0; return hook; } @@ -293,7 +295,8 @@ exec_hooks_body(const rb_execution_conte https://github.com/ruby/ruby/blob/trunk/vm_trace.c#L295 for (hook = list->hooks; hook; hook = hook->next) { if (!(hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED) && (trace_arg->event & hook->events) && - (hook->filter.th == 0 || hook->filter.th == rb_ec_thread_ptr(ec))) { + (LIKELY(hook->filter.th == 0) || hook->filter.th == rb_ec_thread_ptr(ec)) && + (LIKELY(hook->filter.target_line == 0) || (hook->filter.target_line == (unsigned int)rb_vm_get_sourceline(ec->cfp)))) { if (!(hook->hook_flags & RUBY_EVENT_HOOK_FLAG_RAW_ARG)) { (*hook->func)(trace_arg->event, hook->data, trace_arg->self, trace_arg->id, trace_arg->klass); } @@ -1154,21 +1157,31 @@ iseq_of(VALUE target) https://github.com/ruby/ruby/blob/trunk/vm_trace.c#L1157 const rb_method_definition_t *rb_method_def(VALUE method); /* proc.c */ static VALUE -rb_tracepoint_enable_for_target(VALUE tpval, VALUE target) +rb_tracepoint_enable_for_target(VALUE tpval, VALUE target, VALUE target_line) { rb_tp_t *tp = tpptr(tpval); const rb_iseq_t *iseq = iseq_of(target); int n; + unsigned int line = 0; if (tp->tracing > 0) { rb_raise(rb_eArgError, "can't nest-enable a targetting TracePoint"); } + if (!NIL_P(target_line)) { + if ((tp->events & RUBY_EVENT_LINE) == 0) { + rb_raise(rb_eArgError, "target_line is specified, but line event is not specified"); + } + else { + line = NUM2UINT(target_line); + } + } + VM_ASSERT(tp->local_target_set == Qfalse); tp->local_target_set = rb_obj_hide(rb_ident_hash_new()); /* iseq */ - n = rb_iseq_add_local_tracepoint_recursively(iseq, tp->events, tpval); + n = rb_iseq_add_local_tracepoint_recursively(iseq, tp->events, tpval, line); rb_hash_aset(tp->local_target_set, (VALUE)iseq, Qtrue); /* bmethod */ @@ -1177,7 +1190,7 @@ rb_tracepoint_enable_for_target(VALUE tp https://github.com/ruby/ruby/blob/trunk/vm_trace.c#L1190 if (def->type == VM_METHOD_TYPE_BMETHOD && (tp->events & (RUBY_EVENT_CALL | RUBY_EVENT_RETURN))) { def->body.bmethod.hooks = ZALLOC(rb_hook_list_t); - rb_hook_list_connect_tracepoint(target, def->body.bmethod.hooks, tpval); + rb_hook_list_connect_tracepoint(target, def->body.bmethod.hooks, tpval, 0); rb_hash_aset(tp->local_target_set, target, Qfalse); n++; @@ -1240,11 +1253,12 @@ rb_tracepoint_disable(VALUE tpval) https://github.com/ruby/ruby/blob/trunk/vm_trace.c#L1253 } void -rb_hook_list_connect_tracepoint(VALUE target, rb_hook_list_t *list, VALUE tpval) +rb_hook_list_connect_tracepoint(VALUE target, rb_hook_list_t *list, VALUE tpval, unsigned int target_line) { rb_tp_t *tp = tpptr(tpval); rb_event_hook_t *hook = alloc_event_hook((rb_event_hook_func_t)tp_call_trace, tp->events, tpval, RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG); + hook->filter.target_line = target_line; hook_list_connect(target, list, hook, FALSE); } @@ -1306,16 +1320,19 @@ rb_hook_list_remove_tracepoint(rb_hook_l https://github.com/ruby/ruby/blob/trunk/vm_trace.c#L1320 * */ static VALUE -tracepoint_enable_m(VALUE tpval, VALUE target) +tracepoint_enable_m(VALUE tpval, VALUE target, VALUE target_line) { rb_tp_t *tp = tpptr(tpval); int previous_tracing = tp->tracing; if (NIL_P(target)) { + if (!NIL_P(target_line)) { + rb_raise(rb_eArgError, "only target_line is specified"); + } rb_tracepoint_enable(tpval); } else { - rb_tracepoint_enable_for_target(tpval, target); + rb_tracepoint_enable_for_target(tpval, target, target_line); } if (rb_block_given_p()) { @@ -1706,7 +1723,7 @@ Init_vm_trace(void) https://github.com/ruby/ruby/blob/trunk/vm_trace.c#L1723 */ rb_define_singleton_method(rb_cTracePoint, "trace", tracepoint_trace_s, -1); - rb_define_method(rb_cTracePoint, "__enable", tracepoint_enable_m, 1); + rb_define_method(rb_cTracePoint, "__enable", tracepoint_enable_m, 2); rb_define_method(rb_cTracePoint, "disable", tracepoint_disable_m, 0); rb_define_method(rb_cTracePoint, "enabled?", rb_tracepoint_enabled_p, 0); Index: iseq.c =================================================================== --- iseq.c (revision 66007) +++ iseq.c (revision 66008) @@ -2980,7 +2980,7 @@ rb_iseq_trace_flag_cleared(const rb_iseq https://github.com/ruby/ruby/blob/trunk/iseq.c#L2980 } static int -iseq_add_local_tracepoint(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval) +iseq_add_local_tracepoint(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval, unsigned int target_line) { unsigned int pc; int n = 0; @@ -2990,18 +2990,29 @@ iseq_add_local_tracepoint(const rb_iseq_ https://github.com/ruby/ruby/blob/trunk/iseq.c#L2990 VM_ASSERT((iseq->flags & ISEQ_USE_COMPILE_DATA) == 0); for (pc=0; pc<body->iseq_size;) { - rb_event_flag_t pc_events = rb_iseq_event_flags(iseq, pc); - if (pc_events & turnon_events) { + const struct iseq_insn_info_entry *entry = get_insn_info(iseq, pc); + rb_event_flag_t pc_events = entry->events; + rb_event_flag_t target_events = turnon_events; + unsigned int line = (int)entry->line_no; + + if (target_line == 0 || target_line == line) { + /* ok */ + } + else { + target_events &= ~RUBY_EVENT_LINE; + } + + if (pc_events & target_events) { n++; } - pc += encoded_iseq_trace_instrument(&iseq_encoded[pc], pc_events & (turnon_events | iseq->aux.global_trace_events)); + pc += encoded_iseq_trace_instrument(&iseq_encoded[pc], pc_events & (target_events | iseq->aux.global_trace_events)); } if (n > 0) { if (iseq->local_hooks == NULL) { ((rb_iseq_t *)iseq)->local_hooks = RB_ZALLOC(rb_hook_list_t); } - rb_hook_list_connect_tracepoint((VALUE)iseq, iseq->local_hooks, tpval); + rb_hook_list_connect_tracepoint((VALUE)iseq, iseq->local_hooks, tpval, target_line); } return n; @@ -3010,6 +3021,7 @@ iseq_add_local_tracepoint(const rb_iseq_ https://github.com/ruby/ruby/blob/trunk/iseq.c#L3021 struct trace_set_local_events_struct { rb_event_flag_t turnon_events; VALUE tpval; + unsigned int target_line; int n; }; @@ -3017,16 +3029,17 @@ static void https://github.com/ruby/ruby/blob/trunk/iseq.c#L3029 iseq_add_local_tracepoint_i(const rb_iseq_t *iseq, void *p) { struct trace_set_local_events_struct *data = (struct trace_set_local_events_struct *)p; - data->n += iseq_add_local_tracepoint(iseq, data->turnon_events, data->tpval); + data->n += iseq_add_local_tracepoint(iseq, data->turnon_events, data->tpval, data->target_line); iseq_iterate_children(iseq, iseq_add_local_tracepoint_i, p); } int -rb_iseq_add_local_tracepoint_recursively(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval) +rb_iseq_add_local_tracepoint_recursively(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval, unsigned int target_line) { struct trace_set_local_events_struct data; data.turnon_events = turnon_events; data.tpval = tpval; + data.target_line = target_line; data.n = 0; iseq_add_local_tracepoint_i(iseq, (void *)&data); Index: iseq.h =================================================================== --- iseq.h (revision 66007) +++ iseq.h (revision 66008) @@ -148,7 +148,7 @@ void rb_ibf_load_iseq_complete(rb_iseq_t https://github.com/ruby/ruby/blob/trunk/iseq.h#L148 const rb_iseq_t *rb_iseq_ibf_load(VALUE str); VALUE rb_iseq_ibf_load_extra_data(VALUE str); void rb_iseq_init_trace(rb_iseq_t *iseq); -int rb_iseq_add_local_tracepoint_recursively(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval); +int rb_iseq_add_local_tracepoint_recursively(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval, unsigned int target_line); int rb_iseq_remove_local_tracepoint_recursively(const rb_iseq_t *iseq, VALUE tpval); #if VM_INSN_INFO_TABLE_IMPL == 2 Index: prelude.rb =================================================================== --- prelude.rb (revision 66007) +++ prelude.rb (revision 66008) @@ -133,8 +133,8 @@ class IO https://github.com/ruby/ruby/blob/trunk/prelude.rb#L133 end class TracePoint - def enable target: nil, &blk - self.__enable target, &blk + def enable target: nil, target_line: nil, &blk + self.__enable target, target_line, &blk end end Index: test/ruby/test_settracefunc.rb =================================================================== --- test/ruby/test_settracefunc.rb (revision 66007) +++ test/ruby/test_settracefunc.rb (revision 66008) @@ -2056,4 +2056,31 @@ class TestSetTraceFunc < Test::Unit::Tes https://github.com/ruby/ruby/blob/trunk/test/ruby/test_settracefunc.rb#L2056 end assert_equal [:tp2, :tp2, :tp1, :tp2, :___], events end + + def test_tracepoint_enable_with_target_line + events = [] + code1 = proc{ + events << 1 + events << 2 + events << 3 + } + tp = TracePoint.new(:line) do |tp| + events << :tp + end + tp.enable(target: code1, target_line: 2064) do + code1.call + end + assert_equal [1, :tp, 2, 3], events + + + e = assert_raise(ArgumentError) do + TracePoint.new(:line){}.enable(target_line: 10){} + end + assert_equal 'only target_line is specified', e.message + + e = assert_raise(ArgumentError) do + TracePoint.new(:call){}.enable(target: code1, target_line: 10){} + end + assert_equal 'target_line is specified, but line event is not specified', e.message + end end Index: vm_core.h =================================================================== --- vm_core.h (revision 66007) +++ vm_core.h (revision 66008) @@ -1825,7 +1825,7 @@ struct rb_trace_arg_struct { https://github.com/ruby/ruby/blob/trunk/vm_core.h#L1825 void rb_hook_list_mark(rb_hook_list_t *hooks); void rb_hook_list_free(rb_hook_list_t *hooks); -void rb_hook_list_connect_tracepoint(VALUE target, rb_hook_list_t *list, VALUE tpval); +void rb_hook_list_connect_tracepoint(VALUE target, rb_hook_list_t *list, VALUE tpval, unsigned int target_line); void rb_hook_list_remove_tracepoint(rb_hook_list_t *list, VALUE tpval); void rb_exec_event_hooks(struct rb_trace_arg_struct *trace_arg, rb_hook_list_t *hooks, int pop_p); -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/