Sanitize sgen's collection trigger internal API.
authorRodrigo Kumpera <kumpera@gmail.com>
Tue, 5 Jun 2012 22:59:03 +0000 (19:59 -0300)
committerRodrigo Kumpera <kumpera@gmail.com>
Mon, 9 Jul 2012 18:18:54 +0000 (15:18 -0300)
* sgen-gc.c: Sanitize all collection triggering functions in
just two. Make it possible to trigger a major GC before a minor
overflow happens.

mono/metadata/sgen-alloc.c
mono/metadata/sgen-gc.c
mono/metadata/sgen-gc.h
mono/metadata/sgen-los.c
mono/metadata/sgen-marksweep.c
mono/metadata/sgen-memory-governor.c
mono/metadata/sgen-memory-governor.h

index 4c443821ee4d3e1cb5506b367bf98470fe3921f7..7896c31292aed6725c0da30f937933d8a7988596 100644 (file)
@@ -129,9 +129,7 @@ alloc_degraded (MonoVTable *vtable, size_t size, gboolean for_mature)
                InterlockedExchangeAdd (&degraded_mode, size);
        }
 
-       if (sgen_need_major_collection (0)) {
-               sgen_collect_major_no_lock ("degraded overflow");
-       }
+       sgen_ensure_free_space (size);
 
        return major_collector.alloc_degraded (vtable, size);
 }
@@ -168,7 +166,7 @@ mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
 
                if (collect_before_allocs) {
                        if (((current_alloc % collect_before_allocs) == 0) && nursery_section) {
-                               sgen_collect_nursery_no_lock (0);
+                               sgen_perform_collection (0, GENERATION_NURSERY, "collect-before-alloc-triggered");
                                if (!degraded_mode && sgen_can_alloc_size (size) && size <= SGEN_MAX_SMALL_OBJ_SIZE) {
                                        // FIXME:
                                        g_assert_not_reached ();
@@ -253,7 +251,7 @@ mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
                                do {
                                        p = sgen_nursery_alloc (size);
                                        if (!p) {
-                                               sgen_minor_collect_or_expand_inner (size);
+                                               sgen_ensure_free_space (size);
                                                if (degraded_mode) {
                                                        p = alloc_degraded (vtable, size, FALSE);
                                                        binary_protocol_alloc_degraded (p, vtable, size);
@@ -280,7 +278,7 @@ mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
                                do {
                                        p = sgen_nursery_alloc_range (tlab_size, size, &alloc_size);
                                        if (!p) {
-                                               sgen_minor_collect_or_expand_inner (tlab_size);
+                                               sgen_ensure_free_space (tlab_size);
                                                if (degraded_mode) {
                                                        p = alloc_degraded (vtable, size, FALSE);
                                                        binary_protocol_alloc_degraded (p, vtable, size);
index 2a9c4cb96bda15989d0e45fb6fe44053efff2ab2..2e1a4b2726f48aac1176e8a621b4d2a4e66a3d29 100644 (file)
@@ -571,7 +571,7 @@ typedef SgenGrayQueue GrayQueue;
 
 /* forward declarations */
 static int stop_world (int generation);
-static int restart_world (int generation);
+static int restart_world (int generation, GGTimingInfo *timing);
 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue);
 static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue);
 static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeReadyEntry *list, GrayQueue *queue);
@@ -590,7 +590,6 @@ static void process_dislink_stage_entries (void);
 static void pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue);
 static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue);
 static void finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue);
-static void major_collection (const char *reason);
 
 static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track, gboolean in_gc);
 static gboolean mono_gc_is_critical_method (MonoMethod *method);
@@ -2568,29 +2567,13 @@ collect_nursery (size_t requested_size)
        sgen_memgov_minor_collection_end ();
 
        /*objects are late pinned because of lack of memory, so a major is a good call*/
-       needs_major = sgen_need_major_collection (0) || objects_pinned;
+       needs_major = objects_pinned > 0;
        current_collection_generation = -1;
        objects_pinned = 0;
 
        return needs_major;
 }
 
-void
-sgen_collect_nursery_no_lock (size_t requested_size)
-{
-       gint64 gc_start_time;
-
-       mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
-       gc_start_time = mono_100ns_ticks ();
-
-       stop_world (0);
-       collect_nursery (requested_size);
-       restart_world (0);
-
-       mono_trace_message (MONO_TRACE_GC, "minor gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
-       mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
-}
-
 static gboolean
 major_do_collection (const char *reason)
 {
@@ -2609,6 +2592,7 @@ major_do_collection (const char *reason)
        ScanThreadDataJobData stdjd;
        ScanFinalizerEntriesJobData sfejd_fin_ready, sfejd_critical_fin;
 
+       current_collection_generation = GENERATION_OLD;
        mono_perfcounters->gc_collections1++;
 
        current_object_ops = major_collector.major_ops;
@@ -2884,6 +2868,7 @@ major_do_collection (const char *reason)
        g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
 
        sgen_memgov_major_collection_end ();
+       current_collection_generation = -1;
 
        major_collector.finish_major_collection ();
 
@@ -2896,92 +2881,112 @@ major_do_collection (const char *reason)
        return bytes_pinned_from_failed_allocation > 0;
 }
 
-static void
-major_collection (const char *reason)
+static gboolean major_do_collection (const char *reason);
+
+/*
+ * Ensure an allocation request for @size will succeed by freeing enough memory.
+ *
+ * LOCKING: The GC lock MUST be held.
+ */
+void
+sgen_ensure_free_space (size_t size)
 {
-       gboolean need_minor_collection;
+       int generation_to_collect = -1;
+       const char *reason = NULL;
 
-       if (disable_major_collections) {
-               collect_nursery (0);
-               return;
-       }
 
-       current_collection_generation = GENERATION_OLD;
-       need_minor_collection = major_do_collection (reason);
-       current_collection_generation = -1;
+       if (size > SGEN_MAX_SMALL_OBJ_SIZE) {
+               if (sgen_need_major_collection (size)) {
+                       reason = "LOS overflow";
+                       generation_to_collect = GENERATION_OLD;
+               }
+       } else {
+               if (degraded_mode) {
+                       if (sgen_need_major_collection (size)) {
+                               reason = "Degraded mode overflow";
+                               generation_to_collect = GENERATION_OLD;
+                       }
+               } else if (sgen_need_major_collection (size)) {
+                       reason = "Minor allowance";
+                       generation_to_collect = GENERATION_OLD;
+               } else {
+                       generation_to_collect = GENERATION_NURSERY;
+                       reason = "Nursery full";                        
+               }
+       }
 
-       if (need_minor_collection)
-               collect_nursery (0);
+       if (generation_to_collect == -1)
+               return;
+       sgen_perform_collection (size, generation_to_collect, reason);
 }
 
 void
-sgen_collect_major_no_lock (const char *reason)
-{
-       gint64 gc_start_time;
+sgen_perform_collection (size_t requested_size, int generation_to_collect, const char *reason)
+{
+       TV_DECLARE (gc_end);
+       GGTimingInfo infos [2];
+       int overflow_generation_to_collect = -1;
+       const char *overflow_reason = NULL;
+
+       memset (infos, 0, sizeof (infos));
+       mono_profiler_gc_event (MONO_GC_EVENT_START, generation_to_collect);
+
+       infos [0].generation = generation_to_collect;
+       infos [0].reason = reason;
+       infos [0].is_overflow = FALSE;
+       TV_GETTIME (infos [0].total_time);
+       infos [1].generation = -1;
+
+       stop_world (generation_to_collect);
+       //FIXME extract overflow reason
+       if (generation_to_collect == GENERATION_NURSERY) {
+               if (collect_nursery (requested_size)) {
+                       overflow_generation_to_collect = GENERATION_OLD;
+                       overflow_reason = "Minor overflow";
+               }
+       } else {
+               if (major_do_collection (reason)) {
+                       overflow_generation_to_collect = GENERATION_NURSERY;
+                       overflow_reason = "Excessive pinning";
+               }
+       }
 
-       mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
-       gc_start_time = mono_100ns_ticks ();
-       stop_world (1);
-       major_collection (reason);
-       restart_world (1);
-       mono_trace_message (MONO_TRACE_GC, "major gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
-       mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
-}
+       TV_GETTIME (gc_end);
+       infos [0].total_time = SGEN_TV_ELAPSED (infos [0].total_time, gc_end);
 
-/*
- * When deciding if it's better to collect or to expand, keep track
- * of how much garbage was reclaimed with the last collection: if it's too
- * little, expand.
- * This is called when we could not allocate a small object.
- */
-static void __attribute__((noinline))
-minor_collect_or_expand_inner (size_t size)
-{
-       int do_minor_collection = 1;
 
-       g_assert (nursery_section);
-       if (do_minor_collection) {
-               gint64 total_gc_time, major_gc_time = 0;
+       if (overflow_generation_to_collect != -1) {
+               mono_profiler_gc_event (MONO_GC_EVENT_START, overflow_generation_to_collect);
+               infos [1].generation = overflow_generation_to_collect;
+               infos [1].reason = overflow_reason;
+               infos [1].is_overflow = TRUE;
+               infos [1].total_time = gc_end;
 
-               mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
-               total_gc_time = mono_100ns_ticks ();
+               if (overflow_generation_to_collect == GENERATION_NURSERY)
+                       collect_nursery (0);
+               else
+                       major_do_collection (overflow_reason);
 
-               stop_world (0);
-               if (collect_nursery (size)) {
-                       mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
-                       major_gc_time = mono_100ns_ticks ();
+               TV_GETTIME (gc_end);
+               infos [1].total_time = SGEN_TV_ELAPSED (infos [1].total_time, gc_end);
 
-                       major_collection ("minor overflow");
+               /* keep events symmetric */
+               mono_profiler_gc_event (MONO_GC_EVENT_END, overflow_generation_to_collect);
+       }
 
-                       /* keep events symmetric */
-                       major_gc_time = mono_100ns_ticks () - major_gc_time;
-                       mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
-               }
-               DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)mono_gc_get_heap_size (), (unsigned long)los_memory_usage));
-               restart_world (0);
+       DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)mono_gc_get_heap_size (), (unsigned long)los_memory_usage));
 
-               total_gc_time = mono_100ns_ticks () - total_gc_time;
-               if (major_gc_time)
-                       mono_trace_message (MONO_TRACE_GC, "overflow major gc took %d usecs minor gc took %d usecs", total_gc_time / 10, (total_gc_time - major_gc_time) / 10);
-               else
-                       mono_trace_message (MONO_TRACE_GC, "minor gc took %d usecs", total_gc_time / 10);
-               
-               /* this also sets the proper pointers for the next allocation */
-               if (!sgen_can_alloc_size (size)) {
-                       /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
-                       DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, sgen_get_pinned_count ()));
-                       sgen_dump_pin_queue ();
-                       degraded_mode = 1;
-               }
-               mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
+       /* this also sets the proper pointers for the next allocation */
+       if (generation_to_collect == GENERATION_NURSERY && !sgen_can_alloc_size (requested_size)) {
+               /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
+               DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", requested_size, sgen_get_pinned_count ()));
+               sgen_dump_pin_queue ();
+               degraded_mode = 1;
        }
-       //report_internal_mem_usage ();
-}
 
-void
-sgen_minor_collect_or_expand_inner (size_t size)
-{
-       minor_collect_or_expand_inner (size);
+       restart_world (generation_to_collect, infos);
+
+       mono_profiler_gc_event (MONO_GC_EVENT_END, generation_to_collect);
 }
 
 /*
@@ -3558,7 +3563,7 @@ stop_world (int generation)
 
 /* LOCKING: assumes the GC lock is held */
 static int
-restart_world (int generation)
+restart_world (int generation, GGTimingInfo *timing)
 {
        int count;
        SgenThreadInfo *info;
@@ -3597,8 +3602,13 @@ restart_world (int generation)
 
        TV_GETTIME (end_bridge);
        bridge_usec = TV_ELAPSED (end_sw, end_bridge);
+
+       if (timing) {
+               timing [0].stw_time = usec;
+               timing [0].bridge_time = bridge_usec;
+       }
        
-       sgen_memgov_collection_end (generation, usec, bridge_usec);
+       sgen_memgov_collection_end (generation, timing, timing ? 2 : 0);
 
        return count;
 }
@@ -4285,15 +4295,7 @@ mono_gc_collect (int generation)
        LOCK_GC;
        if (generation > 1)
                generation = 1;
-       mono_profiler_gc_event (MONO_GC_EVENT_START, generation);
-       stop_world (generation);
-       if (generation == 0) {
-               collect_nursery (0);
-       } else {
-               major_collection ("user request");
-       }
-       restart_world (generation);
-       mono_profiler_gc_event (MONO_GC_EVENT_END, generation);
+       sgen_perform_collection (0, generation, "user request");
        UNLOCK_GC;
 }
 
@@ -5307,7 +5309,7 @@ sgen_check_whole_heap_stw (void)
        stop_world (0);
        sgen_clear_nursery_fragments ();
        sgen_check_whole_heap ();
-       restart_world (0);
+       restart_world (0, NULL);
 }
 
 #endif /* HAVE_SGEN_GC */
index c139074ba12ffd8c798123a0e63e81c26752d5e3..c438eb800f4e77310d97d5281aa3e6d5f952fe16 100644 (file)
@@ -794,11 +794,12 @@ enum {
 
 void sgen_pin_object (void *object, SgenGrayQueue *queue) MONO_INTERNAL;
 void sgen_parallel_pin_or_update (void **ptr, void *obj, MonoVTable *vt, SgenGrayQueue *queue) MONO_INTERNAL;
-void sgen_collect_major_no_lock (const char *reason) MONO_INTERNAL;
-void sgen_collect_nursery_no_lock (size_t requested_size) MONO_INTERNAL;
-void sgen_minor_collect_or_expand_inner (size_t size) MONO_INTERNAL;
 void sgen_set_pinned_from_failed_allocation (mword objsize) MONO_INTERNAL;
 
+void sgen_ensure_free_space (size_t size) MONO_INTERNAL;
+void sgen_perform_collection (size_t requested_size, int generation_to_collect, const char *reason) MONO_INTERNAL;
+
+
 /* LOS */
 
 typedef struct _LOSObject LOSObject;
@@ -1048,6 +1049,16 @@ sgen_dummy_use (gpointer v) {
 #endif
 }
 
+
+typedef struct {
+       int generation;
+       const char *reason;
+       gboolean is_overflow;
+       SGEN_TV_DECLARE (total_time);
+       SGEN_TV_DECLARE (stw_time);
+       SGEN_TV_DECLARE (bridge_time);
+} GGTimingInfo;
+
 #endif /* HAVE_SGEN_GC */
 
 #endif /* __MONO_SGENGC_H__ */
index 17fba9e4d41fdcd34b06cb35c0e0e2b23f50d139..c00611469abff28ce578593e461096a8947cfdbf 100644 (file)
@@ -359,10 +359,7 @@ sgen_los_alloc_large_inner (MonoVTable *vtable, size_t size)
        los_segment_index += size + sizeof (LOSObject);
        g_assert (los_segment_index <= LOS_SEGMENT_SIZE);
 #else
-       if (sgen_need_major_collection (size)) {
-               DEBUG (4, fprintf (gc_debug_file, "Should trigger major collection: req size %zd (los already: %lu, limit: %lu)\n", size, (unsigned long)los_memory_usage, (unsigned long)next_los_collection));
-               sgen_collect_major_no_lock ("LOS overflow");
-       }
+       sgen_ensure_free_space (size);
 
 #ifdef USE_MALLOC
        obj = malloc (size + sizeof (LOSObject));
index 0e6ff172abab3f0a2008442c226cb704be1fd900..01da3c9418a0551ef13f859779fff64fdfabbf0d 100644 (file)
@@ -802,8 +802,8 @@ major_alloc_small_pinned_obj (size_t size, gboolean has_references)
          *as pinned alloc is requested by the runtime.
          */
         if (!res) {
-                sgen_collect_major_no_lock ("pinned alloc failure");
-                res = alloc_obj (size, TRUE, has_references);
+               sgen_perform_collection (0, GENERATION_OLD, "pinned alloc failure");
+               res = alloc_obj (size, TRUE, has_references);
         }
         return res;
 }
index 0c880c6b4550da912f42217bca07e9ae9c442b5e..2e7c7690a8d6fda728e4364b676afc38df48844d 100644 (file)
@@ -66,7 +66,6 @@ static int minor_collection_sections_alloced = 0;
 
 static int last_major_num_sections = 0;
 static int last_los_memory_usage = 0;
-static gboolean major_collection_happened = FALSE;
 
 static gboolean need_calculate_minor_collection_allowance;
 
@@ -183,7 +182,6 @@ sgen_memgov_major_collection_start (void)
        last_collection_old_los_memory_usage = los_memory_usage;
 
        need_calculate_minor_collection_allowance = TRUE;
-       major_collection_happened = TRUE;
 }
 
 void
@@ -200,29 +198,49 @@ sgen_memgov_collection_start (int generation)
 {
        last_major_num_sections = major_collector.get_num_major_sections ();
        last_los_memory_usage = los_memory_usage;
-       major_collection_happened = FALSE;
 }
 
-void
-sgen_memgov_collection_end (int generation, unsigned long pause_time, unsigned long bridge_pause_time)
+static void
+log_timming (GGTimingInfo *info)
 {
+       //unsigned long stw_time, unsigned long bridge_time, gboolean is_overflow
        int num_major_sections = major_collector.get_num_major_sections ();
-
-       if (major_collection_happened)
-               mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR: %s pause %.2fms, bridge %.2fms major %dK/%dK los %dK/%dK",
-                       generation ? "" : "(minor overflow)",
-                       (int)pause_time / 1000.0f, (int)bridge_pause_time / 1000.0f,
-                       major_collector.section_size * num_major_sections / 1024,
-                       major_collector.section_size * last_major_num_sections / 1024,
-                       los_memory_usage / 1024,
-                       last_los_memory_usage / 1024);
+       char full_timing_buff [1024];
+       full_timing_buff [0] = '\0';
+
+       if (!info->is_overflow)
+               sprintf (full_timing_buff, "total %.2fms, bridge %.2f", info->stw_time / 1000.0f, (int)info->bridge_time / 1000.0f);
+       if (info->generation == GENERATION_OLD)
+               mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR%s: (%s) pause %.2fms, %s major %dK/%dK los %dK/%dK",
+                       info->is_overflow ? "_OVERFLOW" : "",
+                       info->reason,
+                       (int)info->total_time / 1000.0f,
+                       full_timing_buff,
+                       major_collector.section_size * num_major_sections / 1024,
+                       major_collector.section_size * last_major_num_sections / 1024,
+                       los_memory_usage / 1024,
+                       last_los_memory_usage / 1024);
        else
-               mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MINOR: pause %.2fms, bridge %.2fms promoted %dK major %dK los %dK",
-                       (int)pause_time / 1000.0f, (int)bridge_pause_time / 1000.0f,
-                       (num_major_sections - last_major_num_sections) * major_collector.section_size / 1024,
-                       major_collector.section_size * num_major_sections / 1024,
-                       los_memory_usage / 1024);
+               mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MINOR%s: (%s) pause %.2fms, %s promoted %dK major %dK los %dK",
+                               info->is_overflow ? "_OVERFLOW" : "",
+                       info->reason,
+                       (int)info->total_time / 1000.0f,
+                       full_timing_buff,
+                       (num_major_sections - last_major_num_sections) * major_collector.section_size / 1024,
+                       major_collector.section_size * num_major_sections / 1024,
+                       los_memory_usage / 1024);       
 }
+
+void
+sgen_memgov_collection_end (int generation, GGTimingInfo* info, int info_count)
+{
+       int i;
+       for (i = 0; i < info_count; ++i) {
+               if (info->generation != -1)
+                       log_timming (&info [i]);
+       }
+}
+
 void
 sgen_register_major_sections_alloced (int num_sections)
 {
index ea9211856abc1ec2578827d6efa4a0d1f433b2a7..c321f54d22161505ba6b4fddf2cf0e415b25d247 100644 (file)
@@ -37,7 +37,7 @@ void sgen_memgov_major_collection_start (void) MONO_INTERNAL;
 void sgen_memgov_major_collection_end (void) MONO_INTERNAL;
 
 void sgen_memgov_collection_start (int generation) MONO_INTERNAL;
-void sgen_memgov_collection_end (int generation, unsigned long pause_time, unsigned long bridge_pause_time) MONO_INTERNAL;
+void sgen_memgov_collection_end (int generation, GGTimingInfo* info, int info_count) MONO_INTERNAL;
 
 void sgen_register_major_sections_alloced (int num_sections) MONO_INTERNAL;
 mword sgen_get_minor_collection_allowance (void) MONO_INTERNAL;