Merge pull request #3040 from xmcclure/debugger-step-recursive
[mono.git] / mono / mini / mini-posix.c
index d5787acf13b6dff1e2468d5002302a1aca978a16..6f6e266a3b025d72fd9998435b46a8b053747b31 100644 (file)
@@ -107,7 +107,8 @@ mono_runtime_install_handlers (void)
 void
 mono_runtime_posix_install_handlers(void)
 {
-
+       /* we still need to ignore SIGPIPE */
+       signal (SIGPIPE, SIG_IGN);
 }
 
 void
@@ -322,18 +323,41 @@ per_thread_profiler_hit (void *ctx)
        }
 }
 
+static MonoNativeThreadId sampling_thread;
+
+static gint32 profiler_signals_sent;
+static gint32 profiler_signals_received;
+static gint32 profiler_signals_accepted;
+static gint32 profiler_interrupt_signals_received;
+
 MONO_SIG_HANDLER_FUNC (static, profiler_signal_handler)
 {
        int old_errno = errno;
        int hp_save_index;
        MONO_SIG_HANDLER_GET_CONTEXT;
 
+       /* See the comment in mono_runtime_shutdown_stat_profiler (). */
+       if (mono_native_thread_id_get () == sampling_thread) {
+#ifdef HAVE_CLOCK_NANOSLEEP
+               if (mono_profiler_get_sampling_mode () == MONO_PROFILER_STAT_MODE_PROCESS) {
+                       InterlockedIncrement (&profiler_interrupt_signals_received);
+                       return;
+               }
+#endif
+
+               g_error ("%s: Unexpected profiler signal received by the sampler thread", __func__);
+       }
+
+       InterlockedIncrement (&profiler_signals_received);
+
        if (mono_thread_info_get_small_id () == -1)
                return; //an non-attached thread got the signal
 
        if (!mono_domain_get () || !mono_native_tls_get_value (mono_jit_tls_id))
                return; //thread in the process of dettaching
 
+       InterlockedIncrement (&profiler_signals_accepted);
+
        hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
 
        mono_thread_info_set_is_async_context (TRUE);
@@ -508,6 +532,8 @@ mono_runtime_cleanup_handlers (void)
 
 #ifdef HAVE_PROFILER_SIGNAL
 
+static volatile gint32 sampling_thread_running;
+
 #ifdef PLATFORM_MACOSX
 
 static clock_serv_t sampling_clock_service;
@@ -517,7 +543,11 @@ clock_init (void)
 {
        kern_return_t ret;
 
-       if ((ret = host_get_clock_service (mach_host_self (), SYSTEM_CLOCK, &sampling_clock_service)) != KERN_SUCCESS)
+       do {
+               ret = host_get_clock_service (mach_host_self (), SYSTEM_CLOCK, &sampling_clock_service);
+       } while (ret == KERN_ABORTED);
+
+       if (ret != KERN_SUCCESS)
                g_error ("%s: host_get_clock_service () returned %d", __func__, ret);
 }
 
@@ -526,7 +556,11 @@ clock_cleanup (void)
 {
        kern_return_t ret;
 
-       if ((ret = mach_port_deallocate (mach_task_self (), sampling_clock_service)) != KERN_SUCCESS)
+       do {
+               ret = mach_port_deallocate (mach_task_self (), sampling_clock_service);
+       } while (ret == KERN_ABORTED);
+
+       if (ret != KERN_SUCCESS)
                g_error ("%s: mach_port_deallocate () returned %d", __func__, ret);
 }
 
@@ -536,7 +570,11 @@ clock_get_time_ns (void)
        kern_return_t ret;
        mach_timespec_t mach_ts;
 
-       if ((ret = clock_get_time (sampling_clock_service, &mach_ts)) != KERN_SUCCESS)
+       do {
+               ret = clock_get_time (sampling_clock_service, &mach_ts);
+       } while (ret == KERN_ABORTED);
+
+       if (ret != KERN_SUCCESS)
                g_error ("%s: clock_get_time () returned %d", __func__, ret);
 
        return ((guint64) mach_ts.tv_sec * 1000000000) + (guint64) mach_ts.tv_nsec;
@@ -556,7 +594,7 @@ clock_sleep_ns_abs (guint64 ns_abs)
 
                if (ret != KERN_SUCCESS && ret != KERN_ABORTED)
                        g_error ("%s: clock_sleep () returned %d", __func__, ret);
-       } while (ret == KERN_ABORTED);
+       } while (ret == KERN_ABORTED && InterlockedRead (&sampling_thread_running));
 }
 
 #else
@@ -613,7 +651,7 @@ clock_sleep_ns_abs (guint64 ns_abs)
 
                if (ret != 0 && ret != EINTR)
                        g_error ("%s: clock_nanosleep () returned %d", __func__, ret);
-       } while (ret == EINTR);
+       } while (ret == EINTR && InterlockedRead (&sampling_thread_running));
 #else
        int ret;
        gint64 diff;
@@ -654,21 +692,20 @@ clock_sleep_ns_abs (guint64 ns_abs)
 
                if ((ret = nanosleep (&req, NULL)) == -1 && errno != EINTR)
                        g_error ("%s: nanosleep () returned -1, errno = %d", __func__, errno);
-       } while (ret == -1);
+       } while (ret == -1 && InterlockedRead (&sampling_thread_running));
 #endif
 }
 
 #endif
 
 static int profiler_signal;
-static MonoNativeThreadId sampling_thread;
-static volatile gint32 sampling_thread_running;
+static volatile gint32 sampling_thread_exiting;
 
 static mono_native_thread_return_t
 sampling_thread_func (void *data)
 {
        mono_threads_attach_tools_thread ();
-       mono_thread_info_set_name (mono_native_thread_id_get (), "Profiler sampler");
+       mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler sampler");
 
        gint64 rate = 1000000000 / mono_profiler_get_sampling_rate ();
 
@@ -705,11 +742,14 @@ sampling_thread_func (void *data)
                        g_assert (mono_thread_info_get_tid (info) != mono_native_thread_id_get ());
 
                        mono_threads_pthread_kill (info, profiler_signal);
+                       InterlockedIncrement (&profiler_signals_sent);
                } FOREACH_THREAD_SAFE_END
 
                clock_sleep_ns_abs (sleep);
        }
 
+       InterlockedWrite (&sampling_thread_exiting, 1);
+
        clock_cleanup ();
 
        pthread_setschedparam (pthread_self (), old_policy, &old_sched);
@@ -723,6 +763,41 @@ void
 mono_runtime_shutdown_stat_profiler (void)
 {
        InterlockedWrite (&sampling_thread_running, 0);
+
+#ifdef HAVE_CLOCK_NANOSLEEP
+       /*
+        * There is a slight problem when we're using CLOCK_PROCESS_CPUTIME_ID: If
+        * we're shutting down and there's largely no activity in the process other
+        * than waiting for the sampler thread to shut down, it can take upwards of
+        * 20 seconds (depending on a lot of factors) for us to shut down because
+        * the sleep progresses very slowly as a result of the low CPU activity.
+        *
+        * We fix this by repeatedly sending the profiler signal to the sampler
+        * thread in order to interrupt the sleep. clock_sleep_ns_abs () will check
+        * sampling_thread_running upon an interrupt and return immediately if it's
+        * zero. profiler_signal_handler () has a special case to ignore the signal
+        * for the sampler thread.
+        *
+        * We do not need to do this on platforms where we use a regular sleep
+        * based on a monotonic clock. The sleep will return in a reasonable amount
+        * of time in those cases.
+        */
+       if (mono_profiler_get_sampling_mode () == MONO_PROFILER_STAT_MODE_PROCESS) {
+               MonoThreadInfo *info;
+
+               // Did it shut down already?
+               if ((info = mono_thread_info_lookup (sampling_thread))) {
+                       while (!InterlockedRead (&sampling_thread_exiting)) {
+                               mono_threads_pthread_kill (info, profiler_signal);
+                               mono_thread_info_usleep (10 * 1000 /* 10ms */);
+                       }
+
+                       // Make sure info can be freed.
+                       mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
+               }
+       }
+#endif
+
        pthread_join (sampling_thread, NULL);
 
        /*
@@ -742,12 +817,16 @@ mono_runtime_setup_stat_profiler (void)
         * tends to result in awful delivery rates when the application is heavily
         * loaded.
         *
+        * We avoid real-time signals on Android as they're super broken in certain
+        * API levels (too small sigset_t, nonsensical SIGRTMIN/SIGRTMAX values,
+        * etc).
+        *
         * TODO: On Mac, we should explore using the Mach thread suspend/resume
         * functions and doing the stack walk from the sampling thread. This would
         * get us a 100% sampling rate. However, this may interfere with the GC's
         * STW logic. Could perhaps be solved by taking the suspend lock.
         */
-#if defined (USE_POSIX_BACKEND) && defined (SIGRTMIN)
+#if defined (USE_POSIX_BACKEND) && defined (SIGRTMIN) && !defined (PLATFORM_ANDROID)
        /* Just take the first real-time signal we can get. */
        profiler_signal = mono_threads_posix_signal_search_alternative (-1);
 #else
@@ -756,6 +835,11 @@ mono_runtime_setup_stat_profiler (void)
 
        add_signal_handler (profiler_signal, profiler_signal_handler, SA_RESTART);
 
+       mono_counters_register ("Sampling signals sent", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &profiler_signals_sent);
+       mono_counters_register ("Sampling signals received", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &profiler_signals_received);
+       mono_counters_register ("Sampling signals accepted", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &profiler_signals_accepted);
+       mono_counters_register ("Shutdown signals received", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &profiler_interrupt_signals_received);
+
        InterlockedWrite (&sampling_thread_running, 1);
        mono_native_thread_create (&sampling_thread, sampling_thread_func, NULL);
 }