ruby-changes:31814
From: tmm1 <ko1@a...>
Date: Thu, 28 Nov 2013 16:40:55 +0900 (JST)
Subject: [ruby-changes:31814] tmm1:r43893 (trunk): * gc.c: Expose details about last garbage collection via GC.stat.
tmm1 2013-11-28 16:40:35 +0900 (Thu, 28 Nov 2013) New Revision: 43893 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=43893 Log: * gc.c: Expose details about last garbage collection via GC.stat. * gc.c (gc_stat): Add :last_collection_flags for reason/trigger/type of last GC run. * gc.c (gc_prof_sweep_timer_stop): Record HAVE_FINALIZE GPR even without GC_PROFILE_MORE_DETAIL. * gc.c (gc_profile_flags): Add GC::Profiler.decode_flags to make sense of GC.stat[:last_collection_flags] * test/ruby/test_gc.rb (class TestGc): Test for above. Modified files: trunk/ChangeLog trunk/gc.c trunk/test/ruby/test_gc.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 43892) +++ ChangeLog (revision 43893) @@ -1,3 +1,14 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Thu Nov 28 16:34:43 2013 Aman Gupta <ruby@t...> + + * gc.c: Expose details about last garbage collection via GC.stat. + * gc.c (gc_stat): Add :last_collection_flags for reason/trigger/type of + last GC run. + * gc.c (gc_prof_sweep_timer_stop): Record HAVE_FINALIZE GPR even + without GC_PROFILE_MORE_DETAIL. + * gc.c (gc_profile_flags): Add GC::Profiler.decode_flags to make sense + of GC.stat[:last_collection_flags] + * test/ruby/test_gc.rb (class TestGc): Test for above. + Thu Nov 28 16:15:47 2013 Nobuyoshi Nakada <nobu@r...> * win32/win32.c (rb_w32_dup2): extract from rb_cloexec_dup2() and Index: gc.c =================================================================== --- gc.c (revision 43892) +++ gc.c (revision 43893) @@ -483,6 +483,7 @@ typedef struct rb_objspace { https://github.com/ruby/ruby/blob/trunk/gc.c#L483 size_t count; size_t total_allocated_object_num; size_t total_freed_object_num; + int last_collection_flags; } profile; struct gc_list *global_list; rb_event_flag_t hook_events; /* this place may be affinity with memory cache */ @@ -4837,6 +4838,7 @@ garbage_collect_body(rb_objspace_t *objs https://github.com/ruby/ruby/blob/trunk/gc.c#L4838 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; 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; @@ -5072,6 +5074,7 @@ gc_stat(int argc, VALUE *argv, VALUE sel https://github.com/ruby/ruby/blob/trunk/gc.c#L5074 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; @@ -5102,6 +5105,7 @@ gc_stat(int argc, VALUE *argv, VALUE sel https://github.com/ruby/ruby/blob/trunk/gc.c#L5105 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,6 +5157,7 @@ gc_stat(int argc, VALUE *argv, VALUE sel https://github.com/ruby/ruby/blob/trunk/gc.c#L5157 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); @@ -6409,6 +6414,7 @@ gc_prof_sweep_timer_stop(rb_objspace_t * https://github.com/ruby/ruby/blob/trunk/gc.c#L6414 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; } } @@ -6478,18 +6484,64 @@ gc_profile_clear(void) https://github.com/ruby/ruby/blob/trunk/gc.c#L6484 static VALUE gc_profile_flags(int flags) { - VALUE result = rb_ary_new(); - rb_ary_push(result, ID2SYM(rb_intern(flags & GPR_FLAG_MAJOR_MASK ? "major_gc" : "minor_gc"))); - if (flags & GPR_FLAG_HAVE_FINALIZE) rb_ary_push(result, ID2SYM(rb_intern("HAVE_FINALIZE"))); - if (flags & GPR_FLAG_NEWOBJ) rb_ary_push(result, ID2SYM(rb_intern("CAUSED_BY_NEWOBJ"))); - if (flags & GPR_FLAG_MALLOC) rb_ary_push(result, ID2SYM(rb_intern("CAUSED_BY_MALLOC"))); - if (flags & GPR_FLAG_METHOD) rb_ary_push(result, ID2SYM(rb_intern("CAUSED_BY_METHOD"))); - if (flags & GPR_FLAG_STRESS) rb_ary_push(result, ID2SYM(rb_intern("CAUSED_BY_STRESS"))); + 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, ...] * * Returns an Array of individual raw profile data Hashes ordered @@ -7013,6 +7065,7 @@ Init_GC(void) https://github.com/ruby/ruby/blob/trunk/gc.c#L7065 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: test/ruby/test_gc.rb =================================================================== --- test/ruby/test_gc.rb (revision 43892) +++ test/ruby/test_gc.rb (revision 43893) @@ -79,6 +79,26 @@ class TestGc < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_gc.rb#L79 assert_equal(count[:FREE], stat[:heap_free_slot]) end + def test_gc_reason + 100_000.times{ "a" + "b" } + assert_equal({:gc_by => :newobj}, + GC::Profiler.decode_flags(GC.stat[:last_collection_flags])) + end + + 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 + + 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])) + ensure + GC.stress = false + 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/