1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* 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().



git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37094 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
ko1 2012-10-05 08:14:09 +00:00
parent 7cc6bfa329
commit e03d6d9eb8
5 changed files with 202 additions and 31 deletions

View file

@ -1,3 +1,20 @@
Fri Oct 05 15:26:18 2012 Koichi Sasada <ko1@atdot.net>
* 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@gmail.com>
* gc.c (chain_finalized_object): remove to check a mark flag since

View file

@ -37,6 +37,7 @@ size_t rb_ary_memsize(VALUE);
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 @@ count_tdata_objects(int argc, VALUE *argv, VALUE self)
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 @@ Init_objspace(void)
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);
}

140
gc.c
View file

@ -273,6 +273,11 @@ typedef struct rb_objspace {
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
@ -1137,31 +1142,41 @@ struct os_each_struct {
VALUE of;
};
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 @@ gc_mark_ptr(rb_objspace_t *objspace, VALUE ptr)
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 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr)
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 @@ rb_gc_unregister_address(VALUE *addr)
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 @@ garbage_collect(rb_objspace_t *objspace)
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 @@ garbage_collect(rb_objspace_t *objspace)
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 @@ rb_gc_set_params(void)
}
}
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 ------------------------
*/

3
gc.h
View file

@ -87,7 +87,10 @@ int ruby_get_stack_grow_direction(volatile VALUE *addr);
#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);

View file

@ -68,4 +68,30 @@ class TestObjSpace < Test::Unit::TestCase
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