From: Neale Ferguson Date: Fri, 24 Jun 2016 16:47:01 +0000 (-0400) Subject: Enhanced logging - attempt 2.5 (#3191) X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=e70a8aca1cd575582459db406ab3410e53074b73;p=mono.git Enhanced logging - attempt 2.5 (#3191) * Revert "Revert "Enhance log tracing"" This reverts commit 565c6ac0802eedf68755bd5776c80b00ab388415. * Better handle legacy log handler processing with suggestions from lamdageek * Remove unnecessary assignment * Restore mono_trace_set_log_handler parameters to their original specification --- diff --git a/man/mono.1 b/man/mono.1 index e2eff1620e7..7bd449dd455 100644 --- a/man/mono.1 +++ b/man/mono.1 @@ -1732,6 +1732,16 @@ messages for a certain component. You can use multiple masks by comma separating them. For example to see config file messages and assembly loader messages set you mask to "asm,cfg". .TP +\fBMONO_LOG_DEST\fR +Controls where trace log messages are written. If not set then the messages go to stdout. +If set, the string either specifies a path to a file that will have messages appended to +it, or the string "syslog" in which case the messages will be written to the system log. +Under Windows, this is simulated by writing to a file called "mono.log". +\fBMONO_LOG_HEADER\fR +Controls whether trace log messages not directed to syslog have the id, timestamp, and +pid as the prefix to the log message. To enable a header this environment variable need +just be non-null. +.TP \fBMONO_TRACE\fR Used for runtime tracing of method calls. The format of the comma separated trace options is: diff --git a/mono/sgen/sgen-gc.h b/mono/sgen/sgen-gc.h index 21268a9f8ff..b0ac3dbcb10 100644 --- a/mono/sgen/sgen-gc.h +++ b/mono/sgen/sgen-gc.h @@ -116,16 +116,40 @@ extern guint64 stat_objects_copied_major; g_error (__VA_ARGS__); \ } } while (0) +#ifndef HOST_WIN32 +# define LOG_TIMESTAMP \ + do { \ + time_t t; \ + struct tm tod; \ + time(&t); \ + localtime_r(&t, &tod); \ + strftime(logTime, sizeof(logTime), "%Y-%m-%d %H:%M:%S", &tod); \ + } while (0) +#else +# define LOG_TIMESTAMP \ + do { \ + time_t t; \ + struct tm *tod; \ + time(&t); \ + tod = localtime(&t); \ + strftime(logTime, sizeof(logTime), "%F %T", tod); \ + } while (0) +#endif #define SGEN_LOG(level, format, ...) do { \ if (G_UNLIKELY ((level) <= SGEN_MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) { \ - mono_gc_printf (gc_debug_file, format "\n", ##__VA_ARGS__); \ + char logTime[80]; \ + LOG_TIMESTAMP; \ + mono_gc_printf (gc_debug_file, "%s " format "\n", logTime, ##__VA_ARGS__); \ } } while (0) #define SGEN_COND_LOG(level, cond, format, ...) do { \ - if (G_UNLIKELY ((level) <= SGEN_MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) { \ - if (cond) \ - mono_gc_printf (gc_debug_file, format "\n", ##__VA_ARGS__); \ + if (G_UNLIKELY ((level) <= SGEN_MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) { \ + if (cond) { \ + char logTime[80]; \ + LOG_TIMESTAMP; \ + mono_gc_printf (gc_debug_file, "%s " format "\n", logTime, ##__VA_ARGS__); \ + } \ } } while (0) extern int gc_debug_level; diff --git a/mono/utils/Makefile.am b/mono/utils/Makefile.am index b62633307b4..02d447ad511 100644 --- a/mono/utils/Makefile.am +++ b/mono/utils/Makefile.am @@ -27,6 +27,9 @@ monoutils_sources = \ mono-dl-darwin.c \ mono-dl-posix.c \ mono-dl.h \ + mono-log-windows.c \ + mono-log-common.c \ + mono-log-posix.c \ mono-internal-hash.c \ mono-internal-hash.h \ mono-io-portability.c \ diff --git a/mono/utils/mono-log-common.c b/mono/utils/mono-log-common.c new file mode 100644 index 00000000000..fa11aabcb7b --- /dev/null +++ b/mono/utils/mono-log-common.c @@ -0,0 +1,155 @@ +/* + * mono-log-common.c: Platform-independent interface to the logger + * + * This module contains the POSIX syslog logger interface + * + * Author: + * Neale Ferguson + * + */ +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#ifndef HOST_WIN32 +#include +#else +#include +#endif +#include "mono-logger.h" + +static FILE *logFile = NULL; +static void *logUserData = NULL; + +/** + * mapSyslogLevel: + * + * @level - GLogLevelFlags value + * @returns The equivalent character identifier + */ +static inline char +mapLogFileLevel(GLogLevelFlags level) +{ + if (level & G_LOG_LEVEL_ERROR) + return ('E'); + if (level & G_LOG_LEVEL_CRITICAL) + return ('C'); + if (level & G_LOG_LEVEL_WARNING) + return ('W'); + if (level & G_LOG_LEVEL_MESSAGE) + return ('N'); + if (level & G_LOG_LEVEL_INFO) + return ('I'); + if (level & G_LOG_LEVEL_DEBUG) + return ('D'); + return ('I'); +} + +/** + * mono_log_open_logfile + * + * Open the logfile. If the path is not specified default to stdout. If the + * open fails issue a warning and use stdout as the log file destination. + * + * @path - Path for log file + * @userData - Not used + */ +void +mono_log_open_logfile(const char *path, void *userData) +{ + if (path == NULL) { + logFile = stdout; + } else { +#ifndef HOST_WIN32 + logFile = fopen(path, "w"); +#else + gunichar2 *wPath = g_utf8_to_utf16(path, -1, 0, 0, 0); + if (wPath != NULL) { + logFile = _wfopen((wchar_t *) wPath, L"w"); + g_free (wPath); + } +#endif + if (logFile == NULL) { + g_warning("opening of log file %s failed with %s - defaulting to stdout", + path, strerror(errno)); + logFile = stdout; + } + } + logUserData = userData; +} + +/** + * mono_log_write_logfile + * + * Write data to the log file. + * + * @domain - Identifier string + * @level - Logging level flags + * @format - Printf format string + * @vargs - Variable argument list + */ +void +mono_log_write_logfile(const char *domain, GLogLevelFlags level, mono_bool hdr, const char *format, va_list args) +{ + time_t t; + char logTime[80], + logMessage[512]; + pid_t pid; + int iLog = 0; + size_t nLog; + + if (logFile == NULL) + logFile = stdout; + + if (hdr) { +#ifndef HOST_WIN32 + struct tm tod; + time(&t); + localtime_r(&t, &tod); + pid = getpid(); + strftime(logTime, sizeof(logTime), "%Y-%m-%d %H:%M:%S", &tod); +#else + struct tm *tod; + time(&t); + tod = localtime(&t); + pid = _getpid(); + strftime(logTime, sizeof(logTime), "%F %T", tod); +#endif + iLog = sprintf(logMessage, "%s level[%c] mono[%d]: ", + logTime,mapLogFileLevel(level),pid); + } + nLog = sizeof(logMessage) - iLog - 2; + vsnprintf(logMessage+iLog, nLog, format, args); + iLog = strlen(logMessage); + logMessage[iLog++] = '\n'; + logMessage[iLog++] = '\0'; + fputs(logMessage, logFile); + fflush(logFile); + + if (level == G_LOG_FLAG_FATAL) + abort(); +} + +/** + * mono_log_close_logfile + * + * Close the log file + */ +void +mono_log_close_logfile() +{ + if (logFile) { + if (logFile != stdout) + fclose(logFile); + logFile = NULL; + } +} diff --git a/mono/utils/mono-log-posix.c b/mono/utils/mono-log-posix.c new file mode 100644 index 00000000000..1ce111c1762 --- /dev/null +++ b/mono/utils/mono-log-posix.c @@ -0,0 +1,101 @@ +/* + * mono-log-posix.c: POSIX interface to the logger + * + * This module contains the POSIX syslog logger routines + * + * Author: + * Neale Ferguson + * + */ +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#if defined(_POSIX_VERSION) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mono-logger.h" + +static void *logUserData = NULL; + +/** + * mapSyslogLevel: + * + * @level - GLogLevelFlags value + * @returns The equivalent syslog priority value + */ +static __inline__ int +mapSyslogLevel(GLogLevelFlags level) +{ + if (level & G_LOG_LEVEL_ERROR) + return (LOG_ERR); + if (level & G_LOG_LEVEL_CRITICAL) + return (LOG_CRIT); + if (level & G_LOG_LEVEL_WARNING) + return (LOG_WARNING); + if (level & G_LOG_LEVEL_MESSAGE) + return (LOG_NOTICE); + if (level & G_LOG_LEVEL_INFO) + return (LOG_INFO); + if (level & G_LOG_LEVEL_DEBUG) + return (LOG_DEBUG); + return (LOG_INFO); +} + +/** + * mono_log_open_logfile + * + * Open the syslog interface specifying that we want our PID recorded + * and that we're using the LOG_USER facility. + * + * @ident - Identifier: ignored + * @userData - Not used + */ +void +mono_log_open_syslog(const char *ident, void *userData) +{ + openlog("mono", LOG_PID, LOG_USER); + logUserData = userData; +} + +/** + * mono_log_write_logfile + * + * Write data to the log file. + * + * @domain - Identifier string + * @level - Logging level flags + * @format - Printf format string + * @vargs - Variable argument list + */ +void +mono_log_write_syslog(const char *domain, GLogLevelFlags level, mono_bool hdr, const char *format, va_list args) +{ + vsyslog(mapSyslogLevel(level), format, args); + + if (level == G_LOG_FLAG_FATAL) + abort(); +} + +/** + * mono_log_close_logfile + * + * Close the log file + */ +void +mono_log_close_syslog() +{ + closelog(); +} +#endif diff --git a/mono/utils/mono-log-windows.c b/mono/utils/mono-log-windows.c new file mode 100644 index 00000000000..1746128a938 --- /dev/null +++ b/mono/utils/mono-log-windows.c @@ -0,0 +1,132 @@ +/* + * mono-log-windows.c: Simplistic simulation of a syslog logger for Windows + * + * This module contains the Windows syslog logger interface + * + * Author: + * Neale Ferguson + * + */ +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HOST_WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include "mono-logger.h" + +static FILE *logFile = NULL; +static void *logUserData = NULL; +static wchar_t *logFileName = L".//mono.log"; + +/** + * mapSyslogLevel: + * + * @level - GLogLevelFlags value + * @returns The equivalent character identifier + */ +static inline char +mapLogFileLevel(GLogLevelFlags level) +{ + if (level & G_LOG_LEVEL_ERROR) + return ('E'); + if (level & G_LOG_LEVEL_CRITICAL) + return ('C'); + if (level & G_LOG_LEVEL_WARNING) + return ('W'); + if (level & G_LOG_LEVEL_MESSAGE) + return ('N'); + if (level & G_LOG_LEVEL_INFO) + return ('I'); + if (level & G_LOG_LEVEL_DEBUG) + return ('D'); + return ('I'); +} + +/** + * mono_log_open_syslog + * + * Open the syslog file. If the open fails issue a warning and + * use stdout as the log file destination. + * + * @ident - Identifier: ignored + * @userData - Not used + */ +void +mono_log_open_syslog(const char *ident, void *userData) +{ + logFile = _wfopen(logFileName, L"w"); + if (logFile == NULL) { + g_warning("opening of log file %s failed with %s", + strerror(errno)); + logFile = stdout; + } + logUserData = userData; +} + +/** + * mono_log_write_syslog + * + * Write data to the syslog file. + * + * @domain - Identifier string + * @level - Logging level flags + * @format - Printf format string + * @vargs - Variable argument list + */ +void +mono_log_write_syslog(const char *domain, GLogLevelFlags level, mono_bool hdr, const char *format, va_list args) +{ + time_t t; + struct tm *tod; + char logTime[80], + logMessage[512]; + pid_t pid; + int iLog = 0; + size_t nLog; + + if (logFile == NULL) + mono_log_open_syslog(NULL, NULL); + + time(&t); + tod = localtime(&t); + pid = _getpid(); + strftime(logTime, sizeof(logTime), "%Y-%m-%d %H:%M:%S", tod); + iLog = sprintf(logMessage, "%s level[%c] mono[%d]: ", + logTime,mapLogFileLevel(level),pid); + nLog = sizeof(logMessage) - iLog - 2; + vsnprintf(logMessage+iLog, nLog, format, args); + iLog = strlen(logMessage); + logMessage[iLog++] = '\n'; + logMessage[iLog++] = 0; + fputs(logMessage, logFile); + fflush(logFile); + + if (level == G_LOG_FLAG_FATAL) + abort(); +} + +/** + * mono_log_close_syslog + * + * Close the syslog file + */ +void +mono_log_close_syslog() +{ + if (logFile) { + fclose(logFile); + logFile = NULL; + } +} +#endif diff --git a/mono/utils/mono-logger.c b/mono/utils/mono-logger.c index 7d0c9ce12a4..8a5840a1c47 100644 --- a/mono/utils/mono-logger.c +++ b/mono/utils/mono-logger.c @@ -11,13 +11,26 @@ typedef struct { MonoTraceMask mask; } MonoLogLevelEntry; -GLogLevelFlags mono_internal_current_level = INT_MAX; -MonoTraceMask mono_internal_current_mask = MONO_TRACE_ALL; +GLogLevelFlags mono_internal_current_level = INT_MAX; +MonoTraceMask mono_internal_current_mask = MONO_TRACE_ALL; +gboolean mono_trace_log_header = FALSE; static GQueue *level_stack = NULL; static const char *mono_log_domain = "Mono"; static MonoPrintCallback print_callback, printerr_callback; +static MonoLogCallParm logCallback = { + .opener = NULL, + .writer = NULL, + .closer = NULL, + .header = FALSE +}; + +typedef struct { + MonoLogCallback legacy_callback; + gpointer user_data; +} legacyLoggerUserData; + /** * mono_trace_init: * @@ -32,6 +45,8 @@ mono_trace_init (void) mono_trace_set_mask_string(g_getenv("MONO_LOG_MASK")); mono_trace_set_level_string(g_getenv("MONO_LOG_LEVEL")); + mono_trace_set_logheader_string(g_getenv("MONO_LOG_HEADER")); + mono_trace_set_logdest_string(g_getenv("MONO_LOG_DEST")); } } @@ -48,6 +63,7 @@ mono_trace_cleanup (void) g_free (g_queue_pop_head (level_stack)); } + logCallback.closer(); g_queue_free (level_stack); level_stack = NULL; } @@ -71,7 +87,13 @@ mono_tracev_inner (GLogLevelFlags level, MonoTraceMask mask, const char *format, return; } - g_logv (mono_log_domain, level, format, args); + if (logCallback.opener == NULL) { + logCallback.opener = mono_log_open_logfile; + logCallback.writer = mono_log_write_logfile; + logCallback.closer = mono_log_close_logfile; + logCallback.opener(NULL, NULL); + } + logCallback.writer(mono_log_domain, level, logCallback.header, format, args); } /** @@ -107,7 +129,55 @@ mono_trace_set_mask (MonoTraceMask mask) if(level_stack == NULL) mono_trace_init(); - mono_internal_current_mask = mask; + mono_internal_current_mask = mask; +} + +/** + * mono_trace_set_logdest: + * + * @dest: Destination for logging + * + * Sets the current logging destination. This can be a file or, if supported, + * syslog. + */ +void +mono_trace_set_logdest_string (const char *dest) +{ + MonoLogCallParm logger; + + if(level_stack == NULL) + mono_trace_init(); + + if ((dest != NULL) && (strcmp("syslog", dest) != 0)) { + logger.opener = mono_log_open_logfile; + logger.writer = mono_log_write_logfile; + logger.closer = mono_log_close_logfile; + logger.dest = (char *) dest; + mono_trace_set_log_handler_internal(&logger, NULL); + } else { + logger.opener = mono_log_open_syslog; + logger.writer = mono_log_write_syslog; + logger.closer = mono_log_close_syslog; + logger.dest = (char *) dest; + mono_trace_set_log_handler_internal(&logger, NULL); + } +} + +/** + * mono_trace_set_logheader: + * + * @head: Whether we want pid/date/time header on log messages + * + * Sets the current logging header option. + */ +void +mono_trace_set_logheader_string(const char *head) +{ + if (head == NULL) { + mono_trace_log_header = FALSE; + } else { + mono_trace_log_header = TRUE; + } } /** @@ -236,30 +306,105 @@ mono_trace_is_traced (GLogLevelFlags level, MonoTraceMask mask) return (level <= mono_internal_current_level && mask & mono_internal_current_mask); } -static MonoLogCallback log_callback; - -static const char* +/** + * log_level_get_name + * @log_level severity level + * + * Convert log level into a string for legacy log handlers + */ +static const char * log_level_get_name (GLogLevelFlags log_level) { - switch (log_level & G_LOG_LEVEL_MASK) { - case G_LOG_LEVEL_ERROR: return "error"; - case G_LOG_LEVEL_CRITICAL: return "critical"; - case G_LOG_LEVEL_WARNING: return "warning"; - case G_LOG_LEVEL_MESSAGE: return "message"; - case G_LOG_LEVEL_INFO: return "info"; - case G_LOG_LEVEL_DEBUG: return "debug"; - default: return "unknown"; - } + switch (log_level & G_LOG_LEVEL_MASK) { + case G_LOG_LEVEL_ERROR: return "error"; + case G_LOG_LEVEL_CRITICAL: return "critical"; + case G_LOG_LEVEL_WARNING: return "warning"; + case G_LOG_LEVEL_MESSAGE: return "message"; + case G_LOG_LEVEL_INFO: return "info"; + case G_LOG_LEVEL_DEBUG: return "debug"; + default: return "unknown"; + } } +/** + * callback_adapter + * + * @log_domain Message prefix + * @log_level Severity + * @message Message to be written + * @fatal Fatal flag - write then abort + * @user_data Argument passed to @callback + * + * This adapts the old callback writer exposed by MonoCallback to the newer method of + * logging. We ignore the header request as legacy handlers never had headers. + */ +static void +callback_adapter(const char *domain, GLogLevelFlags level, mono_bool fatal, const char *fmt, va_list args) +{ + legacyLoggerUserData *ll = (legacyLoggerUserData *) logCallback.user_data; + const char *msg = g_strdup_vprintf (fmt, args); + + ll->legacy_callback (domain, log_level_get_name(level), msg, fatal, ll->user_data); + g_free ((void *) msg); +} + +/** + * legacy_opener + * + * Dummy routine for older style loggers + */ static void -log_adapter (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) +legacy_opener(const char *path, void *user_data) { - log_callback (log_domain, log_level_get_name (log_level), message, log_level & G_LOG_LEVEL_ERROR, user_data); + /* nothing to do */ } /** - * mono_trace_set_log_handler: + * legacy_closer + * + * Cleanup routine for older style loggers + */ +static void +legacy_closer() +{ + if (logCallback.user_data != NULL) { + g_free (logCallback.user_data); /* This is a LegacyLoggerUserData struct */ + logCallback.opener = NULL; + logCallback.writer = NULL; + logCallback.closer = NULL; + logCallback.user_data = NULL; + logCallback.header = FALSE; + } +} + +/** + * mono_trace_set_log_handler: + * + * @callback The callback that will replace the default logging handler + * @user_data Argument passed to @callback + * + * The log handler replaces the default runtime logger. All logging requests with be routed to it. + * If the fatal argument in the callback is true, the callback must abort the current process. The runtime expects that + * execution will not resume after a fatal error. This is for "old-style" or legacy log handers. + */ +void +mono_trace_set_log_handler (MonoLogCallback callback, void *user_data) +{ + g_assert (callback); + if (logCallback.closer != NULL) + logCallback.closer(); + legacyLoggerUserData *ll = g_malloc (sizeof (legacyLoggerUserData)); + ll->legacy_callback = callback; + ll->user_data = user_data; + logCallback.opener = legacy_opener; + logCallback.writer = callback_adapter; + logCallback.closer = legacy_closer; + logCallback.user_data = ll; + logCallback.dest = NULL; +} + +/** + * mono_trace_set_log_handler_internal: * * @callback The callback that will replace the default logging handler * @user_data Argument passed to @callback @@ -269,11 +414,17 @@ log_adapter (const gchar *log_domain, GLogLevelFlags log_level, const gchar *mes * execution will not resume after a fatal error. */ void -mono_trace_set_log_handler (MonoLogCallback callback, void *user_data) +mono_trace_set_log_handler_internal (MonoLogCallParm *callback, void *user_data) { g_assert (callback); - log_callback = callback; - g_log_set_default_handler (log_adapter, user_data); + if (logCallback.closer != NULL) + logCallback.closer(); + logCallback.opener = callback->opener; + logCallback.writer = callback->writer; + logCallback.closer = callback->closer; + logCallback.header = mono_trace_log_header; + logCallback.dest = callback->dest; + logCallback.opener(logCallback.dest, user_data); } static void diff --git a/mono/utils/mono-logger.h b/mono/utils/mono-logger.h index 36f7714f2ed..073daf1c87a 100644 --- a/mono/utils/mono-logger.h +++ b/mono/utils/mono-logger.h @@ -10,8 +10,31 @@ mono_trace_set_level_string (const char *value); MONO_API void mono_trace_set_mask_string (const char *value); -typedef void (*MonoLogCallback) (const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data); +MONO_API void +mono_trace_set_logdest_string (const char *value); + +MONO_API void +mono_trace_set_logheader_string (const char *value); + typedef void (*MonoPrintCallback) (const char *string, mono_bool is_stdout); +typedef void (*MonoLogCallback) (const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data); + + +typedef void (*MonoLoggerOpen) (const char *, void *); +typedef void (*MonoLoggerWrite) (const char *, GLogLevelFlags, mono_bool, const char *, va_list); +typedef void (*MonoLoggerClose) (void); + +typedef struct _MonoLogCallParm_ { + MonoLoggerOpen opener; /* Routine to open logging */ + MonoLoggerWrite writer; /* Routine to write log data */ + MonoLoggerClose closer; /* Routine to close logging */ + char *dest; /* Log destination */ + void *user_data; /* User data from legacy handler */ + mono_bool header; /* Whether we want pid/time/date in log message */ +} MonoLogCallParm; + +void +mono_trace_set_log_handler_internal (MonoLogCallParm *callback, void *user_data); MONO_API void mono_trace_set_log_handler (MonoLogCallback callback, void *user_data); @@ -22,6 +45,23 @@ mono_trace_set_print_handler (MonoPrintCallback callback); MONO_API void mono_trace_set_printerr_handler (MonoPrintCallback callback); +MONO_API void +mono_log_open_syslog(const char *, void *); + +MONO_API void +mono_log_write_syslog(const char *, GLogLevelFlags, mono_bool, const char *, va_list); + +MONO_API void +mono_log_close_syslog(void); + +MONO_API void +mono_log_open_logfile(const char *, void *); + +MONO_API void +mono_log_write_logfile(const char *, GLogLevelFlags, mono_bool, const char *, va_list); + +MONO_API void +mono_log_close_logfile(void); MONO_END_DECLS diff --git a/mono/utils/mono-threads.c b/mono/utils/mono-threads.c index fbc208213d3..6ebe007a3c6 100644 --- a/mono/utils/mono-threads.c +++ b/mono/utils/mono-threads.c @@ -80,6 +80,9 @@ static gboolean unified_suspend_enabled; /*abort at 1 sec*/ #define SLEEP_DURATION_BEFORE_ABORT 200 +static long sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING, + sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT; + static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops; void @@ -235,14 +238,14 @@ mono_threads_wait_pending_operations (void) for (i = 0; i < pending_suspends; ++i) { THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n"); InterlockedIncrement (&waits_done); - if (!mono_os_sem_timedwait (&suspend_semaphore, SLEEP_DURATION_BEFORE_ABORT, MONO_SEM_FLAGS_NONE)) + if (!mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE)) continue; mono_stopwatch_stop (&suspension_time); dump_threads (); MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i); - g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), SLEEP_DURATION_BEFORE_ABORT); + g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration); } mono_stopwatch_stop (&suspension_time); THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time)); @@ -639,6 +642,7 @@ mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size) gboolean res; threads_callbacks = *callbacks; thread_info_size = info_size; + char *sleepLimit; #ifdef HOST_WIN32 res = mono_native_tls_alloc (&thread_info_key, NULL); res = mono_native_tls_alloc (&thread_exited_key, NULL); @@ -655,6 +659,15 @@ mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size) g_assert (res); unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || mono_threads_is_coop_enabled (); + + if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) { + long threshold = strtol(sleepLimit, NULL, 10); + if ((errno == 0) && (threshold >= 40)) { + sleepAbortDuration = threshold; + sleepWarnDuration = threshold / 20; + } else + g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40"); + } mono_os_sem_init (&global_suspend_semaphore, 1); mono_os_sem_init (&suspend_semaphore, 0); diff --git a/msvc/libmonoutils.vcxproj b/msvc/libmonoutils.vcxproj index b18b8bc0d21..ca9ce67c465 100644 --- a/msvc/libmonoutils.vcxproj +++ b/msvc/libmonoutils.vcxproj @@ -61,6 +61,8 @@ + + @@ -372,4 +374,4 @@ - \ No newline at end of file +