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

ruby-changes:25042

From: ko1 <ko1@a...>
Date: Fri, 5 Oct 2012 17:15:33 +0900 (JST)
Subject: [ruby-changes:25042] ko1:r37094 (trunk): * ext/objspace/objspace.c: add ObjectSpace#reachable_objects_from.

ko1	2012-10-05 17:14:09 +0900 (Fri, 05 Oct 2012)

  New Revision: 37094

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

  Log:
    * ext/objspace/objspace.c: add ObjectSpace#reachable_objects_from.
      This method returns an array of objects referenced by given object.
      If given object is special objects such as true/false/nil/Fixnum etc
      then it returns nil. See rdoc for details.
      [ruby-core:39772]
    * test/objspace/test_objspace.rb: add a test for this method.
    * gc.c: add rb_objspace_reachable_objects_from().
      To make this function, add several member `mark_func_data'
      to rb_objspace_t.  If mark_func_data is not null, then
      gc_mark() calls mark_func_data::mark_func.
    * gc.h: export rb_objspace_reachable_objects_from().

  Modified files:
    trunk/ChangeLog
    trunk/ext/objspace/objspace.c
    trunk/gc.c
    trunk/gc.h
    trunk/test/objspace/test_objspace.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 37093)
+++ ChangeLog	(revision 37094)
@@ -1,3 +1,20 @@
+Fri Oct 05 15:26:18 2012  Koichi Sasada  <ko1@a...>
+
+	* ext/objspace/objspace.c: add ObjectSpace#reachable_objects_from.
+	  This method returns an array of objects referenced by given object.
+	  If given object is special objects such as true/false/nil/Fixnum etc
+	  then it returns nil. See rdoc for details.
+	  [ruby-core:39772]
+
+	* test/objspace/test_objspace.rb: add a test for this method.
+
+	* gc.c: add rb_objspace_reachable_objects_from().
+	  To make this function, add several member `mark_func_data'
+	  to rb_objspace_t.  If mark_func_data is not null, then
+	  gc_mark() calls mark_func_data::mark_func.
+
+	* gc.h: export rb_objspace_reachable_objects_from().
+
 Fri Oct  5 16:04:33 2012  Narihiro Nakamura  <authornari@g...>
 
 	* gc.c (chain_finalized_object): remove to check a mark flag since
Index: gc.c
===================================================================
--- gc.c	(revision 37093)
+++ gc.c	(revision 37094)
@@ -273,6 +273,11 @@
     struct gc_list *global_list;
     size_t count;
     int gc_stress;
+
+    struct mark_func_data_struct {
+	VALUE data;
+	void (*mark_func)(struct rb_objspace *objspace, VALUE v);
+    } *mark_func_data;
 } rb_objspace_t;
 
 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
@@ -1138,30 +1143,40 @@
 };
 
 static int
+internal_object_p(VALUE obj)
+{
+    RVALUE *p = (RVALUE *)obj;
+
+    if (p->as.basic.flags) {
+	switch (BUILTIN_TYPE(p)) {
+	  case T_NONE:
+	  case T_ICLASS:
+	  case T_NODE:
+	  case T_ZOMBIE:
+	    break;
+	  case T_CLASS:
+	    if (FL_TEST(p, FL_SINGLETON))
+	      break;
+	  default:
+	    if (!p->as.basic.klass) break;
+	    return 0;
+	}
+    }
+    return 1;
+}
+
+static int
 os_obj_of_i(void *vstart, void *vend, size_t stride, void *data)
 {
     struct os_each_struct *oes = (struct os_each_struct *)data;
     RVALUE *p = (RVALUE *)vstart, *pend = (RVALUE *)vend;
-    volatile VALUE v;
 
     for (; p != pend; p++) {
-	if (p->as.basic.flags) {
-	    switch (BUILTIN_TYPE(p)) {
-	      case T_NONE:
-	      case T_ICLASS:
-	      case T_NODE:
-	      case T_ZOMBIE:
-		continue;
-	      case T_CLASS:
-		if (FL_TEST(p, FL_SINGLETON))
-		    continue;
-	      default:
-		if (!p->as.basic.klass) continue;
-		v = (VALUE)p;
-		if (!oes->of || rb_obj_is_kind_of(v, oes->of)) {
-		    rb_yield(v);
-		    oes->num++;
-		}
+	volatile VALUE v = (VALUE)p;
+	if (!internal_object_p(v)) {
+	    if (!oes->of || rb_obj_is_kind_of(v, oes->of)) {
+		rb_yield(v);
+		oes->num++;
 	    }
 	}
     }
@@ -2521,17 +2536,31 @@
     return 1;
 }
 
+static int
+markable_object_p(rb_objspace_t *objspace, VALUE ptr)
+{
+    register RVALUE *obj = RANY(ptr);
+
+    if (rb_special_const_p(ptr)) return 0; /* special const not marked */
+    if (obj->as.basic.flags == 0) return 0 ;       /* free cell */
+
+    return 1;
+}
+
 static void
 gc_mark(rb_objspace_t *objspace, VALUE ptr)
 {
-    register RVALUE *obj;
+    if (!markable_object_p(objspace, ptr)) {
+	return;
+    }
 
-    obj = RANY(ptr);
-    if (rb_special_const_p(ptr)) return; /* special const not marked */
-    if (obj->as.basic.flags == 0) return;       /* free cell */
-    if (!gc_mark_ptr(objspace, ptr)) return;	/* already marked */
-
-    push_mark_stack(&objspace->mark_stack, ptr);
+    if (LIKELY(objspace->mark_func_data == 0)) {
+	if (!gc_mark_ptr(objspace, ptr)) return; /* already marked */
+	push_mark_stack(&objspace->mark_stack, ptr);
+    }
+    else {
+	objspace->mark_func_data->mark_func(objspace, ptr);
+    }
 }
 
 void
@@ -2548,10 +2577,16 @@
     goto marking;		/* skip */
 
   again:
-    obj = RANY(ptr);
-    if (rb_special_const_p(ptr)) return; /* special const not marked */
-    if (obj->as.basic.flags == 0) return;       /* free cell */
-    if (!gc_mark_ptr(objspace, ptr)) return;  /* already marked */
+    if (LIKELY(objspace->mark_func_data == 0)) {
+	obj = RANY(ptr);
+	if (rb_special_const_p(ptr)) return; /* special const not marked */
+	if (obj->as.basic.flags == 0) return;       /* free cell */
+	if (!gc_mark_ptr(objspace, ptr)) return;  /* already marked */
+    }
+    else {
+	gc_mark(objspace, ptr);
+	return;
+    }
 
   marking:
     if (FL_TEST(obj, FL_EXIVAR)) {
@@ -2953,6 +2988,8 @@
 static int
 garbage_collect(rb_objspace_t *objspace)
 {
+    struct mark_func_data_struct *prev_mark_func_data;
+
     if (GC_NOTIFY) printf("start garbage_collect()\n");
 
     if (!heaps) {
@@ -2964,6 +3001,9 @@
 
     gc_prof_timer_start(objspace);
 
+    prev_mark_func_data = objspace->mark_func_data;
+    objspace->mark_func_data = 0;
+
     rest_sweep(objspace);
 
     during_gc++;
@@ -2973,6 +3013,8 @@
     gc_sweep(objspace);
     gc_prof_sweep_timer_stop(objspace);
 
+    objspace->mark_func_data = prev_mark_func_data;
+
     gc_prof_timer_stop(objspace, Qtrue);
     if (GC_NOTIFY) printf("end garbage_collect()\n");
     return TRUE;
@@ -3241,6 +3283,46 @@
     }
 }
 
+static void
+collect_refs(rb_objspace_t *objspace, VALUE obj)
+{
+    if (markable_object_p(objspace, obj) && !internal_object_p(obj)) {
+	st_insert((st_table *)objspace->mark_func_data->data, obj, Qtrue);
+    }
+}
+
+static int
+collect_keys(st_data_t key, st_data_t value, st_data_t data)
+{
+    VALUE ary = (VALUE)data;
+    rb_ary_push(ary, (VALUE)key);
+    return ST_CONTINUE;
+}
+
+VALUE
+rb_objspace_reachable_objects_from(VALUE obj)
+{
+    rb_objspace_t *objspace = &rb_objspace;
+
+    if (markable_object_p(objspace, obj)) {
+	st_table *refs = st_init_numtable();
+	struct mark_func_data_struct mfd;
+	VALUE ret = rb_ary_new();
+	mfd.mark_func = collect_refs;
+	mfd.data = (VALUE)refs;
+	objspace->mark_func_data = &mfd;
+
+	gc_mark_children(objspace, obj);
+
+	objspace->mark_func_data = 0;
+	st_foreach(refs, collect_keys, (st_data_t)ret);
+	return ret;
+    }
+    else {
+	return Qnil;
+    }
+}
+
 /*
   ------------------------ Extended allocator ------------------------
 */
Index: gc.h
===================================================================
--- gc.h	(revision 37093)
+++ gc.h	(revision 37094)
@@ -87,7 +87,10 @@
 #pragma GCC visibility push(default)
 #endif
 
+/* exports for objspace module */
 size_t rb_objspace_data_type_memsize(VALUE obj);
+VALUE rb_objspace_reachable_objects_from(VALUE obj);
+
 void rb_objspace_each_objects(
     int (*callback)(void *start, void *end, size_t stride, void *data),
     void *data);
Index: ext/objspace/objspace.c
===================================================================
--- ext/objspace/objspace.c	(revision 37093)
+++ ext/objspace/objspace.c	(revision 37094)
@@ -37,6 +37,7 @@
 size_t rb_io_memsize(const rb_io_t *);
 size_t rb_generic_ivar_memsize(VALUE);
 size_t rb_objspace_data_type_memsize(VALUE obj);
+VALUE rb_objspace_reachable_objects_from(VALUE obj);
 
 static size_t
 memsize_of(VALUE obj)
@@ -626,6 +627,47 @@
     return hash;
 }
 
+/*
+ *  call-seq:
+ *     ObjectSpace.reachable_objects_from(obj) -> array or nil
+ *
+ *  [MRI specific feature] Return all reachable objects from `obj'.
+ *
+ *  This method returns all reachable objects from `obj'.
+ *  If `obj' has references two or more references to same object `x',
+ *  them returned array only include one `x' object.
+ *  If `obj' is non-markable (non-heap management) object such as
+ *  true, false, nil, symbols and Fixnums (and Flonum) them it simply
+ *  returns nil.
+ *
+ *  With this method, you can find memory leaks.
+ *
+ *  This method is not expected to work except C Ruby.
+ *
+ *  Example:
+ *    ObjectSpace.reachable_objects_from(['a', 'b', 'c'])
+ *    #=> [Array, 'a', 'b', 'c']
+ *
+ *    ObjectSpace.reachable_objects_from(['a', 'a', 'a'])
+ *    #=> [Array, 'a', 'a', 'a'] # all 'a' strings have different object id
+ *
+ *    ObjectSpace.reachable_objects_from([v = 'a', v, v])
+ *    #=> [Array, 'a']
+ *
+ *    ObjectSpace.reachable_objects_from(1)
+ *    #=> nil # 1 is not markable (heap managed) object
+ *
+ *  Limitation: Current implementation can't acquire internal objects.
+ *              This means that you can't acquire complete object graph
+ *              (heap snapshot).  This is future work.
+ *
+ */
+static VALUE
+reachable_objects_from(VALUE self, VALUE obj)
+{
+    return rb_objspace_reachable_objects_from(obj);
+}
+
 /* objspace library extends ObjectSpace module and add several
  * methods to get internal statistic information about
  * object/memory management.
@@ -642,10 +684,11 @@
     VALUE rb_mObjSpace = rb_const_get(rb_cObject, rb_intern("ObjectSpace"));
 
     rb_define_module_function(rb_mObjSpace, "memsize_of", memsize_of_m, 1);
-    rb_define_module_function(rb_mObjSpace, "memsize_of_all",
-			      memsize_of_all_m, -1);
+    rb_define_module_function(rb_mObjSpace, "memsize_of_all", memsize_of_all_m, -1);
 
     rb_define_module_function(rb_mObjSpace, "count_objects_size", count_objects_size, -1);
     rb_define_module_function(rb_mObjSpace, "count_nodes", count_nodes, -1);
     rb_define_module_function(rb_mObjSpace, "count_tdata_objects", count_tdata_objects, -1);
+
+    rb_define_module_function(rb_mObjSpace, "reachable_objects_from", reachable_objects_from, 1);
 }
Index: test/objspace/test_objspace.rb
===================================================================
--- test/objspace/test_objspace.rb	(revision 37093)
+++ test/objspace/test_objspace.rb	(revision 37094)
@@ -68,4 +68,30 @@
     ObjectSpace.count_tdata_objects(arg)
     assert_equal(false, arg.empty?)
   end
+
+  def test_reachable_objects_from
+    assert_equal(nil, ObjectSpace.reachable_objects_from(nil))
+    assert_equal([Array, 'a', 'b', 'c'], ObjectSpace.reachable_objects_from(['a', 'b', 'c']))
+
+    assert_equal([Array, 'a', 'a', 'a'], ObjectSpace.reachable_objects_from(['a', 'a', 'a']))
+    assert_equal([Array, 'a', 'a'], ObjectSpace.reachable_objects_from(['a', v = 'a', v]))
+    assert_equal([Array, 'a'], ObjectSpace.reachable_objects_from([v = 'a', v, v]))
+
+    long_ary = Array.new(1_000){''}
+    max = 0
+
+    ObjectSpace.each_object{|o|
+      refs = ObjectSpace.reachable_objects_from(o)
+      max = [refs.size, max].max
+
+      unless refs.nil?
+        refs.each{|ro|
+          # check this referenced object is not internal object
+          assert_equal(false, ro.nil?)
+        }
+      end
+    }
+    STDERR.puts max
+    assert_equal(true, max >= 1_001) # 1000 elems + Array class
+  end
 end

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

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