[sgen] Refactor collection logging
[mono.git] / mono / metadata / sgen-stw.c
index b2cb17c3b91dd3bb50bbd4c6812409f6734de873..ea14448ccfbfaeb7b376fbf06abd59135cf6b680 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.
  */
 
 #include "config.h"
@@ -35,6 +24,7 @@
 #include "sgen/sgen-client.h"
 #include "metadata/sgen-bridge-internals.h"
 #include "metadata/gc-internals.h"
+#include "utils/mono-threads.h"
 
 #define TV_DECLARE SGEN_TV_DECLARE
 #define TV_GETTIME SGEN_TV_GETTIME
 static void sgen_unified_suspend_restart_world (void);
 static void sgen_unified_suspend_stop_world (void);
 
+static TV_DECLARE (end_of_last_stw);
+
+guint64 mono_time_since_last_stw ()
+{
+       if (end_of_last_stw == 0)
+               return 0;
+
+       TV_DECLARE (current_time);
+       TV_GETTIME (current_time);
+       return TV_ELAPSED (end_of_last_stw, current_time);
+}
+
 unsigned int sgen_global_stop_count = 0;
 
 inline static void*
@@ -54,34 +56,24 @@ align_pointer (void *ptr)
        return (void*)p;
 }
 
-#ifdef USE_MONO_CTX
-static MonoContext cur_thread_ctx;
-#else
-static mword cur_thread_regs [ARCH_NUM_REGS];
-#endif
-
 static void
 update_current_thread_stack (void *start)
 {
        int stack_guard = 0;
-#if !defined(USE_MONO_CTX)
-       void *reg_ptr = cur_thread_regs;
-#endif
        SgenThreadInfo *info = mono_thread_info_current ();
-       
+
        info->client_info.stack_start = align_pointer (&stack_guard);
+       g_assert (info->client_info.stack_start);
        g_assert (info->client_info.stack_start >= info->client_info.stack_start_limit && info->client_info.stack_start < info->client_info.stack_end);
-#ifdef USE_MONO_CTX
-       MONO_CONTEXT_GET_CURRENT (cur_thread_ctx);
-       memcpy (&info->client_info.ctx, &cur_thread_ctx, sizeof (MonoContext));
-       if (mono_gc_get_gc_callbacks ()->thread_suspend_func)
-               mono_gc_get_gc_callbacks ()->thread_suspend_func (info->client_info.runtime_data, NULL, &info->client_info.ctx);
+
+#if !defined(MONO_CROSS_COMPILE) && MONO_ARCH_HAS_MONO_CONTEXT
+       MONO_CONTEXT_GET_CURRENT (info->client_info.ctx);
 #else
-       ARCH_STORE_REGS (reg_ptr);
-       memcpy (&info->client_info.regs, reg_ptr, sizeof (info->client_info.regs));
-       if (mono_gc_get_gc_callbacks ()->thread_suspend_func)
-               mono_gc_get_gc_callbacks ()->thread_suspend_func (info->client_info.runtime_data, NULL, NULL);
+       g_error ("Sgen STW requires a working mono-context");
 #endif
+
+       if (mono_gc_get_gc_callbacks ()->thread_suspend_func)
+               mono_gc_get_gc_callbacks ()->thread_suspend_func (info->client_info.runtime_data, NULL, &info->client_info.ctx);
 }
 
 static gboolean
@@ -113,7 +105,6 @@ is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
 static int
 restart_threads_until_none_in_managed_allocator (void)
 {
-       SgenThreadInfo *info;
        int num_threads_died = 0;
        int sleep_duration = -1;
 
@@ -121,7 +112,7 @@ restart_threads_until_none_in_managed_allocator (void)
                int restart_count = 0, restarted_count = 0;
                /* restart all threads that stopped in the
                   allocator */
-               FOREACH_THREAD_SAFE (info) {
+               FOREACH_THREAD (info) {
                        gboolean result;
                        if (info->client_info.skip || info->client_info.gc_disabled || info->client_info.suspend_done)
                                continue;
@@ -146,7 +137,7 @@ restart_threads_until_none_in_managed_allocator (void)
                                info->client_info.stopped_domain = NULL;
                                info->client_info.suspend_done = TRUE;
                        }
-               } END_FOREACH_THREAD_SAFE
+               } FOREACH_THREAD_END
                /* if no threads were restarted, we're done */
                if (restart_count == 0)
                        break;
@@ -174,7 +165,7 @@ restart_threads_until_none_in_managed_allocator (void)
                        } else {
                                info->client_info.skip = 1;
                        }
-               } END_FOREACH_THREAD
+               } FOREACH_THREAD_END
                /* some threads might have died */
                num_threads_died += restart_count - restarted_count;
                /* wait for the threads to signal their suspension
@@ -249,13 +240,11 @@ sgen_client_stop_world (int generation)
 
 /* LOCKING: assumes the GC lock is held */
 void
-sgen_client_restart_world (int generation, GGTimingInfo *timing)
+sgen_client_restart_world (int generation, gint64 *stw_time)
 {
-       SgenThreadInfo *info;
        TV_DECLARE (end_sw);
        TV_DECLARE (start_handshake);
-       TV_DECLARE (end_bridge);
-       unsigned long usec, bridge_usec;
+       unsigned long usec;
 
        /* notify the profiler of the leftovers */
        /* FIXME this is the wrong spot at we can STW for non collection reasons. */
@@ -264,12 +253,8 @@ sgen_client_restart_world (int generation, GGTimingInfo *timing)
 
        FOREACH_THREAD (info) {
                info->client_info.stack_start = NULL;
-#ifdef USE_MONO_CTX
                memset (&info->client_info.ctx, 0, sizeof (MonoContext));
-#else
-               memset (&info->client_info.regs, 0, sizeof (info->client_info.regs));
-#endif
-       } END_FOREACH_THREAD
+       } FOREACH_THREAD_END
 
        TV_GETTIME (start_handshake);
 
@@ -282,6 +267,7 @@ sgen_client_restart_world (int generation, GGTimingInfo *timing)
        time_restart_world += TV_ELAPSED (start_handshake, end_sw);
        usec = TV_ELAPSED (stop_world_time, end_sw);
        max_pause_usec = MAX (usec, max_pause_usec);
+       end_of_last_stw = end_sw;
 
        SGEN_LOG (2, "restarted (pause time: %d usec, max: %d)", (int)usec, (int)max_pause_usec);
 
@@ -297,13 +283,7 @@ sgen_client_restart_world (int generation, GGTimingInfo *timing)
         */
        release_gc_locks ();
 
-       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;
-       }
+       *stw_time = usec;
 }
 
 void
@@ -316,7 +296,7 @@ mono_sgen_init_stw (void)
 /* Unified suspend code */
 
 static gboolean
-sgen_is_thread_in_current_stw (SgenThreadInfo *info)
+sgen_is_thread_in_current_stw (SgenThreadInfo *info, int *reason)
 {
        /*
        A thread explicitly asked to be skiped because it holds no managed state.
@@ -324,6 +304,8 @@ sgen_is_thread_in_current_stw (SgenThreadInfo *info)
        FIXME Use an atomic variable for this to avoid everyone taking the GC LOCK.
        */
        if (info->client_info.gc_disabled) {
+               if (reason)
+                       *reason = 1;
                return FALSE;
        }
 
@@ -332,6 +314,8 @@ sgen_is_thread_in_current_stw (SgenThreadInfo *info)
        FIXME: can't we merge this with thread_is_dying?
        */
        if (info->client_info.skip) {
+               if (reason)
+                       *reason = 2;
                return FALSE;
        }
 
@@ -339,6 +323,8 @@ sgen_is_thread_in_current_stw (SgenThreadInfo *info)
        Suspending the current thread will deadlock us, bad idea.
        */
        if (info == mono_thread_info_current ()) {
+               if (reason)
+                       *reason = 3;
                return FALSE;
        }
 
@@ -347,6 +333,8 @@ sgen_is_thread_in_current_stw (SgenThreadInfo *info)
        FIXME Use some state bit in SgenThreadInfo for this.
        */
        if (sgen_thread_pool_is_thread_pool_thread (mono_thread_info_get_tid (info))) {
+               if (reason)
+                       *reason = 4;
                return FALSE;
        }
 
@@ -355,63 +343,44 @@ sgen_is_thread_in_current_stw (SgenThreadInfo *info)
        FIXME: can't we merge this with skip
        */
        if (!mono_thread_info_is_live (info)) {
+               if (reason)
+                       *reason = 5;
                return FALSE;
        }
 
        return TRUE;
 }
 
-static void
-update_sgen_info (SgenThreadInfo *info)
-{
-       char *stack_start;
-
-       /* Once we remove the old suspend code, we should move sgen to directly access the state in MonoThread */
-       info->client_info.stopped_domain = (MonoDomain *)mono_thread_info_tls_get (info, TLS_KEY_DOMAIN);
-       info->client_info.stopped_ip = (gpointer) MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx);
-       stack_start = (char*)MONO_CONTEXT_GET_SP (&mono_thread_info_get_suspend_state (info)->ctx) - REDZONE_SIZE;
-
-       /* altstack signal handler, sgen can't handle them, mono-threads should have handled this. */
-       if (stack_start < (char*)info->client_info.stack_start_limit || stack_start >= (char*)info->client_info.stack_end)
-               g_error ("BAD STACK");
-
-       info->client_info.stack_start = stack_start;
-#ifdef USE_MONO_CTX
-       info->client_info.ctx = mono_thread_info_get_suspend_state (info)->ctx;
-#else
-       g_assert_not_reached ();
-#endif
-}
-
 static void
 sgen_unified_suspend_stop_world (void)
 {
        int restart_counter;
-       SgenThreadInfo *info;
        int sleep_duration = -1;
 
        mono_threads_begin_global_suspend ();
        THREADS_STW_DEBUG ("[GC-STW-BEGIN] *** BEGIN SUSPEND *** \n");
 
-       FOREACH_THREAD_SAFE (info) {
+       FOREACH_THREAD (info) {
+               int reason;
                info->client_info.skip = FALSE;
                info->client_info.suspend_done = FALSE;
-               if (sgen_is_thread_in_current_stw (info)) {
-                       info->client_info.skip = !mono_thread_info_begin_suspend (info, FALSE);
+               if (sgen_is_thread_in_current_stw (info, &reason)) {
+                       info->client_info.skip = !mono_thread_info_begin_suspend (info);
                        THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] SUSPEND thread %p skip %d\n", mono_thread_info_get_tid (info), info->client_info.skip);
                } else {
-                       THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] IGNORE thread %p skip %d\n", mono_thread_info_get_tid (info), info->client_info.skip);
+                       THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] IGNORE thread %p skip %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.skip, reason);
                }
-       } END_FOREACH_THREAD_SAFE
+       } FOREACH_THREAD_END
 
        mono_thread_info_current ()->client_info.suspend_done = TRUE;
        mono_threads_wait_pending_operations ();
 
        for (;;) {
                restart_counter = 0;
-               FOREACH_THREAD_SAFE (info) {
-                       if (info->client_info.suspend_done || !sgen_is_thread_in_current_stw (info)) {
-                               THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE thread %p not been processed done %d current %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info));
+               FOREACH_THREAD (info) {
+                       int reason = 0;
+                       if (info->client_info.suspend_done || !sgen_is_thread_in_current_stw (info, &reason)) {
+                               THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE RESUME thread %p not been processed done %d current %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info, NULL), reason);
                                continue;
                        }
 
@@ -421,12 +390,11 @@ sgen_unified_suspend_stop_world (void)
                        - We haven't accepted the previous suspend as good.
                        - We haven't gave up on it for this STW (it's either bad or asked not to)
                        */
-                       if (!mono_thread_info_check_suspend_result (info)) {
-                               THREADS_STW_DEBUG ("[GC-STW-RESTART] SKIP thread %p failed to finish to suspend\n", mono_thread_info_get_tid (info));
-                               info->client_info.skip = TRUE;
-                       } else if (mono_thread_info_in_critical_location (info)) {
+                       if (mono_thread_info_in_critical_location (info)) {
                                gboolean res;
-                               g_assert (mono_thread_info_suspend_count (info) == 1);
+                               gint suspend_count = mono_thread_info_suspend_count (info);
+                               if (!(suspend_count == 1))
+                                       g_error ("[%p] suspend_count = %d, but should be 1", mono_thread_info_get_tid (info), suspend_count);
                                res = mono_thread_info_begin_resume (info);
                                THREADS_STW_DEBUG ("[GC-STW-RESTART] RESTART thread %p skip %d\n", mono_thread_info_get_tid (info), res);
                                if (res)
@@ -438,61 +406,93 @@ sgen_unified_suspend_stop_world (void)
                                g_assert (!info->client_info.in_critical_region);
                                info->client_info.suspend_done = TRUE;
                        }
-               } END_FOREACH_THREAD_SAFE
+               } FOREACH_THREAD_END
 
                if (restart_counter == 0)
                        break;
                mono_threads_wait_pending_operations ();
 
                if (sleep_duration < 0) {
-#ifdef HOST_WIN32
-                       SwitchToThread ();
-#else
-                       sched_yield ();
-#endif
+                       mono_thread_info_yield ();
                        sleep_duration = 0;
                } else {
                        g_usleep (sleep_duration);
                        sleep_duration += 10;
                }
 
-               FOREACH_THREAD_SAFE (info) {
-                       if (sgen_is_thread_in_current_stw (info) && mono_thread_info_is_running (info)) {
-                               gboolean res = mono_thread_info_begin_suspend (info, FALSE);
+               FOREACH_THREAD (info) {
+                       int reason = 0;
+                       if (info->client_info.suspend_done || !sgen_is_thread_in_current_stw (info, &reason)) {
+                               THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE SUSPEND thread %p not been processed done %d current %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info, NULL), reason);
+                               continue;
+                       }
+
+                       if (mono_thread_info_is_running (info)) {
+                               gboolean res = mono_thread_info_begin_suspend (info);
                                THREADS_STW_DEBUG ("[GC-STW-RESTART] SUSPEND thread %p skip %d\n", mono_thread_info_get_tid (info), res);
                                if (!res)
                                        info->client_info.skip = TRUE;
                        }
-               } END_FOREACH_THREAD_SAFE
+               } FOREACH_THREAD_END
 
                mono_threads_wait_pending_operations ();
        }
 
-       FOREACH_THREAD_SAFE (info) {
-               if (sgen_is_thread_in_current_stw (info)) {
+       FOREACH_THREAD (info) {
+               int reason = 0;
+               if (sgen_is_thread_in_current_stw (info, &reason)) {
+                       MonoThreadUnwindState *state;
+
                        THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is suspended\n", mono_thread_info_get_tid (info));
                        g_assert (info->client_info.suspend_done);
-                       update_sgen_info (info);
+
+                       state = mono_thread_info_get_suspend_state (info);
+
+                       info->client_info.ctx = state->ctx;
+
+                       if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN] || !state->unwind_data [MONO_UNWIND_DATA_LMF]) {
+                               /* thread is starting or detaching, nothing to scan here */
+                               info->client_info.stopped_domain = NULL;
+                               info->client_info.stopped_ip = NULL;
+                               info->client_info.stack_start = NULL;
+                       } else {
+                               /* Once we remove the old suspend code, we should move sgen to directly access the state in MonoThread */
+                               info->client_info.stopped_domain = (MonoDomain*) mono_thread_info_tls_get (info, TLS_KEY_DOMAIN);
+                               info->client_info.stopped_ip = (gpointer) (MONO_CONTEXT_GET_IP (&info->client_info.ctx));
+                               info->client_info.stack_start = (gpointer) ((char*)MONO_CONTEXT_GET_SP (&info->client_info.ctx) - REDZONE_SIZE);
+
+                               /* altstack signal handler, sgen can't handle them, mono-threads should have handled this. */
+                               if (!info->client_info.stack_start
+                                        || info->client_info.stack_start < info->client_info.stack_start_limit
+                                        || info->client_info.stack_start >= info->client_info.stack_end) {
+                                       g_error ("BAD STACK: stack_start = %p, stack_start_limit = %p, stack_end = %p",
+                                               info->client_info.stack_start, info->client_info.stack_start_limit, info->client_info.stack_end);
+                               }
+                       }
+
+                       binary_protocol_thread_suspend ((gpointer) mono_thread_info_get_tid (info), info->client_info.stopped_ip);
                } else {
+                       THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is NOT suspended, reason %d\n", mono_thread_info_get_tid (info), reason);
                        g_assert (!info->client_info.suspend_done || info == mono_thread_info_current ());
                }
-       } END_FOREACH_THREAD_SAFE
+       } FOREACH_THREAD_END
 }
 
 static void
 sgen_unified_suspend_restart_world (void)
 {
-       SgenThreadInfo *info;
-
        THREADS_STW_DEBUG ("[GC-STW-END] *** BEGIN RESUME ***\n");
-       FOREACH_THREAD_SAFE (info) {
-               if (sgen_is_thread_in_current_stw (info)) {
+       FOREACH_THREAD (info) {
+               int reason = 0;
+               if (sgen_is_thread_in_current_stw (info, &reason)) {
                        g_assert (mono_thread_info_begin_resume (info));
                        THREADS_STW_DEBUG ("[GC-STW-RESUME-WORLD] RESUME thread %p\n", mono_thread_info_get_tid (info));
+
+                       binary_protocol_thread_restart ((gpointer) mono_thread_info_get_tid (info));
                } else {
-                       THREADS_STW_DEBUG ("[GC-STW-RESUME-WORLD] IGNORE thread %p\n", mono_thread_info_get_tid (info));
+                       THREADS_STW_DEBUG ("[GC-STW-RESUME-WORLD] IGNORE thread %p, reason %d\n", mono_thread_info_get_tid (info), reason);
                }
-       } END_FOREACH_THREAD_SAFE
+       } FOREACH_THREAD_END
 
        mono_threads_wait_pending_operations ();
        mono_threads_end_global_suspend ();