ruby-changes:31929
From: tmm1 <ko1@a...>
Date: Thu, 5 Dec 2013 19:30:46 +0900 (JST)
Subject: [ruby-changes:31929] tmm1:r44008 (trunk): gc.c: add GC.latest_gc_info()
tmm1 2013-12-05 19:30:38 +0900 (Thu, 05 Dec 2013) New Revision: 44008 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=44008 Log: gc.c: add GC.latest_gc_info() * gc.c (struct rb_objspace): rename internal last_collection_flags to latest_gc_info * gc.c (gc_latest_collection_info): add GC.latest_gc_info() with similar behavior to GC.stat() * gc.c (rb_gc_latest_gc_info): new c-api for above * gc.c (gc_stat_internal): remove :last_collection_flags from GC.stat * gc.c (gc_profile_decode_flags): remove GC::Profiler.decode_flags * include/ruby/intern.h (rb_gc_latest_gc_info): export new c-api * test/ruby/test_gc.rb (class TestGc): test for new behavior * NEWS: note about new api * gc.c (gc_stat_internal): raise TypeError on wrong type * gc.c (gc_stat): fix error message Modified files: trunk/ChangeLog trunk/NEWS trunk/gc.c trunk/include/ruby/intern.h trunk/test/ruby/test_gc.rb Index: include/ruby/intern.h =================================================================== --- include/ruby/intern.h (revision 44007) +++ include/ruby/intern.h (revision 44008) @@ -487,6 +487,7 @@ VALUE rb_define_finalizer(VALUE, VALUE); https://github.com/ruby/ruby/blob/trunk/include/ruby/intern.h#L487 VALUE rb_undefine_finalizer(VALUE); size_t rb_gc_count(void); size_t rb_gc_stat(VALUE); +VALUE rb_gc_latest_gc_info(VALUE); /* hash.c */ void st_foreach_safe(struct st_table *, int (*)(ANYARGS), st_data_t); VALUE rb_check_hash_type(VALUE); Index: ChangeLog =================================================================== --- ChangeLog (revision 44007) +++ ChangeLog (revision 44008) @@ -1,3 +1,19 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Thu Dec 5 19:21:10 2013 Aman Gupta <ruby@t...> + + * gc.c (struct rb_objspace): rename internal last_collection_flags to + latest_gc_info + * gc.c (gc_latest_collection_info): add GC.latest_gc_info() with similar + behavior to GC.stat() + * gc.c (rb_gc_latest_gc_info): new c-api for above + * gc.c (gc_stat_internal): remove :last_collection_flags from GC.stat + * gc.c (gc_profile_decode_flags): remove GC::Profiler.decode_flags + * include/ruby/intern.h (rb_gc_latest_gc_info): export new c-api + * test/ruby/test_gc.rb (class TestGc): test for new behavior + * NEWS: note about new api + + * gc.c (gc_stat_internal): raise TypeError on wrong type + * gc.c (gc_stat): fix error message + Thu Dec 5 18:18:08 2013 Aman Gupta <ruby@t...> * ext/objspace/gc_hook.c: remove this file Index: gc.c =================================================================== --- gc.c (revision 44007) +++ gc.c (revision 44008) @@ -496,7 +496,7 @@ typedef struct rb_objspace { https://github.com/ruby/ruby/blob/trunk/gc.c#L496 size_t count; size_t total_allocated_object_num; size_t total_freed_object_num; - int last_collection_flags; + int latest_gc_info; } profile; struct gc_list *global_list; rb_event_flag_t hook_events; /* this place may be affinity with memory cache */ @@ -4869,7 +4869,7 @@ garbage_collect_body(rb_objspace_t *objs https://github.com/ruby/ruby/blob/trunk/gc.c#L4869 if (GC_NOTIFY) fprintf(stderr, "start garbage_collect(%d, %d, %d)\n", full_mark, immediate_sweep, reason); objspace->profile.count++; - objspace->profile.last_collection_flags = reason; + objspace->profile.latest_gc_info = reason; gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_START, 0 /* TODO: pass minor/immediate flag? */); objspace->profile.total_allocated_object_num_at_gc_start = objspace->profile.total_allocated_object_num; @@ -5057,6 +5057,111 @@ gc_count(VALUE self) https://github.com/ruby/ruby/blob/trunk/gc.c#L5057 } static VALUE +gc_info_decode(int flags, VALUE hash_or_key) +{ + static VALUE sym_major_by = Qnil, sym_gc_by, sym_immediate_sweep, sym_have_finalizer; + static VALUE sym_nofree, sym_oldgen, sym_shady, sym_rescan, sym_stress, sym_oldmalloc; + static VALUE sym_newobj, sym_malloc, sym_method, sym_capi; + VALUE hash = Qnil, key = Qnil; + + if (SYMBOL_P(hash_or_key)) + key = hash_or_key; + else if (RB_TYPE_P(hash_or_key, T_HASH)) + hash = hash_or_key; + else + rb_raise(rb_eTypeError, "non-hash or symbol given"); + + if (sym_major_by == Qnil) { +#define S(s) sym_##s = ID2SYM(rb_intern_const(#s)) + S(major_by); + S(gc_by); + S(immediate_sweep); + S(have_finalizer); + S(nofree); + S(oldgen); + S(shady); + S(rescan); + S(stress); + S(oldmalloc); + S(newobj); + S(malloc); + S(method); + S(capi); +#undef S + } + +#define SET(name, attr) \ + if (key == sym_##name) \ + return (attr); \ + else if (hash != Qnil) \ + rb_hash_aset(hash, sym_##name, (attr)); + + SET(major_by, + (flags & GPR_FLAG_MAJOR_BY_NOFREE) ? sym_nofree : + (flags & GPR_FLAG_MAJOR_BY_OLDGEN) ? sym_oldgen : + (flags & GPR_FLAG_MAJOR_BY_SHADY) ? sym_shady : + (flags & GPR_FLAG_MAJOR_BY_RESCAN) ? sym_rescan : + (flags & GPR_FLAG_MAJOR_BY_STRESS) ? sym_stress : +#if RGENGC_ESTIMATE_OLDMALLOC + (flags & GPR_FLAG_MAJOR_BY_OLDMALLOC) ? sym_oldmalloc : +#endif + Qnil + ); + + SET(gc_by, + (flags & GPR_FLAG_NEWOBJ) ? sym_newobj : + (flags & GPR_FLAG_MALLOC) ? sym_malloc : + (flags & GPR_FLAG_METHOD) ? sym_method : + (flags & GPR_FLAG_CAPI) ? sym_capi : + (flags & GPR_FLAG_STRESS) ? sym_stress : + Qnil + ); + + SET(have_finalizer, (flags & GPR_FLAG_HAVE_FINALIZE) ? Qtrue : Qfalse); + SET(immediate_sweep, (flags & GPR_FLAG_IMMEDIATE_SWEEP) ? Qtrue : Qfalse); +#undef SET + + if (key != Qnil) /* matched key should return above */ + rb_raise(rb_eArgError, "unknown key: %s", RSTRING_PTR(rb_id2str(SYM2ID(key)))); + + return hash; +} + +VALUE +rb_gc_latest_gc_info(VALUE key) +{ + rb_objspace_t *objspace = &rb_objspace; + return gc_info_decode(objspace->profile.latest_gc_info, key); +} + +/* + * call-seq: + * GC.latest_gc_info -> {:gc_by=>:newobj} + * GC.latest_gc_info(hash) -> hash + * GC.latest_gc_info(:major_by) -> :malloc + * + * Returns information about the most recent garbage collection. + */ + +static VALUE +gc_latest_gc_info(int argc, VALUE *argv, VALUE self) +{ + rb_objspace_t *objspace = &rb_objspace; + VALUE arg = Qnil; + + if (rb_scan_args(argc, argv, "01", &arg) == 1) { + if (!SYMBOL_P(arg) && !RB_TYPE_P(arg, T_HASH)) { + rb_raise(rb_eTypeError, "non-hash or symbol given"); + } + } + + if (arg == Qnil) + arg = rb_hash_new(); + + return gc_info_decode(objspace->profile.latest_gc_info, arg); +} + +static VALUE gc_stat_internal(VALUE hash_or_sym, size_t *out) { static VALUE sym_count; @@ -5065,7 +5170,6 @@ gc_stat_internal(VALUE hash_or_sym, size https://github.com/ruby/ruby/blob/trunk/gc.c#L5170 static VALUE sym_heap_eden_page_length, sym_heap_tomb_page_length; static VALUE sym_total_allocated_object, sym_total_freed_object; static VALUE sym_malloc_increase, sym_malloc_limit; - static VALUE sym_last_collection_flags; #if USE_RGENGC static VALUE sym_minor_gc_count, sym_major_gc_count; static VALUE sym_remembered_shady_object, sym_remembered_shady_object_limit; @@ -5088,7 +5192,7 @@ gc_stat_internal(VALUE hash_or_sym, size https://github.com/ruby/ruby/blob/trunk/gc.c#L5192 else if (SYMBOL_P(hash_or_sym) && out) key = hash_or_sym; else - rb_raise(rb_eArgError, "non-hash or symbol argument"); + rb_raise(rb_eTypeError, "non-hash or symbol argument"); if (sym_count == 0) { #define S(s) sym_##s = ID2SYM(rb_intern_const(#s)) @@ -5106,7 +5210,6 @@ gc_stat_internal(VALUE hash_or_sym, size https://github.com/ruby/ruby/blob/trunk/gc.c#L5210 S(total_freed_object); S(malloc_increase); S(malloc_limit); - S(last_collection_flags); #if USE_RGENGC S(minor_gc_count); S(major_gc_count); @@ -5153,7 +5256,6 @@ gc_stat_internal(VALUE hash_or_sym, size https://github.com/ruby/ruby/blob/trunk/gc.c#L5256 SET(total_freed_object, objspace->profile.total_freed_object_num); SET(malloc_increase, malloc_increase); SET(malloc_limit, malloc_limit); - SET(last_collection_flags, objspace->profile.last_collection_flags); #if USE_RGENGC SET(minor_gc_count, objspace->profile.minor_gc_count); SET(major_gc_count, objspace->profile.major_gc_count); @@ -5251,7 +5353,7 @@ gc_stat(int argc, VALUE *argv, VALUE sel https://github.com/ruby/ruby/blob/trunk/gc.c#L5353 gc_stat_internal(arg, &value); return SIZET2NUM(value); } else if (!RB_TYPE_P(arg, T_HASH)) { - rb_raise(rb_eTypeError, "non-hash given"); + rb_raise(rb_eTypeError, "non-hash or symbol given"); } } @@ -6537,7 +6639,7 @@ gc_prof_sweep_timer_stop(rb_objspace_t * https://github.com/ruby/ruby/blob/trunk/gc.c#L6639 record->gc_sweep_time += sweep_time; if (heap_pages_deferred_final) record->flags |= GPR_FLAG_HAVE_FINALIZE; #endif - if (heap_pages_deferred_final) objspace->profile.last_collection_flags |= GPR_FLAG_HAVE_FINALIZE; + if (heap_pages_deferred_final) objspace->profile.latest_gc_info |= GPR_FLAG_HAVE_FINALIZE; } } @@ -6604,65 +6706,6 @@ gc_profile_clear(void) https://github.com/ruby/ruby/blob/trunk/gc.c#L6706 return Qnil; } -static VALUE -gc_profile_flags(int flags) -{ - VALUE result = rb_hash_new(); - static VALUE sym_major_by = Qnil, sym_gc_by, sym_immediate_sweep, sym_have_finalizer; - static VALUE sym_nofree, sym_oldgen, sym_shady, sym_rescan, sym_stress, sym_oldmalloc; - static VALUE sym_newobj, sym_malloc, sym_method, sym_capi; - - if (sym_major_by == Qnil) { - sym_major_by = ID2SYM(rb_intern("major_by")); - sym_gc_by = ID2SYM(rb_intern("gc_by")); - sym_immediate_sweep = ID2SYM(rb_intern("immediate_sweep")); - sym_have_finalizer = ID2SYM(rb_intern("have_finalizer")); - sym_nofree = ID2SYM(rb_intern("nofree")); - sym_oldgen = ID2SYM(rb_intern("oldgen")); - sym_shady = ID2SYM(rb_intern("shady")); - sym_rescan = ID2SYM(rb_intern("rescan")); - sym_stress = ID2SYM(rb_intern("stress")); - sym_oldmalloc = ID2SYM(rb_intern("oldmalloc")); - sym_newobj = ID2SYM(rb_intern("newobj")); - sym_malloc = ID2SYM(rb_intern("malloc")); - sym_method = ID2SYM(rb_intern("method")); - sym_capi = ID2SYM(rb_intern("capi")); - } - - if (flags & GPR_FLAG_MAJOR_BY_NOFREE) rb_hash_aset(result, sym_major_by, sym_nofree); - if (flags & GPR_FLAG_MAJOR_BY_OLDGEN) rb_hash_aset(result, sym_major_by, sym_oldgen); - if (flags & GPR_FLAG_MAJOR_BY_SHADY) rb_hash_aset(result, sym_major_by, sym_shady); - if (flags & GPR_FLAG_MAJOR_BY_RESCAN) rb_hash_aset(result, sym_major_by, sym_rescan); - if (flags & GPR_FLAG_MAJOR_BY_STRESS) rb_hash_aset(result, sym_major_by, sym_stress); -#if RGENGC_ESTIMATE_OLDMALLOC - if (flags & GPR_FLAG_MAJOR_BY_OLDMALLOC) rb_hash_aset(result, sym_major_by, sym_oldmalloc); -#endif - - if (flags & GPR_FLAG_NEWOBJ) rb_hash_aset(result, sym_gc_by, sym_newobj); - if (flags & GPR_FLAG_MALLOC) rb_hash_aset(result, sym_gc_by, sym_malloc); - if (flags & GPR_FLAG_METHOD) rb_hash_aset(result, sym_gc_by, sym_method); - if (flags & GPR_FLAG_CAPI) rb_hash_aset(result, sym_gc_by, sym_capi); - if (flags & GPR_FLAG_STRESS) rb_hash_aset(result, sym_gc_by, sym_stress); - - if (flags & GPR_FLAG_HAVE_FINALIZE) rb_hash_aset(result, sym_have_finalizer, Qtrue); - if (flags & GPR_FLAG_IMMEDIATE_SWEEP) rb_hash_aset(result, sym_immediate_sweep, Qtrue); - - return result; -} - -/* - * call-seq: - * GC::Profiler.decode_flags(flags) -> {:gc_by => :newobj} - * GC::Profiler.decode_flags(flags) -> {:major_by=>:nofree, :gc_by=>:method} - * - * Converts GC.stat[:last_collection_flags] to a Hash - */ -static VALUE -gc_profile_decode_flags(VALUE self, VALUE flags) -{ - return gc_profile_flags(FIX2INT(flags)); -} - /* * call-seq: * GC::Profiler.raw_data -> [Hash, ...] @@ -6729,7 +6772,7 @@ gc_profile_record_get(void) https://github.com/ruby/ruby/blob/trunk/gc.c#L6772 gc_profile_record *record = &objspace->profile.records[i]; prof = rb_hash_new(); - rb_hash_aset(prof, ID2SYM(rb_intern("GC_FLAGS")), gc_profile_flags(record->flags)); + rb_hash_aset(prof, ID2SYM(rb_intern("GC_FLAGS")), gc_info_decode(record->flags, rb_hash_new())); rb_hash_aset(prof, ID2SYM(rb_intern("GC_TIME")), DBL2NUM(record->gc_time)); rb_hash_aset(prof, ID2SYM(rb_intern("GC_INVOKE_TIME")), DBL2NUM(record->gc_invoke_time)); rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_SIZE")), SIZET2NUM(record->heap_use_size)); @@ -7169,6 +7212,7 @@ Init_GC(void) https://github.com/ruby/ruby/blob/trunk/gc.c#L7212 rb_define_singleton_method(rb_mGC, "stress=", gc_stress_set, 1); rb_define_singleton_method(rb_mGC, "count", gc_count, 0); rb_define_singleton_method(rb_mGC, "stat", gc_stat, -1); + rb_define_singleton_method(rb_mGC, "latest_gc_info", gc_latest_gc_info, -1); rb_define_method(rb_mGC, "garbage_collect", rb_gc_start, 0); gc_constants = rb_hash_new(); @@ -7188,7 +7232,6 @@ Init_GC(void) https://github.com/ruby/ruby/blob/trunk/gc.c#L7232 rb_define_singleton_method(rb_mProfiler, "result", gc_profile_result, 0); rb_define_singleton_method(rb_mProfiler, "report", gc_profile_report, -1); rb_define_singleton_method(rb_mProfiler, "total_time", gc_profile_total_time, 0); - rb_define_singleton_method(rb_mProfiler, "decode_flags", gc_profile_decode_flags, 1); rb_mObjSpace = rb_define_module("ObjectSpace"); rb_define_module_function(rb_mObjSpace, "each_object", os_each_obj, -1); Index: NEWS =================================================================== --- NEWS (revision 44007) +++ NEWS (revision 44008) @@ -322,6 +322,8 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L322 * rb_gc_stat() added. This allows access to specific GC.stat() values from C without any allocation overhead. +* rb_gc_latest_gc_info() added. This allows access to GC.latest_gc_info(). + * 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. Index: test/ruby/test_gc.rb =================================================================== --- test/ruby/test_gc.rb (revision 44007) +++ test/ruby/test_gc.rb (revision 44008) @@ -85,27 +85,31 @@ class TestGc < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_gc.rb#L85 assert_raise(ArgumentError){ GC.stat(:invalid) } end - def test_gc_reason + def test_latest_gc_info GC.start GC.stat[:heap_free_slot].times{ "a" + "b" } - assert_equal({:gc_by => :newobj}, - GC::Profiler.decode_flags(GC.stat[:last_collection_flags])) - end + assert_equal :newobj, GC.latest_gc_info[:gc_by] - def test_gc_reason_method GC.start - assert_equal({:major_by=>:nofree, :gc_by=>:method, :immediate_sweep=>true}, - GC::Profiler.decode_flags(GC.stat[:last_collection_flags])) - end + assert_equal :nofree, GC.latest_gc_info[:major_by] + assert_equal :method, GC.latest_gc_info[:gc_by] + assert_equal true, GC.latest_gc_info[:immediate_sweep] - def test_gc_reason_stress GC.stress = true - assert_equal({:major_by=>:stress, :gc_by=>:malloc, :immediate_sweep=>true}, - GC::Profiler.decode_flags(GC.stat[:last_collection_flags])) + assert_equal :stress, GC.latest_gc_info[:major_by] ensure GC.stress = false end + def test_latest_gc_info_argument + info = {} + GC.latest_gc_info(info) + + assert_not_empty info + assert_equal info[:gc_by], GC.latest_gc_info(:gc_by) + assert_raises(ArgumentError){ GC.latest_gc_info(:invalid) } + end + def test_singleton_method assert_in_out_err(%w[--disable-gems], <<-EOS, [], [], "[ruby-dev:42832]") GC.stress = true -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/