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

ruby-changes:28894

From: ko1 <ko1@a...>
Date: Mon, 27 May 2013 09:21:16 +0900 (JST)
Subject: [ruby-changes:28894] ko1:r40946 (trunk): * include/ruby/ruby.h, gc.c, vm_trace.c: add internal events.

ko1	2013-05-27 09:21:02 +0900 (Mon, 27 May 2013)

  New Revision: 40946

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=40946

  Log:
    * include/ruby/ruby.h, gc.c, vm_trace.c: add internal events.
    * RUBY_INTERNAL_EVENT_NEWOBJ: object created.
    * RUBY_INTERNAL_EVENT_FREE: object freeed.
    * RUBY_INTERNAL_EVENT_GC_START: GC started.
      And rename `RUBY_EVENT_SWITCH' to `RUBY_INTERNAL_EVENT_SWITCH'.
      Internal events can not invoke any Ruby program because the tracing
      timing may be critical (under huge restriction).
      These events can be hooked only by C-extensions.
      We recommend to use rb_potponed_job_register() API to call Ruby
      program safely.
      This change is mostly written by Aman Gupta (tmm1).
      https://bugs.ruby-lang.org/issues/8107#note-12
      [Feature #8107]
    * include/ruby/debug.h, vm_trace.c: added two new APIs.
    * rb_tracearg_event_flag() returns rb_event_flag_t of this event.
    * rb_tracearg_object() returns created/freeed object.
    * ext/-test-/tracepoint/extconf.rb,
      ext/-test-/tracepoint/tracepoint.c,
      test/-ext-/tracepoint/test_tracepoint.rb: add a test.

  Added directories:
    trunk/ext/-test-/tracepoint/
    trunk/test/-ext-/tracepoint/
  Added files:
    trunk/ext/-test-/tracepoint/extconf.rb
    trunk/ext/-test-/tracepoint/tracepoint.c
    trunk/test/-ext-/tracepoint/test_tracepoint.rb
  Modified files:
    trunk/ChangeLog
    trunk/gc.c
    trunk/include/ruby/debug.h
    trunk/include/ruby/ruby.h
    trunk/internal.h
    trunk/thread.c
    trunk/vm_trace.c

Index: include/ruby/ruby.h
===================================================================
--- include/ruby/ruby.h	(revision 40945)
+++ include/ruby/ruby.h	(revision 40946)
@@ -1715,16 +1715,24 @@ int ruby_native_thread_p(void); https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L1715
 #define RUBY_EVENT_ALL       0x00ff
 
 /* for TracePoint extended events */
-#define RUBY_EVENT_B_CALL          0x0100
-#define RUBY_EVENT_B_RETURN        0x0200
-#define RUBY_EVENT_THREAD_BEGIN    0x0400
-#define RUBY_EVENT_THREAD_END      0x0800
-#define RUBY_EVENT_TRACEPOINT_ALL  0xFFFF
+#define RUBY_EVENT_B_CALL            0x0100
+#define RUBY_EVENT_B_RETURN          0x0200
+#define RUBY_EVENT_THREAD_BEGIN      0x0400
+#define RUBY_EVENT_THREAD_END        0x0800
+#define RUBY_EVENT_TRACEPOINT_ALL    0xffff
 
 /* special events */
-#define RUBY_EVENT_SPECIFIED_LINE 0x10000
-#define RUBY_EVENT_SWITCH         0x20000
-#define RUBY_EVENT_COVERAGE       0x40000
+#define RUBY_EVENT_SPECIFIED_LINE         0x010000
+#define RUBY_EVENT_COVERAGE               0x020000
+
+/* internal events */
+#define RUBY_INTERNAL_EVENT_SWITCH        0x040000
+                                       /* 0x080000 */
+#define RUBY_INTERNAL_EVENT_NEWOBJ        0x100000
+#define RUBY_INTERNAL_EVENT_FREE          0x200000
+#define RUBY_INTERNAL_EVENT_GC_START      0x400000
+#define RUBY_INTERNAL_EVENT_OBJSPACE_MASK 0x700000
+#define RUBY_INTERNAL_EVENT_MASK        0xfffe0000
 
 typedef unsigned long rb_event_flag_t;
 typedef void (*rb_event_hook_func_t)(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass);
Index: include/ruby/debug.h
===================================================================
--- include/ruby/debug.h	(revision 40945)
+++ include/ruby/debug.h	(revision 40946)
@@ -56,6 +56,7 @@ VALUE rb_tracepoint_enabled_p(VALUE tpva https://github.com/ruby/ruby/blob/trunk/include/ruby/debug.h#L56
 typedef struct rb_trace_arg_struct rb_trace_arg_t;
 rb_trace_arg_t *rb_tracearg_from_tracepoint(VALUE tpval);
 
+rb_event_flag_t rb_tracearg_event_flag(rb_trace_arg_t *trace_arg);
 VALUE rb_tracearg_event(rb_trace_arg_t *trace_arg);
 VALUE rb_tracearg_lineno(rb_trace_arg_t *trace_arg);
 VALUE rb_tracearg_path(rb_trace_arg_t *trace_arg);
@@ -65,6 +66,7 @@ VALUE rb_tracearg_binding(rb_trace_arg_t https://github.com/ruby/ruby/blob/trunk/include/ruby/debug.h#L66
 VALUE rb_tracearg_self(rb_trace_arg_t *trace_arg);
 VALUE rb_tracearg_return_value(rb_trace_arg_t *trace_arg);
 VALUE rb_tracearg_raised_exception(rb_trace_arg_t *trace_arg);
+VALUE rb_tracearg_object(rb_trace_arg_t *trace_arg);
 
 /* Postponed Job API */
 typedef void (*rb_postponed_job_func_t)(void *arg);
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 40945)
+++ ChangeLog	(revision 40946)
@@ -1,3 +1,29 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Mon May 27 09:05:17 2013  Koichi Sasada  <ko1@a...>
+
+	* include/ruby/ruby.h, gc.c, vm_trace.c: add internal events.
+	  * RUBY_INTERNAL_EVENT_NEWOBJ: object created.
+	  * RUBY_INTERNAL_EVENT_FREE: object freeed.
+	  * RUBY_INTERNAL_EVENT_GC_START: GC started.
+	  And rename `RUBY_EVENT_SWITCH' to `RUBY_INTERNAL_EVENT_SWITCH'.
+
+	  Internal events can not invoke any Ruby program because the tracing
+	  timing may be critical (under huge restriction).
+	  These events can be hooked only by C-extensions.
+	  We recommend to use rb_potponed_job_register() API to call Ruby
+	  program safely.
+
+	  This change is mostly written by Aman Gupta (tmm1).
+	  https://bugs.ruby-lang.org/issues/8107#note-12
+	  [Feature #8107]
+
+	* include/ruby/debug.h, vm_trace.c: added two new APIs.
+	  * rb_tracearg_event_flag() returns rb_event_flag_t of this event.
+	  * rb_tracearg_object() returns created/freeed object.
+
+	* ext/-test-/tracepoint/extconf.rb,
+	  ext/-test-/tracepoint/tracepoint.c,
+	  test/-ext-/tracepoint/test_tracepoint.rb: add a test.
+
 Mon May 27 08:38:21 2013  Koichi Sasada  <ko1@a...>
 
 	* ext/-test-/postponed_job/postponed_job.c: fix `init' function name.
Index: thread.c
===================================================================
--- thread.c	(revision 40945)
+++ thread.c	(revision 40946)
@@ -1989,7 +1989,7 @@ rb_threadptr_execute_interrupts(rb_threa https://github.com/ruby/ruby/blob/trunk/thread.c#L1989
 	    if (th->status == THREAD_RUNNABLE)
 		th->running_time_us += TIME_QUANTUM_USEC;
 
-	    EXEC_EVENT_HOOK(th, RUBY_EVENT_SWITCH, th->cfp->self, 0, 0, Qundef);
+	    EXEC_EVENT_HOOK(th, RUBY_INTERNAL_EVENT_SWITCH, th->cfp->self, 0, 0, Qundef);
 
 	    rb_thread_schedule_limits(limits_us);
 	}
Index: gc.c
===================================================================
--- gc.c	(revision 40945)
+++ gc.c	(revision 40946)
@@ -348,6 +348,7 @@ typedef struct rb_objspace { https://github.com/ruby/ruby/blob/trunk/gc.c#L348
     size_t count;
     size_t total_allocated_object_num;
     size_t total_freed_object_num;
+    rb_event_flag_t hook_events; /* this place may be affinity with memory cache */
     int gc_stress;
 
     struct mark_func_data_struct {
@@ -826,6 +827,27 @@ heaps_increment(rb_objspace_t *objspace) https://github.com/ruby/ruby/blob/trunk/gc.c#L827
     return FALSE;
 }
 
+void
+rb_objspace_set_event_hook(const rb_event_flag_t event)
+{
+    rb_objspace_t *objspace = &rb_objspace;
+    objspace->hook_events = event & RUBY_INTERNAL_EVENT_OBJSPACE_MASK;
+}
+
+static void
+gc_event_hook_body(rb_objspace_t *objspace, const rb_event_flag_t event, VALUE data)
+{
+    rb_thread_t *th = GET_THREAD();
+    EXEC_EVENT_HOOK(th, event, th->cfp->self, 0, 0, data);
+}
+
+#define gc_event_hook(objspace, event, data) do { \
+    if (UNLIKELY((objspace)->hook_events & (event))) { \
+	gc_event_hook_body((objspace), (event), (data)); \
+    } \
+} while (0)
+
+
 static VALUE
 newobj_of(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3)
 {
@@ -870,7 +892,6 @@ newobj_of(VALUE klass, VALUE flags, VALU https://github.com/ruby/ruby/blob/trunk/gc.c#L892
     RANY(obj)->file = rb_sourcefile();
     RANY(obj)->line = rb_sourceline();
 #endif
-    objspace->total_allocated_object_num++;
 
 #if RGENGC_PROFILE
     if (flags & FL_WB_PROTECTED) objspace->profile.generated_sunny_object_count++;
@@ -889,6 +910,9 @@ newobj_of(VALUE klass, VALUE flags, VALU https://github.com/ruby/ruby/blob/trunk/gc.c#L910
     if (rgengc_remembered(objspace, (VALUE)obj)) rb_bug("newobj: %p (%s) is remembered.\n", (void *)obj, obj_type_name(obj));
 #endif
 
+    objspace->total_allocated_object_num++;
+    gc_event_hook(objspace, RUBY_INTERNAL_EVENT_NEWOBJ, obj);
+
     return obj;
 }
 
@@ -1097,6 +1121,8 @@ make_io_deferred(RVALUE *p) https://github.com/ruby/ruby/blob/trunk/gc.c#L1121
 static int
 obj_free(rb_objspace_t *objspace, VALUE obj)
 {
+    gc_event_hook(objspace, RUBY_INTERNAL_EVENT_FREE, obj);
+
     switch (BUILTIN_TYPE(obj)) {
       case T_NIL:
       case T_FIXNUM:
@@ -3785,6 +3811,8 @@ garbage_collect_body(rb_objspace_t *objs https://github.com/ruby/ruby/blob/trunk/gc.c#L3811
 	objspace->rgengc.oldgen_object_count = 0;
     }
 
+    gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_START, 0 /* TODO: pass minor/immediate flag? */);
+
     gc_prof_timer_start(objspace, reason | (minor_gc ? GPR_FLAG_MINOR : 0));
     {
 	assert(during_gc > 0);
Index: ext/-test-/tracepoint/extconf.rb
===================================================================
--- ext/-test-/tracepoint/extconf.rb	(revision 0)
+++ ext/-test-/tracepoint/extconf.rb	(revision 40946)
@@ -0,0 +1 @@
+create_makefile("-test-/tracepoint")
Index: ext/-test-/tracepoint/tracepoint.c
===================================================================
--- ext/-test-/tracepoint/tracepoint.c	(revision 0)
+++ ext/-test-/tracepoint/tracepoint.c	(revision 40946)
@@ -0,0 +1,65 @@ https://github.com/ruby/ruby/blob/trunk/ext/-test-/tracepoint/tracepoint.c#L1
+#include "ruby/ruby.h"
+#include "ruby/debug.h"
+
+static size_t newobj_count;
+static size_t free_count;
+static size_t gc_start_count;
+static size_t objects_count;
+static VALUE objects[10];
+
+void
+tracepoint_track_objspace_events_i(VALUE tpval, void *data)
+{
+    rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
+    switch (rb_tracearg_event_flag(tparg)) {
+      case RUBY_INTERNAL_EVENT_NEWOBJ:
+	{
+	    VALUE obj = rb_tracearg_object(tparg);
+	    if (objects_count < sizeof(objects)/sizeof(VALUE)) objects[objects_count++] = obj;
+	    newobj_count++;
+	    break;
+	}
+      case RUBY_INTERNAL_EVENT_FREE:
+	{
+	    free_count++;
+	    break;
+	}
+      case RUBY_INTERNAL_EVENT_GC_START:
+	{
+	    gc_start_count++;
+	    break;
+	}
+      default:
+	rb_raise(rb_eRuntimeError, "unknown event");
+    }
+}
+
+VALUE
+tracepoint_track_objspace_events(VALUE self)
+{
+    VALUE tpval = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ | RUBY_INTERNAL_EVENT_FREE | RUBY_INTERNAL_EVENT_GC_START, tracepoint_track_objspace_events_i, 0);
+    VALUE result = rb_ary_new();
+    int i;
+
+    newobj_count = free_count = gc_start_count = objects_count = 0;
+
+    rb_tracepoint_enable(tpval);
+    rb_yield(Qundef);
+    rb_tracepoint_disable(tpval);
+
+    rb_ary_push(result, SIZET2NUM(newobj_count));
+    rb_ary_push(result, SIZET2NUM(free_count));
+    rb_ary_push(result, SIZET2NUM(gc_start_count));
+    for (i=0; i<objects_count; i++) {
+	rb_ary_push(result, objects[i]);
+    }
+
+    return result;
+}
+
+void
+Init_tracepoint(void)
+{
+    VALUE mBug = rb_define_module("Bug");
+    rb_define_module_function(mBug, "tracepoint_track_objspace_events", tracepoint_track_objspace_events, 0);
+}
Index: vm_trace.c
===================================================================
--- vm_trace.c	(revision 40945)
+++ vm_trace.c	(revision 40946)
@@ -72,6 +72,8 @@ recalc_add_ruby_vm_event_flags(rb_event_ https://github.com/ruby/ruby/blob/trunk/vm_trace.c#L72
 	}
 	ruby_vm_event_flags |= ruby_event_flag_count[i] ? (1<<i) : 0;
     }
+
+    rb_objspace_set_event_hook(ruby_vm_event_flags);
 }
 
 static void
@@ -86,6 +88,8 @@ recalc_remove_ruby_vm_event_flags(rb_eve https://github.com/ruby/ruby/blob/trunk/vm_trace.c#L88
 	}
 	ruby_vm_event_flags |= ruby_event_flag_count[i] ? (1<<i) : 0;
     }
+
+    rb_objspace_set_event_hook(ruby_vm_event_flags);
 }
 
 /* add/remove hooks */
@@ -260,7 +264,7 @@ exec_hooks(rb_thread_t *th, rb_hook_list https://github.com/ruby/ruby/blob/trunk/vm_trace.c#L264
 	rb_event_hook_t *hook;
 
 	for (hook = list->hooks; hook; hook = hook->next) {
-	    if (LIKELY(!(hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED)) && (trace_arg->event & hook->events)) {
+	    if (!(hook->hook_flags & RUBY_EVENT_HOOK_FLAG_DELETED) && (trace_arg->event & hook->events)) {
 		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);
 		}
@@ -692,6 +696,12 @@ rb_tracearg_from_tracepoint(VALUE tpval) https://github.com/ruby/ruby/blob/trunk/vm_trace.c#L696
     return get_trace_arg();
 }
 
+rb_event_flag_t
+rb_tracearg_event_flag(rb_trace_arg_t *trace_arg)
+{
+    return trace_arg->event;
+}
+
 VALUE
 rb_tracearg_event(rb_trace_arg_t *trace_arg)
 {
@@ -805,6 +815,21 @@ rb_tracearg_raised_exception(rb_trace_ar https://github.com/ruby/ruby/blob/trunk/vm_trace.c#L815
 	/* ok */
     }
     else {
+	rb_raise(rb_eRuntimeError, "not supported by this event");
+    }
+    if (trace_arg->data == Qundef) {
+	rb_bug("tp_attr_raised_exception_m: unreachable");
+    }
+    return trace_arg->data;
+}
+
+VALUE
+rb_tracearg_object(rb_trace_arg_t *trace_arg)
+{
+    if (trace_arg->event & (RUBY_INTERNAL_EVENT_NEWOBJ | RUBY_INTERNAL_EVENT_FREE)) {
+	/* ok */
+    }
+    else {
 	rb_raise(rb_eRuntimeError, "not supported by this event");
     }
     if (trace_arg->data == Qundef) {
Index: internal.h
===================================================================
--- internal.h	(revision 40945)
+++ internal.h	(revision 40946)
@@ -188,6 +188,7 @@ void rb_w32_init_file(void); https://github.com/ruby/ruby/blob/trunk/internal.h#L188
 /* gc.c */
 void Init_heap(void);
 void *ruby_mimmalloc(size_t size);
+void rb_objspace_set_event_hook(const rb_event_flag_t event);
 
 /* hash.c */
 struct st_table *rb_hash_tbl_raw(VALUE hash);
Index: test/-ext-/tracepoint/test_tracepoint.rb
===================================================================
--- test/-ext-/tracepoint/test_tracepoint.rb	(revision 0)
+++ test/-ext-/tracepoint/test_tracepoint.rb	(revision 40946)
@@ -0,0 +1,40 @@ https://github.com/ruby/ruby/blob/trunk/test/-ext-/tracepoint/test_tracepoint.rb#L1
+require 'test/unit'
+require '-test-/tracepoint'
+
+class TestTracepointObj < Test::Unit::TestCase
+  def test_not_available_from_ruby
+    assert_raises ArgumentError do
+      TracePoint.trace(:obj_new){}
+    end
+  end
+
+  def test_tracks_objspace_events
+    result = Bug.tracepoint_track_objspace_events{
+      99
+      'abc'
+      v="foobar"
+      Object.new
+      nil
+    }
+
+    newobj_count, free_count, gc_start_count, *newobjs = *result
+    assert_equal 2, newobj_count
+    assert_equal 2, newobjs.size
+    assert_equal 'foobar', newobjs[0]
+    assert_equal Object, newobjs[1].class
+
+    stat1 = {}
+    stat2 = {}
+    GC.stat(stat1)
+    result = Bug.tracepoint_track_objspace_events{
+      1_000_000.times{''}
+    }
+    GC.stat(stat2)
+
+    newobj_count, free_count, gc_start_count, *newobjs = *result
+
+    assert_operator stat2[:total_allocated_object] - stat1[:total_allocated_object], :>=, newobj_count
+    assert_operator stat2[:total_freed_object] - stat1[:total_freed_object], :>=, free_count
+    assert_operator stat2[:count] - stat1[:count], :==, gc_start_count
+  end
+end

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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