[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]