[sgen] Refactor collection logging
[mono.git] / mono / sgen / sgen-gc.c
index 2274febcb108a57359ff380aec845c0554feb08f..37a162b240c3c44a9957d69a072ea18a6ff20301 100644 (file)
  * Copyright 2011 Xamarin, Inc.
  * Copyright (C) 2012 Xamarin Inc
  *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License 2.0 as published by the Free Software Foundation;
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License 2.0 along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  *
  * Important: allocation provides always zeroed memory, having to do
  * a memset after allocation is deadly for performance.
@@ -343,7 +332,6 @@ nursery_canaries_enabled (void)
  * ######################################################################
  */
 MonoCoopMutex gc_mutex;
-gboolean sgen_try_free_some_memory;
 
 #define SCAN_START_SIZE        SGEN_SCAN_START_SIZE
 
@@ -708,6 +696,8 @@ pin_objects_from_nursery_pin_queue (gboolean do_scan_objects, ScanCopyContext ct
                        definitely_pinned [count] = obj_to_pin;
                        count++;
                }
+               if (concurrent_collection_in_progress)
+                       sgen_pinning_register_pinned_in_nursery (obj_to_pin);
 
        next_pin_queue_entry:
                last = addr;
@@ -1086,6 +1076,12 @@ finish_gray_stack (int generation, ScanCopyContext ctx)
        if (sgen_client_bridge_need_processing ())
                sgen_client_bridge_reset_data ();
 
+       /*
+        * Mark all strong toggleref objects. This must be done before we walk ephemerons or finalizers
+        * to ensure they see the full set of live objects.
+        */
+       sgen_client_mark_togglerefs (start_addr, end_addr, ctx);
+
        /*
         * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
         * before processing finalizable objects and non-tracking weak links to avoid finalizing/clearing
@@ -1098,8 +1094,6 @@ finish_gray_stack (int generation, ScanCopyContext ctx)
                ++ephemeron_rounds;
        } while (!done_with_ephemerons);
 
-       sgen_client_mark_togglerefs (start_addr, end_addr, ctx);
-
        if (sgen_client_bridge_need_processing ()) {
                /*Make sure the gray stack is empty before we process bridge objects so we get liveness right*/
                sgen_drain_gray_stack (ctx);
@@ -1169,7 +1163,7 @@ finish_gray_stack (int generation, ScanCopyContext ctx)
        sgen_client_clear_togglerefs (start_addr, end_addr, ctx);
 
        TV_GETTIME (btv);
-       SGEN_LOG (2, "Finalize queue handling scan for %s generation: %lld usecs %d ephemeron rounds", generation_name (generation), TV_ELAPSED (atv, btv), ephemeron_rounds);
+       SGEN_LOG (2, "Finalize queue handling scan for %s generation: %lld usecs %d ephemeron rounds", generation_name (generation), (long long)TV_ELAPSED (atv, btv), ephemeron_rounds);
 
        /*
         * handle disappearing links
@@ -1419,6 +1413,8 @@ job_mod_union_preclean (void *worker_data_untyped, SgenThreadPoolJob *job)
 
        major_collector.scan_card_table (CARDTABLE_SCAN_MOD_UNION_PRECLEAN, ctx);
        sgen_los_scan_card_table (CARDTABLE_SCAN_MOD_UNION_PRECLEAN, ctx);
+
+       sgen_scan_pin_queue_objects (ctx);
 }
 
 static void
@@ -1478,7 +1474,7 @@ enqueue_scan_from_roots_jobs (char *heap_start, char *heap_end, SgenObjectOperat
  * Return whether any objects were late-pinned due to being out of memory.
  */
 static gboolean
-collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
+collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
 {
        gboolean needs_major;
        size_t max_garbage_amount;
@@ -1561,7 +1557,7 @@ collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
 
        TV_GETTIME (atv);
        time_minor_pinning += TV_ELAPSED (btv, atv);
-       SGEN_LOG (2, "Finding pinned pointers: %zd in %lld usecs", sgen_get_pinned_count (), TV_ELAPSED (btv, atv));
+       SGEN_LOG (2, "Finding pinned pointers: %zd in %lld usecs", sgen_get_pinned_count (), (long long)TV_ELAPSED (btv, atv));
        SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ());
 
        sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan remset", job_remembered_set_scan, sizeof (ScanJob));
@@ -1571,7 +1567,7 @@ collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
        /* we don't have complete write barrier yet, so we scan all the old generation sections */
        TV_GETTIME (btv);
        time_minor_scan_remsets += TV_ELAPSED (atv, btv);
-       SGEN_LOG (2, "Old generation scan: %lld usecs", TV_ELAPSED (atv, btv));
+       SGEN_LOG (2, "Old generation scan: %lld usecs", (long long)TV_ELAPSED (atv, btv));
 
        sgen_pin_stats_print_class_stats ();
 
@@ -1612,7 +1608,7 @@ collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
        sgen_client_binary_protocol_reclaim_end (GENERATION_NURSERY);
        TV_GETTIME (btv);
        time_minor_fragment_creation += TV_ELAPSED (atv, btv);
-       SGEN_LOG (2, "Fragment creation: %lld usecs, %lu bytes available", TV_ELAPSED (atv, btv), (unsigned long)fragment_total);
+       SGEN_LOG (2, "Fragment creation: %lld usecs, %lu bytes available", (long long)TV_ELAPSED (atv, btv), (unsigned long)fragment_total);
 
        if (consistency_check_at_minor_collection)
                sgen_check_major_refs ();
@@ -1642,7 +1638,7 @@ collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
 
        binary_protocol_flush_buffers (FALSE);
 
-       sgen_memgov_minor_collection_end ();
+       sgen_memgov_minor_collection_end (reason, is_overflow);
 
        /*objects are late pinned because of lack of memory, so a major is a good call*/
        needs_major = objects_pinned > 0;
@@ -1713,7 +1709,7 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
 
        sgen_client_pre_collection_checks ();
 
-       if (!concurrent) {
+       if (mode != COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
                /* Remsets are not useful for a major collection */
                remset.clear_cards ();
        }
@@ -1789,7 +1785,7 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
 
        TV_GETTIME (btv);
        time_major_pinning += TV_ELAPSED (atv, btv);
-       SGEN_LOG (2, "Finding pinned pointers: %zd in %lld usecs", sgen_get_pinned_count (), TV_ELAPSED (atv, btv));
+       SGEN_LOG (2, "Finding pinned pointers: %zd in %lld usecs", sgen_get_pinned_count (), (long long)TV_ELAPSED (atv, btv));
        SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ());
 
        major_collector.init_to_space ();
@@ -1873,7 +1869,7 @@ major_finish_copy_or_mark (CopyOrMarkFromRootsMode mode)
 }
 
 static void
-major_start_collection (gboolean concurrent, size_t *old_next_pin_slot)
+major_start_collection (const char *reason, gboolean concurrent, size_t *old_next_pin_slot)
 {
        SgenObjectOperations *object_ops;
 
@@ -1897,7 +1893,7 @@ major_start_collection (gboolean concurrent, size_t *old_next_pin_slot)
 
        reset_pinned_from_failed_allocation ();
 
-       sgen_memgov_major_collection_start ();
+       sgen_memgov_major_collection_start (concurrent, reason);
 
        //count_ref_nonref_objs ();
        //consistency_check ();
@@ -1916,7 +1912,7 @@ major_start_collection (gboolean concurrent, size_t *old_next_pin_slot)
 }
 
 static void
-major_finish_collection (const char *reason, size_t old_next_pin_slot, gboolean forced)
+major_finish_collection (const char *reason, gboolean is_overflow, size_t old_next_pin_slot, gboolean forced)
 {
        ScannedObjectCounts counts;
        SgenObjectOperations *object_ops;
@@ -2029,7 +2025,7 @@ major_finish_collection (const char *reason, size_t old_next_pin_slot, gboolean
 
        g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
 
-       sgen_memgov_major_collection_end (forced);
+       sgen_memgov_major_collection_end (forced, concurrent_collection_in_progress, reason, is_overflow);
        current_collection_generation = -1;
 
        memset (&counts, 0, sizeof (ScannedObjectCounts));
@@ -2051,7 +2047,7 @@ major_finish_collection (const char *reason, size_t old_next_pin_slot, gboolean
 }
 
 static gboolean
-major_do_collection (const char *reason, gboolean forced)
+major_do_collection (const char *reason, gboolean is_overflow, gboolean forced)
 {
        TV_DECLARE (time_start);
        TV_DECLARE (time_end);
@@ -2068,8 +2064,8 @@ major_do_collection (const char *reason, gboolean forced)
        /* world must be stopped already */
        TV_GETTIME (time_start);
 
-       major_start_collection (FALSE, &old_next_pin_slot);
-       major_finish_collection (reason, old_next_pin_slot, forced);
+       major_start_collection (reason, FALSE, &old_next_pin_slot);
+       major_finish_collection (reason, is_overflow, old_next_pin_slot, forced);
 
        TV_GETTIME (time_end);
        gc_stats.major_gc_time += TV_ELAPSED (time_start, time_end);
@@ -2100,7 +2096,7 @@ major_start_concurrent_collection (const char *reason)
        binary_protocol_concurrent_start ();
 
        // FIXME: store reason and pass it when finishing
-       major_start_collection (TRUE, NULL);
+       major_start_collection (reason, TRUE, NULL);
 
        gray_queue_redirect (&gray_queue);
 
@@ -2167,7 +2163,7 @@ major_finish_concurrent_collection (gboolean forced)
 
        current_collection_generation = GENERATION_OLD;
        sgen_cement_reset ();
-       major_finish_collection ("finishing", -1, forced);
+       major_finish_collection ("finishing", FALSE, -1, forced);
 
        if (whole_heap_check_before_collection)
                sgen_check_whole_heap (FALSE);
@@ -2227,100 +2223,58 @@ sgen_ensure_free_space (size_t size, int generation)
 void
 sgen_perform_collection (size_t requested_size, int generation_to_collect, const char *reason, gboolean wait_to_finish)
 {
-       TV_DECLARE (gc_start);
-       TV_DECLARE (gc_end);
        TV_DECLARE (gc_total_start);
        TV_DECLARE (gc_total_end);
-       GGTimingInfo infos [2];
        int overflow_generation_to_collect = -1;
        int oldest_generation_collected = generation_to_collect;
        const char *overflow_reason = NULL;
+       gboolean finish_concurrent = concurrent_collection_in_progress && (major_should_finish_concurrent_collection () || generation_to_collect == GENERATION_OLD);
 
        binary_protocol_collection_requested (generation_to_collect, requested_size, wait_to_finish ? 1 : 0);
 
        SGEN_ASSERT (0, generation_to_collect == GENERATION_NURSERY || generation_to_collect == GENERATION_OLD, "What generation is this?");
 
-       TV_GETTIME (gc_start);
-
        sgen_stop_world (generation_to_collect);
 
        TV_GETTIME (gc_total_start);
 
-       if (concurrent_collection_in_progress) {
-               /*
-                * If the concurrent worker is finished or we are asked to do a major collection
-                * then we finish the concurrent collection.
-                */
-               gboolean finish = major_should_finish_concurrent_collection () || generation_to_collect == GENERATION_OLD;
-
-               if (finish) {
-                       major_finish_concurrent_collection (wait_to_finish);
-                       oldest_generation_collected = GENERATION_OLD;
-               } else {
-                       SGEN_ASSERT (0, generation_to_collect == GENERATION_NURSERY, "Why aren't we finishing the concurrent collection?");
+       // FIXME: extract overflow reason
+       // FIXME: minor overflow for concurrent case
+       if (generation_to_collect == GENERATION_NURSERY && !finish_concurrent) {
+               if (concurrent_collection_in_progress)
                        major_update_concurrent_collection ();
-                       collect_nursery (NULL, FALSE);
-               }
 
-               goto done;
-       }
-
-       SGEN_ASSERT (0, !concurrent_collection_in_progress, "Why did this not get handled above?");
-
-       /*
-        * There's no concurrent collection in progress.  Collect the generation we're asked
-        * to collect.  If the major collector is concurrent and we're not forced to wait,
-        * start a concurrent collection.
-        */
-       // FIXME: extract overflow reason
-       if (generation_to_collect == GENERATION_NURSERY) {
-               if (collect_nursery (NULL, FALSE)) {
+               if (collect_nursery (reason, FALSE, NULL, FALSE) && !concurrent_collection_in_progress) {
                        overflow_generation_to_collect = GENERATION_OLD;
                        overflow_reason = "Minor overflow";
                }
+       } else if (finish_concurrent) {
+               major_finish_concurrent_collection (wait_to_finish);
+               oldest_generation_collected = GENERATION_OLD;
        } else {
+               SGEN_ASSERT (0, generation_to_collect == GENERATION_OLD, "We should have handled nursery collections above");
                if (major_collector.is_concurrent && !wait_to_finish) {
-                       collect_nursery (NULL, FALSE);
+                       collect_nursery ("Concurrent start", FALSE, NULL, FALSE);
                        major_start_concurrent_collection (reason);
-                       // FIXME: set infos[0] properly
-                       goto done;
-               }
-
-               if (major_do_collection (reason, wait_to_finish)) {
+                       oldest_generation_collected = GENERATION_NURSERY;
+               } else if (major_do_collection (reason, FALSE, wait_to_finish)) {
                        overflow_generation_to_collect = GENERATION_NURSERY;
                        overflow_reason = "Excessive pinning";
                }
        }
 
-       TV_GETTIME (gc_end);
-
-       memset (infos, 0, sizeof (infos));
-       infos [0].generation = generation_to_collect;
-       infos [0].reason = reason;
-       infos [0].is_overflow = FALSE;
-       infos [1].generation = -1;
-       infos [0].total_time = SGEN_TV_ELAPSED (gc_start, gc_end);
-
-       SGEN_ASSERT (0, !concurrent_collection_in_progress, "Why did this not get handled above?");
-
        if (overflow_generation_to_collect != -1) {
+               SGEN_ASSERT (0, !concurrent_collection_in_progress, "We don't yet support overflow collections with the concurrent collector");
+
                /*
                 * We need to do an overflow collection, either because we ran out of memory
                 * or the nursery is fully pinned.
                 */
 
-               infos [1].generation = overflow_generation_to_collect;
-               infos [1].reason = overflow_reason;
-               infos [1].is_overflow = TRUE;
-               gc_start = gc_end;
-
                if (overflow_generation_to_collect == GENERATION_NURSERY)
-                       collect_nursery (NULL, FALSE);
+                       collect_nursery (overflow_reason, TRUE, NULL, FALSE);
                else
-                       major_do_collection (overflow_reason, wait_to_finish);
-
-               TV_GETTIME (gc_end);
-               infos [1].total_time = SGEN_TV_ELAPSED (gc_start, gc_end);
+                       major_do_collection (overflow_reason, TRUE, wait_to_finish);
 
                oldest_generation_collected = MAX (oldest_generation_collected, overflow_generation_to_collect);
        }
@@ -2335,13 +2289,12 @@ sgen_perform_collection (size_t requested_size, int generation_to_collect, const
                degraded_mode = 1;
        }
 
- done:
        g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
 
        TV_GETTIME (gc_total_end);
        time_max = MAX (time_max, TV_ELAPSED (gc_total_start, gc_total_end));
 
-       sgen_restart_world (oldest_generation_collected, infos);
+       sgen_restart_world (oldest_generation_collected);
 }
 
 /*
@@ -3016,6 +2969,7 @@ sgen_gc_init (void)
 
        alloc_nursery ();
 
+       sgen_pinning_init ();
        sgen_cement_init (cement_enabled);
 
        if ((env = g_getenv (MONO_GC_DEBUG_NAME))) {
@@ -3187,11 +3141,7 @@ sgen_gc_lock (void)
 void
 sgen_gc_unlock (void)
 {
-       gboolean try_free = sgen_try_free_some_memory;
-       sgen_try_free_some_memory = FALSE;
        mono_coop_mutex_unlock (&gc_mutex);
-       if (try_free)
-               mono_thread_hazardous_try_free_some ();
 }
 
 void
@@ -3242,9 +3192,10 @@ sgen_stop_world (int generation)
 
 /* LOCKING: assumes the GC lock is held */
 void
-sgen_restart_world (int generation, GGTimingInfo *timing)
+sgen_restart_world (int generation)
 {
        long long major_total = -1, major_marked = -1, los_total = -1, los_marked = -1;
+       gint64 stw_time;
 
        SGEN_ASSERT (0, world_is_stopped, "Why are we restarting a running world?");
 
@@ -3252,18 +3203,16 @@ sgen_restart_world (int generation, GGTimingInfo *timing)
                count_cards (&major_total, &major_marked, &los_total, &los_marked);
        binary_protocol_world_restarting (generation, sgen_timestamp (), major_total, major_marked, los_total, los_marked);
 
-       sgen_client_restart_world (generation, timing);
-
        world_is_stopped = FALSE;
 
-       binary_protocol_world_restarted (generation, sgen_timestamp ());
+       sgen_client_restart_world (generation, &stw_time);
 
-       sgen_try_free_some_memory = TRUE;
+       binary_protocol_world_restarted (generation, sgen_timestamp ());
 
        if (sgen_client_bridge_need_processing ())
                sgen_client_bridge_processing_finish (generation);
 
-       sgen_memgov_collection_end (generation, timing, timing ? 2 : 0);
+       sgen_memgov_collection_end (generation, stw_time);
 }
 
 gboolean
@@ -3278,7 +3227,7 @@ sgen_check_whole_heap_stw (void)
        sgen_stop_world (0);
        sgen_clear_nursery_fragments ();
        sgen_check_whole_heap (FALSE);
-       sgen_restart_world (0, NULL);
+       sgen_restart_world (0);
 }
 
 gint64