Merge pull request #3066 from alexanderkyte/pedump_sgen
[mono.git] / mono / sgen / sgen-memory-governor.c
index ce34f7f4a67a7d304ff038e674c01aa38651e0c5..e7de362976055377c0a67e6da9e4874d0c3b2db5 100644 (file)
  * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
  * 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.
  */
 
 #include "config.h"
 
 #define MIN_MINOR_COLLECTION_ALLOWANCE ((mword)(DEFAULT_NURSERY_SIZE * default_allowance_nursery_size_ratio))
 
+static SgenPointerQueue log_entries = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_TEMPORARY);
+static MonoCoopMutex log_entries_mutex;
+
+mword total_promoted_size = 0;
+mword total_allocated_major = 0;
+static mword total_promoted_size_start;
+static mword total_allocated_major_end;
+
 /*Heap limits and allocation knobs*/
 static mword max_heap_size = ((mword)0)- ((mword)1);
 static mword soft_heap_limit = ((mword)0) - ((mword)1);
@@ -48,6 +45,9 @@ static mword allocated_heap;
 static mword total_alloc = 0;
 static mword total_alloc_max = 0;
 
+static SGEN_TV_DECLARE(last_minor_start);
+static SGEN_TV_DECLARE(last_major_start);
+
 /* GC triggers. */
 
 static gboolean debug_print_allowance = FALSE;
@@ -56,13 +56,14 @@ static gboolean debug_print_allowance = FALSE;
 /* use this to tune when to do a major/minor collection */
 static mword major_collection_trigger_size;
 
-static mword last_major_num_sections = 0;
-static mword last_los_memory_usage = 0;
+static mword major_pre_sweep_heap_size;
+static mword major_start_heap_size;
 
 static gboolean need_calculate_minor_collection_allowance;
 
 /* The size of the LOS after the last major collection, after sweeping. */
 static mword last_collection_los_memory_usage = 0;
+static mword last_used_slots_size = 0;
 
 static mword sgen_memgov_available_free_space (void);
 
@@ -73,6 +74,7 @@ static void
 sgen_memgov_calculate_minor_collection_allowance (void)
 {
        size_t new_major, new_heap_size, allowance_target, allowance;
+       size_t decrease;
 
        if (!need_calculate_minor_collection_allowance)
                return;
@@ -90,6 +92,16 @@ sgen_memgov_calculate_minor_collection_allowance (void)
 
        allowance = MAX (allowance_target, MIN_MINOR_COLLECTION_ALLOWANCE);
 
+       /*
+        * For the concurrent collector, we decrease the allowance relative to the memory
+        * growth during the M&S phase, survival rate of the collection and the allowance
+        * ratio.
+        */
+       decrease = (major_pre_sweep_heap_size - major_start_heap_size) * ((float)new_heap_size / major_pre_sweep_heap_size) * (SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO + 1);
+       if (decrease > allowance)
+               decrease = allowance;
+       allowance -= decrease;
+
        if (new_heap_size + allowance > soft_heap_limit) {
                if (new_heap_size > soft_heap_limit)
                        allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
@@ -129,13 +141,14 @@ sgen_need_major_collection (mword space_needed)
                if (heap_size <= major_collection_trigger_size)
                        return FALSE; 
 
-               /* We allow the heap to grow an additional third of the allowance during a concurrent collection */
-               if ((heap_size - major_collection_trigger_size) >
-                               (major_collection_trigger_size
-                               * (SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO / (SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO + 1))
-                               * SGEN_DEFAULT_CONCURRENT_HEAP_ALLOWANCE_RATIO)) {
+               /*
+                * The more the heap grows, the more we need to decrease the allowance above,
+                * in order to have similar trigger sizes as the synchronous collector.
+                * If the heap grows so much that we would need to have a negative allowance,
+                * we force the finishing of the collection, to avoid increased memory usage.
+                */
+               if ((heap_size - major_start_heap_size) > major_start_heap_size * SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO)
                        return TRUE;
-               }
                return FALSE;
        }
 
@@ -156,28 +169,110 @@ sgen_need_major_collection (mword space_needed)
 void
 sgen_memgov_minor_collection_start (void)
 {
+       total_promoted_size_start = total_promoted_size;
+       SGEN_TV_GETTIME (last_minor_start);
+}
+
+static void
+sgen_add_log_entry (SgenLogEntry *log_entry)
+{
+       mono_coop_mutex_lock (&log_entries_mutex);
+       sgen_pointer_queue_add (&log_entries, log_entry);
+       mono_coop_mutex_unlock (&log_entries_mutex);
 }
 
 void
-sgen_memgov_minor_collection_end (void)
+sgen_memgov_minor_collection_end (const char *reason, gboolean is_overflow)
 {
+       if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
+               SgenLogEntry *log_entry = (SgenLogEntry*)sgen_alloc_internal (INTERNAL_MEM_LOG_ENTRY);
+               SGEN_TV_DECLARE (current_time);
+               SGEN_TV_GETTIME (current_time);
+
+               log_entry->type = SGEN_LOG_NURSERY;
+               log_entry->reason = reason;
+               log_entry->is_overflow = is_overflow;
+               log_entry->time = SGEN_TV_ELAPSED (last_minor_start, current_time);
+               log_entry->promoted_size = total_promoted_size - total_promoted_size_start;
+               log_entry->major_size = major_collector.get_num_major_sections () * major_collector.section_size;
+               log_entry->major_size_in_use = last_used_slots_size + total_allocated_major - total_allocated_major_end;
+               log_entry->los_size = los_memory_usage_total;
+               log_entry->los_size_in_use = los_memory_usage;
+
+               sgen_add_log_entry (log_entry);
+       }
 }
 
 void
-sgen_memgov_major_collection_start (void)
+sgen_memgov_major_pre_sweep (void)
+{
+       if (sgen_concurrent_collection_in_progress ()) {
+               major_pre_sweep_heap_size = get_heap_size ();
+       } else {
+               /* We decrease the allowance only in the concurrent case */
+               major_pre_sweep_heap_size = major_start_heap_size;
+       }
+}
+
+void
+sgen_memgov_major_post_sweep (mword used_slots_size)
+{
+       if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
+               SgenLogEntry *log_entry = (SgenLogEntry*)sgen_alloc_internal (INTERNAL_MEM_LOG_ENTRY);
+
+               log_entry->type = SGEN_LOG_MAJOR_SWEEP_FINISH;
+               log_entry->major_size = major_collector.get_num_major_sections () * major_collector.section_size;
+               log_entry->major_size_in_use = used_slots_size + total_allocated_major - total_allocated_major_end;
+
+               sgen_add_log_entry (log_entry);
+       }
+       last_used_slots_size = used_slots_size;
+}
+
+void
+sgen_memgov_major_collection_start (gboolean concurrent, const char *reason)
 {
        need_calculate_minor_collection_allowance = TRUE;
+       major_start_heap_size = get_heap_size ();
 
        if (debug_print_allowance) {
-               SGEN_LOG (0, "Starting collection with heap size %ld bytes", (long)get_heap_size ());
+               SGEN_LOG (0, "Starting collection with heap size %ld bytes", (long)major_start_heap_size);
        }
+       if (concurrent && mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
+               SgenLogEntry *log_entry = (SgenLogEntry*)sgen_alloc_internal (INTERNAL_MEM_LOG_ENTRY);
+
+               log_entry->type = SGEN_LOG_MAJOR_CONC_START;
+               log_entry->reason = reason;
+
+               sgen_add_log_entry (log_entry);
+       }
+       SGEN_TV_GETTIME (last_major_start);
 }
 
 void
-sgen_memgov_major_collection_end (gboolean forced)
+sgen_memgov_major_collection_end (gboolean forced, gboolean concurrent, const char *reason, gboolean is_overflow)
 {
-       last_collection_los_memory_usage = los_memory_usage;
+       if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
+               SgenLogEntry *log_entry = (SgenLogEntry*)sgen_alloc_internal (INTERNAL_MEM_LOG_ENTRY);
+               SGEN_TV_DECLARE (current_time);
+               SGEN_TV_GETTIME (current_time);
+
+               if (concurrent) {
+                       log_entry->type = SGEN_LOG_MAJOR_CONC_FINISH;
+               } else {
+                       log_entry->type = SGEN_LOG_MAJOR_SERIAL;
+               }
+               log_entry->time = SGEN_TV_ELAPSED (last_major_start, current_time);
+               log_entry->reason = reason;
+               log_entry->is_overflow = is_overflow;
+               log_entry->los_size = los_memory_usage_total;
+               log_entry->los_size_in_use = los_memory_usage;
+
+               sgen_add_log_entry (log_entry);
+       }
 
+       last_collection_los_memory_usage = los_memory_usage;
+       total_allocated_major_end = total_allocated_major;
        if (forced) {
                sgen_get_major_collector ()->finish_sweeping ();
                sgen_memgov_calculate_minor_collection_allowance ();
@@ -189,15 +284,77 @@ sgen_memgov_collection_start (int generation)
 {
 }
 
+static void
+sgen_output_log_entry (SgenLogEntry *entry, gint64 stw_time, int generation)
+{
+       char full_timing_buff [1024];
+       full_timing_buff [0] = '\0';
+
+       if (!entry->is_overflow)
+                sprintf (full_timing_buff, "stw %.2fms", stw_time / 10000.0f);
+
+       switch (entry->type) {
+               case SGEN_LOG_NURSERY:
+                       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MINOR%s: (%s) time %.2fms, %s promoted %dK major size: %dK in use: %dK los size: %dK in use: %dK",
+                               entry->is_overflow ? "_OVERFLOW" : "",
+                               entry->reason ? entry->reason : "",
+                               entry->time / 10000.0f,
+                               (generation == GENERATION_NURSERY) ? full_timing_buff : "",
+                               entry->promoted_size / 1024,
+                               entry->major_size / 1024,
+                               entry->major_size_in_use / 1024,
+                               entry->los_size / 1024,
+                               entry->los_size_in_use / 1024);
+                       break;
+               case SGEN_LOG_MAJOR_SERIAL:
+                       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR%s: (%s) time %.2fms, %s los size: %dK in use: %dK",
+                               entry->is_overflow ? "_OVERFLOW" : "",
+                               entry->reason ? entry->reason : "",
+                               (int)entry->time / 10000.0f,
+                               full_timing_buff,
+                               entry->los_size / 1024,
+                               entry->los_size_in_use / 1024);
+                       break;
+               case SGEN_LOG_MAJOR_CONC_START:
+                       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR_CONCURRENT_START: (%s)", entry->reason ? entry->reason : "");
+                       break;
+               case SGEN_LOG_MAJOR_CONC_FINISH:
+                       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR_CONCURRENT_FINISH: (%s) time %.2fms, %s los size: %dK in use: %dK",
+                               entry->reason ? entry->reason : "",
+                               entry->time / 10000.0f,
+                               full_timing_buff,
+                               entry->los_size / 1024,
+                               entry->los_size_in_use / 1024);
+                       break;
+               case SGEN_LOG_MAJOR_SWEEP_FINISH:
+                       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR_SWEEP: major size: %dK in use: %dK",
+                               entry->major_size / 1024,
+                               entry->major_size_in_use / 1024);
+                       break;
+               default:
+                       SGEN_ASSERT (0, FALSE, "Invalid log entry type");
+                       break;
+       }
+}
+
 void
-sgen_memgov_collection_end (int generation, GGTimingInfo* info, int info_count)
+sgen_memgov_collection_end (int generation, gint64 stw_time)
 {
-       int i;
-       for (i = 0; i < info_count; ++i) {
-               if (info[i].generation != -1)
-                       sgen_client_log_timing (&info [i], last_major_num_sections, last_los_memory_usage);
+       /*
+        * At this moment the world has been restarted which means we can log all pending entries
+        * without risking deadlocks.
+        */
+       if (mono_trace_is_traced (G_LOG_LEVEL_INFO, MONO_TRACE_GC)) {
+               size_t i;
+               SGEN_ASSERT (0, !sgen_is_world_stopped (), "We can't log if the world is stopped");
+               mono_coop_mutex_lock (&log_entries_mutex);
+               for (i = 0; i < log_entries.next_slot; i++) {
+                       sgen_output_log_entry (log_entries.data [i], stw_time, generation);
+                       sgen_free_internal (log_entries.data [i], INTERNAL_MEM_LOG_ENTRY);
+               }
+               sgen_pointer_queue_clear (&log_entries);
+               mono_coop_mutex_unlock (&log_entries_mutex);
        }
-       last_major_num_sections = major_collector.get_num_major_sections ();
 }
 
 /*
@@ -322,6 +479,10 @@ sgen_memgov_init (size_t max_heap, size_t soft_limit, gboolean debug_allowance,
        mono_counters_register ("Memgov alloc", MONO_COUNTER_GC | MONO_COUNTER_WORD | MONO_COUNTER_BYTES | MONO_COUNTER_VARIABLE, &total_alloc);
        mono_counters_register ("Memgov max alloc", MONO_COUNTER_GC | MONO_COUNTER_WORD | MONO_COUNTER_BYTES | MONO_COUNTER_MONOTONIC, &total_alloc_max);
 
+       mono_coop_mutex_init (&log_entries_mutex);
+
+       sgen_register_fixed_internal_mem_type (INTERNAL_MEM_LOG_ENTRY, sizeof (SgenLogEntry));
+
        if (max_heap == 0)
                return;