Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / sgen / sgen-memory-governor.c
index 4648d8d761d444cbb8b6ec411d6fff6d3d2ad4da..12c775a2384a1c106251a5ab5e5c2e1830bf81ce 100644 (file)
@@ -1,6 +1,6 @@
-/*
- * sgen-memory-governor.c: When to schedule collections based on
- * memory usage.
+/**
+ * \file
+ * When to schedule collections based on memory usage.
  *
  * Author:
  *     Rodrigo Kumpera (rkumpera@novell.com)
 
 #include "mono/sgen/sgen-gc.h"
 #include "mono/sgen/sgen-memory-governor.h"
-#include "mono/sgen/sgen-thread-pool.h"
+#include "mono/sgen/sgen-workers.h"
 #include "mono/sgen/sgen-client.h"
 
-#define MIN_MINOR_COLLECTION_ALLOWANCE ((mword)(DEFAULT_NURSERY_SIZE * default_allowance_nursery_size_ratio))
+#define MIN_MINOR_COLLECTION_ALLOWANCE ((mword)(SGEN_DEFAULT_NURSERY_SIZE * default_allowance_nursery_size_ratio))
+
+static SgenPointerQueue log_entries = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_TEMPORARY);
+static mono_mutex_t log_entries_mutex;
 
 mword total_promoted_size = 0;
 mword total_allocated_major = 0;
@@ -42,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;
@@ -105,7 +111,7 @@ sgen_memgov_calculate_minor_collection_allowance (void)
 
        /* FIXME: Why is this here? */
        if (major_collector.free_swept_blocks)
-               major_collector.free_swept_blocks (allowance);
+               major_collector.free_swept_blocks (major_collector.get_num_major_sections () * SGEN_DEFAULT_ALLOWANCE_HEAP_SIZE_RATIO);
 
        major_collection_trigger_size = new_heap_size + allowance;
 
@@ -164,11 +170,37 @@ 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_os_mutex_lock (&log_entries_mutex);
+       sgen_pointer_queue_add (&log_entries, log_entry);
+       mono_os_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
@@ -185,16 +217,20 @@ sgen_memgov_major_pre_sweep (void)
 void
 sgen_memgov_major_post_sweep (mword used_slots_size)
 {
-       mword num_major_sections = major_collector.get_num_major_sections ();
+       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;
 
-       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR_SWEEP: major size: %dK in use: %dK",
-               num_major_sections * major_collector.section_size / 1024,
-               (used_slots_size + total_allocated_major - total_allocated_major_end) / 1024);
+               sgen_add_log_entry (log_entry);
+       }
        last_used_slots_size = used_slots_size;
 }
 
 void
-sgen_memgov_major_collection_start (void)
+sgen_memgov_major_collection_start (gboolean concurrent, const char *reason)
 {
        need_calculate_minor_collection_allowance = TRUE;
        major_start_heap_size = get_heap_size ();
@@ -202,13 +238,40 @@ sgen_memgov_major_collection_start (void)
        if (debug_print_allowance) {
                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 ();
@@ -221,13 +284,76 @@ 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 %zdK major size: %zdK in use: %zdK los size: %zdK in use: %zdK",
+                               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: %zdK in use: %zdK",
+                               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: %zdK in use: %zdK",
+                               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: %zdK in use: %zdK",
+                               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], total_promoted_size - total_promoted_size_start, last_used_slots_size + total_allocated_major - total_allocated_major_end);
+       /*
+        * 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_os_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_os_mutex_unlock (&log_entries_mutex);
        }
 }
 
@@ -258,13 +384,13 @@ sgen_assert_memory_alloc (void *ptr, size_t requested_size, const char *assert_d
  * This must not require any lock.
  */
 void*
-sgen_alloc_os_memory (size_t size, SgenAllocFlags flags, const char *assert_description)
+sgen_alloc_os_memory (size_t size, SgenAllocFlags flags, const char *assert_description, MonoMemAccountType type)
 {
        void *ptr;
 
        g_assert (!(flags & ~(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE)));
 
-       ptr = mono_valloc (0, size, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE));
+       ptr = mono_valloc (0, size, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE), type);
        sgen_assert_memory_alloc (ptr, size, assert_description);
        if (ptr) {
                SGEN_ATOMIC_ADD_P (total_alloc, size);
@@ -274,14 +400,15 @@ sgen_alloc_os_memory (size_t size, SgenAllocFlags flags, const char *assert_desc
 }
 
 /* size must be a power of 2 */
+// FIXME: remove assert_description
 void*
-sgen_alloc_os_memory_aligned (size_t size, mword alignment, SgenAllocFlags flags, const char *assert_description)
+sgen_alloc_os_memory_aligned (size_t size, mword alignment, SgenAllocFlags flags, const char *assert_description, MonoMemAccountType type)
 {
        void *ptr;
 
        g_assert (!(flags & ~(SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE)));
 
-       ptr = mono_valloc_aligned (size, alignment, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE));
+       ptr = mono_valloc_aligned (size, alignment, prot_flags_for_activate (flags & SGEN_ALLOC_ACTIVATE), type);
        sgen_assert_memory_alloc (ptr, size, assert_description);
        if (ptr) {
                SGEN_ATOMIC_ADD_P (total_alloc, size);
@@ -294,11 +421,11 @@ sgen_alloc_os_memory_aligned (size_t size, mword alignment, SgenAllocFlags flags
  * Free the memory returned by sgen_alloc_os_memory (), returning it to the OS.
  */
 void
-sgen_free_os_memory (void *addr, size_t size, SgenAllocFlags flags)
+sgen_free_os_memory (void *addr, size_t size, SgenAllocFlags flags, MonoMemAccountType type)
 {
        g_assert (!(flags & ~SGEN_ALLOC_HEAP));
 
-       mono_vfree (addr, size);
+       mono_vfree (addr, size, type);
        SGEN_ATOMIC_ADD_P (total_alloc, -(gssize)size);
        total_alloc_max = MAX (total_alloc_max, total_alloc);
 }
@@ -332,7 +459,7 @@ gboolean
 sgen_memgov_try_alloc_space (mword size, int space)
 {
        if (sgen_memgov_available_free_space () < size) {
-               SGEN_ASSERT (4, !sgen_thread_pool_is_thread_pool_thread (mono_native_thread_id_get ()), "Memory shouldn't run out in worker thread");
+               SGEN_ASSERT (4, !sgen_workers_is_worker_thread (mono_native_thread_id_get ()), "Memory shouldn't run out in worker thread");
                return FALSE;
        }
 
@@ -350,8 +477,12 @@ sgen_memgov_init (size_t max_heap, size_t soft_limit, gboolean debug_allowance,
        debug_print_allowance = debug_allowance;
        major_collection_trigger_size = MIN_MINOR_COLLECTION_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_counters_register ("Memgov alloc", MONO_COUNTER_GC | MONO_COUNTER_WORD | MONO_COUNTER_BYTES | MONO_COUNTER_VARIABLE, (void*)&total_alloc);
+       mono_counters_register ("Memgov max alloc", MONO_COUNTER_GC | MONO_COUNTER_WORD | MONO_COUNTER_BYTES | MONO_COUNTER_MONOTONIC, (void*)&total_alloc_max);
+
+       mono_os_mutex_init (&log_entries_mutex);
+
+       sgen_register_fixed_internal_mem_type (INTERNAL_MEM_LOG_ENTRY, sizeof (SgenLogEntry));
 
        if (max_heap == 0)
                return;
@@ -361,11 +492,11 @@ sgen_memgov_init (size_t max_heap, size_t soft_limit, gboolean debug_allowance,
                max_heap = soft_limit;
        }
 
-       if (max_heap < sgen_nursery_size * 4) {
+       if (max_heap < SGEN_DEFAULT_NURSERY_SIZE * 4) {
                sgen_env_var_error (MONO_GC_PARAMS_NAME, "Setting to minimum.", "`max-heap-size` must be at least 4 times as large as `nursery size`.");
-               max_heap = sgen_nursery_size * 4;
+               max_heap = SGEN_DEFAULT_NURSERY_SIZE * 4;
        }
-       max_heap_size = max_heap - sgen_nursery_size;
+       max_heap_size = max_heap - SGEN_DEFAULT_NURSERY_SIZE;
 
        if (allowance_ratio)
                default_allowance_nursery_size_ratio = allowance_ratio;