-/*
- * mini-posix.c: POSIX signal handling support for Mono.
+/**
+ * \file
+ * POSIX signal handling support for Mono.
*
* Authors:
* Mono Team (mono-list@lists.ximian.com)
#include <mono/metadata/threads.h>
#include <mono/metadata/appdomain.h>
#include <mono/metadata/debug-helpers.h>
-#include <mono/io-layer/io-layer.h>
-#include "mono/metadata/profiler.h"
#include <mono/metadata/profiler-private.h>
#include <mono/metadata/mono-config.h>
#include <mono/metadata/environment.h>
#include "jit-icalls.h"
-#ifdef PLATFORM_MACOSX
+#ifdef HOST_DARWIN
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <mach/clock.h>
#endif
-#if defined(__native_client__) || defined(HOST_WATCHOS)
+#if defined(HOST_WATCHOS)
void
mono_runtime_setup_stat_profiler (void)
return FALSE;
}
-#ifndef PLATFORM_MACOSX
+#ifndef HOST_DARWIN
void
mono_runtime_install_handlers (void)
{
}
}
-#if defined(__i386__) || defined(__x86_64__)
-#define FULL_STAT_PROFILER_BACKTRACE 1
-#define CURRENT_FRAME_GET_BASE_POINTER(f) (* (gpointer*)(f))
-#define CURRENT_FRAME_GET_RETURN_ADDRESS(f) (* (((gpointer*)(f)) + 1))
-#if MONO_ARCH_STACK_GROWS_UP
-#define IS_BEFORE_ON_STACK <
-#define IS_AFTER_ON_STACK >
-#else
-#define IS_BEFORE_ON_STACK >
-#define IS_AFTER_ON_STACK <
-#endif
-#else
-#define FULL_STAT_PROFILER_BACKTRACE 0
-#endif
-
#if (defined (USE_POSIX_BACKEND) && defined (SIGRTMIN)) || defined (SIGPROF)
#define HAVE_PROFILER_SIGNAL
#endif
#ifdef HAVE_PROFILER_SIGNAL
-static void
-per_thread_profiler_hit (void *ctx)
-{
- int call_chain_depth = mono_profiler_stat_get_call_chain_depth ();
- MonoProfilerCallChainStrategy call_chain_strategy = mono_profiler_stat_get_call_chain_strategy ();
-
- if (call_chain_depth == 0) {
- mono_profiler_stat_hit ((guchar *)mono_arch_ip_from_context (ctx), ctx);
- } else {
- MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
- int current_frame_index = 1;
- MonoContext mono_context;
- guchar *ips [call_chain_depth + 1];
-
- mono_sigctx_to_monoctx (ctx, &mono_context);
- ips [0] = (guchar *)MONO_CONTEXT_GET_IP (&mono_context);
-
- if (jit_tls != NULL) {
- if (call_chain_strategy == MONO_PROFILER_CALL_CHAIN_NATIVE) {
-#if FULL_STAT_PROFILER_BACKTRACE
- guchar *current_frame;
- guchar *stack_bottom;
- guchar *stack_top;
-
- stack_bottom = (guchar *)jit_tls->end_of_stack;
- stack_top = (guchar *)MONO_CONTEXT_GET_SP (&mono_context);
- current_frame = (guchar *)MONO_CONTEXT_GET_BP (&mono_context);
-
- while ((current_frame_index <= call_chain_depth) &&
- (stack_bottom IS_BEFORE_ON_STACK (guchar*) current_frame) &&
- ((guchar*) current_frame IS_BEFORE_ON_STACK stack_top)) {
- ips [current_frame_index] = (guchar *)CURRENT_FRAME_GET_RETURN_ADDRESS (current_frame);
- current_frame_index ++;
- stack_top = current_frame;
- current_frame = (guchar *)CURRENT_FRAME_GET_BASE_POINTER (current_frame);
- }
-#else
- call_chain_strategy = MONO_PROFILER_CALL_CHAIN_GLIBC;
-#endif
- }
-
- if (call_chain_strategy == MONO_PROFILER_CALL_CHAIN_GLIBC) {
-#if GLIBC_PROFILER_BACKTRACE
- current_frame_index = backtrace ((void**) & ips [1], call_chain_depth);
-#else
- call_chain_strategy = MONO_PROFILER_CALL_CHAIN_MANAGED;
-#endif
- }
-
- if (call_chain_strategy == MONO_PROFILER_CALL_CHAIN_MANAGED) {
- MonoDomain *domain = mono_domain_get ();
- if (domain != NULL) {
- MonoLMF *lmf = NULL;
- MonoJitInfo *ji;
- MonoJitInfo res;
- MonoContext new_mono_context;
- int native_offset;
- ji = mono_find_jit_info (domain, jit_tls, &res, NULL, &mono_context,
- &new_mono_context, NULL, &lmf, &native_offset, NULL);
- while ((ji != NULL) && (current_frame_index <= call_chain_depth)) {
- ips [current_frame_index] = (guchar *)MONO_CONTEXT_GET_IP (&new_mono_context);
- current_frame_index ++;
- mono_context = new_mono_context;
- ji = mono_find_jit_info (domain, jit_tls, &res, NULL, &mono_context,
- &new_mono_context, NULL, &lmf, &native_offset, NULL);
- }
- }
- }
- }
-
- mono_profiler_stat_call_chain (current_frame_index, & ips [0], ctx);
- }
-}
-
static MonoNativeThreadId sampling_thread;
static gint32 profiler_signals_sent;
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 (). */
InterlockedIncrement (&profiler_signals_received);
- if (mono_thread_info_get_small_id () == -1)
- return; //an non-attached thread got the signal
+ // Did a non-attached or detaching thread get the signal?
+ if (mono_thread_info_get_small_id () == -1 ||
+ !mono_domain_get () ||
+ !mono_tls_get_jit_tls ()) {
+ errno = old_errno;
+ return;
+ }
- if (!mono_domain_get () || !mono_tls_get_jit_tls ())
- return; //thread in the process of dettaching
+ // See the comment in sampling_thread_func ().
+ InterlockedWrite (&mono_thread_info_current ()->profiler_signal_ack, 1);
InterlockedIncrement (&profiler_signals_accepted);
- hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
+ int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
mono_thread_info_set_is_async_context (TRUE);
- per_thread_profiler_hit (ctx);
+
+ MONO_PROFILER_RAISE (sample_hit, (mono_arch_ip_from_context (ctx), ctx));
+
mono_thread_info_set_is_async_context (FALSE);
mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
+
errno = old_errno;
mono_chain_signal (MONO_SIG_HANDLER_PARAMS);
#ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
/*Apple likes to deliver SIGBUS for *0 */
-#ifdef PLATFORM_MACOSX
+#ifdef HOST_DARWIN
if (signo == SIGSEGV || signo == SIGBUS) {
#else
if (signo == SIGSEGV) {
add_signal_handler (SIGSEGV, mono_sigsegv_signal_handler, 0);
}
-#ifndef PLATFORM_MACOSX
+#ifndef HOST_DARWIN
void
mono_runtime_install_handlers (void)
{
static volatile gint32 sampling_thread_running;
-#ifdef PLATFORM_MACOSX
+#ifdef HOST_DARWIN
static clock_serv_t sampling_clock_service;
static void
-clock_init (void)
+clock_init (MonoProfilerSampleMode mode)
{
kern_return_t ret;
clockid_t sampling_posix_clock;
static void
-clock_init (void)
+clock_init (MonoProfilerSampleMode mode)
{
- switch (mono_profiler_get_sampling_mode ()) {
- case MONO_PROFILER_STAT_MODE_PROCESS:
+ switch (mode) {
+ case MONO_PROFILER_SAMPLE_MODE_PROCESS: {
+ /*
+ * If we don't have clock_nanosleep (), measuring the process time
+ * makes very little sense as we can only use nanosleep () to sleep on
+ * real time.
+ */
#ifdef HAVE_CLOCK_NANOSLEEP
+ struct timespec ts = { 0 };
+
/*
- * If we don't have clock_nanosleep (), measuring the process time
- * makes very little sense as we can only use nanosleep () to sleep on
- * real time.
+ * Some systems (e.g. Windows Subsystem for Linux) declare the
+ * CLOCK_PROCESS_CPUTIME_ID clock but don't actually support it. For
+ * those systems, we fall back to CLOCK_MONOTONIC if we get EINVAL.
*/
- sampling_posix_clock = CLOCK_PROCESS_CPUTIME_ID;
- break;
+ if (clock_nanosleep (CLOCK_PROCESS_CPUTIME_ID, TIMER_ABSTIME, &ts, NULL) != EINVAL) {
+ sampling_posix_clock = CLOCK_PROCESS_CPUTIME_ID;
+ break;
+ }
#endif
- case MONO_PROFILER_STAT_MODE_REAL: sampling_posix_clock = CLOCK_MONOTONIC; break;
+
+ // fallthrough
+ }
+ case MONO_PROFILER_SAMPLE_MODE_REAL: sampling_posix_clock = CLOCK_MONOTONIC; break;
default: g_assert_not_reached (); break;
}
}
mono_threads_attach_tools_thread ();
mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler sampler");
- gint64 rate = 1000000000 / mono_profiler_get_sampling_rate ();
-
int old_policy;
struct sched_param old_sched;
pthread_getschedparam (pthread_self (), &old_policy, &old_sched);
struct sched_param sched = { .sched_priority = sched_get_priority_max (SCHED_FIFO) };
pthread_setschedparam (pthread_self (), SCHED_FIFO, &sched);
- clock_init ();
+ MonoProfilerSampleMode mode;
- guint64 sleep = clock_get_time_ns ();
+init:
+ mono_profiler_get_sample_mode (NULL, &mode, NULL);
- while (InterlockedRead (&sampling_thread_running)) {
- sleep += rate;
+ if (mode == MONO_PROFILER_SAMPLE_MODE_NONE) {
+ mono_profiler_sampling_thread_wait ();
+
+ if (!InterlockedRead (&sampling_thread_running))
+ goto done;
+
+ goto init;
+ }
+
+ clock_init (mode);
+
+ for (guint64 sleep = clock_get_time_ns (); InterlockedRead (&sampling_thread_running); clock_sleep_ns_abs (sleep)) {
+ uint32_t freq;
+ MonoProfilerSampleMode new_mode;
+
+ mono_profiler_get_sample_mode (NULL, &new_mode, &freq);
+
+ if (new_mode != mode) {
+ clock_cleanup ();
+ goto init;
+ }
+
+ sleep += 1000000000 / freq;
FOREACH_THREAD_SAFE (info) {
/* info should never be this thread as we're a tools thread. */
g_assert (mono_thread_info_get_tid (info) != mono_native_thread_id_get ());
+ /*
+ * Require an ack for the last sampling signal sent to the thread
+ * so that we don't overflow the signal queue, leading to all sorts
+ * of problems (e.g. GC STW failing).
+ */
+ if (profiler_signal != SIGPROF && !InterlockedCompareExchange (&info->profiler_signal_ack, 0, 1))
+ continue;
+
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 ();
+done:
+ InterlockedWrite (&sampling_thread_exiting, 1);
+
pthread_setschedparam (pthread_self (), old_policy, &old_sched);
mono_thread_info_detach ();
{
InterlockedWrite (&sampling_thread_running, 0);
-#ifndef PLATFORM_MACOSX
+ mono_profiler_sampling_thread_post ();
+
+#ifndef HOST_DARWIN
/*
* 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
* 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) && !defined (PLATFORM_ANDROID)
+#if defined (USE_POSIX_BACKEND) && defined (SIGRTMIN) && !defined (HOST_ANDROID)
/* Just take the first real-time signal we can get. */
profiler_signal = mono_threads_suspend_search_alternative_signal ();
#else
#endif
-#endif /* defined(__native_client__) || defined(HOST_WATCHOS) */
-
-#if defined(__native_client__)
-
-void
-mono_gdb_render_native_backtraces (pid_t crashed_pid)
-{
-}
-
-#else
-
-pid_t
-mono_runtime_syscall_fork ()
-{
-#if defined(PLATFORM_ANDROID)
- /* SYS_fork is defined to be __NR_fork which is not defined in some ndk versions */
- g_assert_not_reached ();
- return 0;
-#elif defined(SYS_fork)
- return (pid_t) syscall (SYS_fork);
-#elif defined(PLATFORM_MACOSX) && HAVE_FORK
- return (pid_t) fork ();
-#else
- g_assert_not_reached ();
- return 0;
-#endif
-}
+#endif /* defined(HOST_WATCHOS) */
static gboolean
native_stack_with_gdb (pid_t crashed_pid, const char **argv, FILE *commands, char* commands_filename)
commands = fopen (commands_filename, "w");
if (!commands) {
- unlink (commands_filename);
+ unlink (commands_filename);
return;
}
memset (argv, 0, sizeof (char*) * 10);
-#if defined(PLATFORM_MACOSX)
+#if defined(HOST_DARWIN)
if (native_stack_with_lldb (crashed_pid, argv, commands, commands_filename))
goto exec;
#endif
if (native_stack_with_gdb (crashed_pid, argv, commands, commands_filename))
goto exec;
-#if !defined(PLATFORM_MACOSX)
+#if !defined(HOST_DARWIN)
if (native_stack_with_lldb (crashed_pid, argv, commands, commands_filename))
goto exec;
#endif
return;
exec:
+ fclose (commands);
execv (argv [0], (char**)argv);
_exit (-1);
#endif // HAVE_EXECV
}
-#endif /* defined(__native_client__) */
-
#if !defined (__MACH__)
gboolean