From 241c1a5459d986187ec42f166535d51b37202532 Mon Sep 17 00:00:00 2001 From: tmm1 Date: Thu, 5 Dec 2013 07:45:13 +0000 Subject: [PATCH] gc.c: expose GC.stat() to C-api via rb_gc_stat() * include/ruby/intern.h: add rb_gc_stat() for access to GC.stat variables from c-api * gc.c (rb_gc_stat): new c-api method. accepts either VALUE hash like GC.stat, or VALUE symbol key and returns size_t directly. the second form is useful to avoid allocations, i.e. for usage inside INTERNAL_EVENT_GC tracepoints. * gc.c (gc_stat): add GC.stat(:key) to return single value instead of hash * gc.c (gc_stat_internal): helper method to retrieve single or all stat values * test/ruby/test_gc.rb (class TestGc): test for new behavior * NEWS: note about this new api git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@44005 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 13 ++++ NEWS | 6 +- gc.c | 157 +++++++++++++++++++++++++++--------------- include/ruby/intern.h | 1 + test/ruby/test_gc.rb | 6 ++ 5 files changed, 126 insertions(+), 57 deletions(-) diff --git a/ChangeLog b/ChangeLog index ac1852865c..495aeab0cf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +Thu Dec 5 16:11:04 2013 Aman Gupta + + * include/ruby/intern.h: add rb_gc_stat() for access to GC.stat + variables from c-api + * gc.c (rb_gc_stat): new c-api method. accepts either VALUE hash like + GC.stat, or VALUE symbol key and returns size_t directly. the second + form is useful to avoid allocations, i.e. for usage inside + INTERNAL_EVENT_GC tracepoints. + * gc.c (gc_stat): add GC.stat(:key) to return single value instead of hash + * gc.c (gc_stat_internal): helper method to retrieve single or all stat values + * test/ruby/test_gc.rb (class TestGc): test for new behavior + * NEWS: note about this new api + Thu Dec 5 14:40:41 2013 Nobuyoshi Nakada * hash.c (rb_hash): revert r43981 and bail out to the outermost frame diff --git a/NEWS b/NEWS index 8dcc979975..22a6b4735c 100644 --- a/NEWS +++ b/NEWS @@ -319,6 +319,9 @@ with all sufficient information, see the ChangeLog file. * rb_gc_count() added. This returns the number of times GC occurred. +* rb_gc_stat() added. This allows access to specific GC.stat() values from C + without any allocation overhead. + * rb_postponed_job_register() added. Takes a function callback which is invoked when the VM is in a consistent state, i.e. to perform work from a C signal handler. @@ -330,6 +333,7 @@ with all sufficient information, see the ChangeLog file. * RUBY_INTERNAL_EVENT_NEWOBJ * RUBY_INTERNAL_EVENT_FREEOBJ * RUBY_INTERNAL_EVENT_GC_START - * RUBY_INTERNAL_EVENT_GC_END + * RUBY_INTERNAL_EVENT_GC_END_MARK + * RUBY_INTERNAL_EVENT_GC_END_SWEEP * Note that you *can not* specify "internal events" with normal events (such as RUBY_EVENT_CALL, RUBY_EVENT_RETURN) simultaneously. diff --git a/gc.c b/gc.c index aec6828289..ac41c013a8 100644 --- a/gc.c +++ b/gc.c @@ -5056,49 +5056,9 @@ gc_count(VALUE self) return SIZET2NUM(rb_gc_count()); } -/* - * call-seq: - * GC.stat -> Hash - * - * Returns a Hash containing information about the GC. - * - * The hash includes information about internal statistics about GC such as: - * - * { - * :count=>2, - * :heap_used=>9, - * :heap_length=>11, - * :heap_increment=>2, - * :heap_live_slot=>6836, - * :heap_free_slot=>519, - * :heap_final_slot=>0, - * :heap_swept_slot=>818, - * :total_allocated_object=>7674, - * :total_freed_object=>838, - * :malloc_increase=>181034, - * :malloc_limit=>16777216, - * :minor_gc_count=>2, - * :major_gc_count=>0, - * :remembered_shady_object=>55, - * :remembered_shady_object_limit=>0, - * :old_object=>2422, - * :old_object_limit=>0, - * :oldmalloc_increase=>277386, - * :oldmalloc_limit=>16777216 - * } - * - * The contents of the hash are implementation specific and may be changed in - * the future. - * - * This method is only expected to work on C Ruby. - * - */ - static VALUE -gc_stat(int argc, VALUE *argv, VALUE self) +gc_stat_internal(VALUE hash_or_sym, size_t *out) { - rb_objspace_t *objspace = &rb_objspace; - VALUE hash; static VALUE sym_count; static VALUE sym_heap_used, sym_heap_length, sym_heap_increment; static VALUE sym_heap_live_slot, sym_heap_free_slot, sym_heap_final_slot, sym_heap_swept_slot; @@ -5120,6 +5080,16 @@ gc_stat(int argc, VALUE *argv, VALUE self) #endif /* RGENGC_PROFILE */ #endif /* USE_RGENGC */ + rb_objspace_t *objspace = &rb_objspace; + VALUE hash = Qnil, key = Qnil; + + if (RB_TYPE_P(hash_or_sym, T_HASH)) + hash = hash_or_sym; + else if (SYMBOL_P(hash_or_sym) && out) + key = hash_or_sym; + else + rb_raise(rb_eArgError, "non-hash or symbol argument"); + if (sym_count == 0) { #define S(s) sym_##s = ID2SYM(rb_intern_const(#s)) S(count); @@ -5161,17 +5131,12 @@ gc_stat(int argc, VALUE *argv, VALUE self) #undef S } - if (rb_scan_args(argc, argv, "01", &hash) == 1) { - if (!RB_TYPE_P(hash, T_HASH)) { - rb_raise(rb_eTypeError, "non-hash given"); - } - } +#define SET(name, attr) \ + if (key == sym_##name) \ + return (*out = attr, Qnil); \ + else if (hash != Qnil) \ + rb_hash_aset(hash, sym_##name, SIZET2NUM(attr)); - if (hash == Qnil) { - hash = rb_hash_new(); - } - -#define SET(name, attr) rb_hash_aset(hash, sym_##name, SIZET2NUM(attr)) SET(count, objspace->profile.count); /* implementation dependent counters */ @@ -5211,8 +5176,15 @@ gc_stat(int argc, VALUE *argv, VALUE self) #endif SET(remembered_normal_object_count, objspace->profile.remembered_normal_object_count); SET(remembered_shady_object_count, objspace->profile.remembered_shady_object_count); -#if RGENGC_PROFILE >= 2 - { +#endif /* RGENGC_PROFILE */ +#endif /* USE_RGENGC */ +#undef SET + + if (key != Qnil) /* matched key should return above */ + rb_raise(rb_eArgError, "unknown key: %s", RSTRING_PTR(rb_id2str(SYM2ID(key)))); + +#if defined(RGENGC_PROFILE) && RGENGC_PROFILE >= 2 + if (hash != Qnil) { gc_count_add_each_types(hash, "generated_normal_object_count_types", objspace->profile.generated_normal_object_count_types); gc_count_add_each_types(hash, "generated_shady_object_count_types", objspace->profile.generated_shady_object_count_types); gc_count_add_each_types(hash, "shade_operation_count_types", objspace->profile.shade_operation_count_types); @@ -5224,12 +5196,85 @@ gc_stat(int argc, VALUE *argv, VALUE self) gc_count_add_each_types(hash, "remembered_shady_object_count_types", objspace->profile.remembered_shady_object_count_types); } #endif -#endif /* RGENGC_PROFILE */ -#endif /* USE_RGENGC */ -#undef SET + return hash; } +/* + * call-seq: + * GC.stat -> Hash + * GC.stat(hash) -> hash + * GC.stat(:key) -> Numeric + * + * Returns a Hash containing information about the GC. + * + * The hash includes information about internal statistics about GC such as: + * + * { + * :count=>2, + * :heap_used=>9, + * :heap_length=>11, + * :heap_increment=>2, + * :heap_live_slot=>6836, + * :heap_free_slot=>519, + * :heap_final_slot=>0, + * :heap_swept_slot=>818, + * :total_allocated_object=>7674, + * :total_freed_object=>838, + * :malloc_increase=>181034, + * :malloc_limit=>16777216, + * :minor_gc_count=>2, + * :major_gc_count=>0, + * :remembered_shady_object=>55, + * :remembered_shady_object_limit=>0, + * :old_object=>2422, + * :old_object_limit=>0, + * :oldmalloc_increase=>277386, + * :oldmalloc_limit=>16777216 + * } + * + * The contents of the hash are implementation specific and may be changed in + * the future. + * + * This method is only expected to work on C Ruby. + * + */ + +static VALUE +gc_stat(int argc, VALUE *argv, VALUE self) +{ + VALUE arg = Qnil; + + if (rb_scan_args(argc, argv, "01", &arg) == 1) { + if (SYMBOL_P(arg)) { + size_t value = 0; + gc_stat_internal(arg, &value); + return SIZET2NUM(value); + } else if (!RB_TYPE_P(arg, T_HASH)) { + rb_raise(rb_eTypeError, "non-hash given"); + } + } + + if (arg == Qnil) { + arg = rb_hash_new(); + } + gc_stat_internal(arg, 0); + return arg; +} + +size_t +rb_gc_stat(VALUE key) +{ + if (SYMBOL_P(key)) { + size_t value = 0; + gc_stat_internal(key, &value); + return value; + } else { + gc_stat_internal(key, 0); + return 0; + } +} + /* * call-seq: * GC.stress -> fixnum, true or false diff --git a/include/ruby/intern.h b/include/ruby/intern.h index c1114ae875..cc13557c4e 100644 --- a/include/ruby/intern.h +++ b/include/ruby/intern.h @@ -486,6 +486,7 @@ DEPRECATED(void rb_gc_set_params(void)); VALUE rb_define_finalizer(VALUE, VALUE); VALUE rb_undefine_finalizer(VALUE); size_t rb_gc_count(void); +size_t rb_gc_stat(VALUE); /* hash.c */ void st_foreach_safe(struct st_table *, int (*)(ANYARGS), st_data_t); VALUE rb_check_hash_type(VALUE); diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb index 7410516c7a..d43f8b8693 100644 --- a/test/ruby/test_gc.rb +++ b/test/ruby/test_gc.rb @@ -79,6 +79,12 @@ class TestGc < Test::Unit::TestCase assert_equal(count[:FREE], stat[:heap_free_slot]) end + def test_stat_single + stat = GC.stat + assert_equal stat[:count], GC.stat(:count) + assert_raise(ArgumentError){ GC.stat(:invalid) } + end + def test_gc_reason GC.start GC.stat[:heap_free_slot].times{ "a" + "b" }