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/