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:
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;
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 \
--- /dev/null
+/*
+ * mono-log-common.c: Platform-independent interface to the logger
+ *
+ * This module contains the POSIX syslog logger interface
+ *
+ * Author:
+ * Neale Ferguson <neale@sinenomine.net>
+ *
+ */
+#include <config.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <glib.h>
+#include <errno.h>
+#include <time.h>
+#ifndef HOST_WIN32
+#include <sys/time.h>
+#else
+#include <process.h>
+#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 = snprintf(logMessage, sizeof(logMessage), "%s level[%c] mono[%d]: ",
+ logTime,mapLogFileLevel(level),pid);
+ }
+ nLog = sizeof(logMessage) - iLog - 2;
+ iLog = vsnprintf(logMessage+iLog, nLog, format, args);
+ 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;
+ }
+}
--- /dev/null
+/*
+ * mono-log-posix.c: POSIX interface to the logger
+ *
+ * This module contains the POSIX syslog logger routines
+ *
+ * Author:
+ * Neale Ferguson <neale@sinenomine.net>
+ *
+ */
+#include <config.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if defined(_POSIX_VERSION)
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <glib.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#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
--- /dev/null
+/*
+ * mono-log-windows.c: Simplistic simulation of a syslog logger for Windows
+ *
+ * This module contains the Windows syslog logger interface
+ *
+ * Author:
+ * Neale Ferguson <neale@sinenomine.net>
+ *
+ */
+#include <config.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HOST_WIN32
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <glib.h>
+#include <errno.h>
+#include <time.h>
+#include <process.h>
+#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 = snprintf(logMessage, sizeof(logMessage), "%s level[%c] mono[%d]: ",
+ logTime,mapLogFileLevel(level),pid);
+ nLog = sizeof(logMessage) - iLog - 2;
+ iLog = vsnprintf(logMessage+iLog, nLog, format, args);
+ 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
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 MonoLogCallback logCallback = {
+ .opener = NULL,
+ .writer = NULL,
+ .closer = NULL,
+ .header = FALSE
+};
+
/**
* mono_trace_init:
*
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"));
}
}
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);
}
/**
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)
+{
+ MonoLogCallback 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;
+ mono_trace_set_log_handler(&logger, dest, NULL);
+ } else {
+ logger.opener = mono_log_open_syslog;
+ logger.writer = mono_log_write_syslog;
+ logger.closer = mono_log_close_syslog;
+ mono_trace_set_log_handler(&logger, mono_log_domain, 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)
+{
+ MonoLogCallback logger;
+
+ if (head == NULL) {
+ mono_trace_log_header = FALSE;
+ } else {
+ mono_trace_log_header = TRUE;
+ }
}
/**
return (level <= mono_internal_current_level && mask & mono_internal_current_mask);
}
-static MonoLogCallback log_callback;
-
-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";
- }
-}
-
-static void
-log_adapter (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
-{
- log_callback (log_domain, log_level_get_name (log_level), message, log_level & G_LOG_LEVEL_ERROR, user_data);
-}
-
/**
* mono_trace_set_log_handler:
*
* execution will not resume after a fatal error.
*/
void
-mono_trace_set_log_handler (MonoLogCallback callback, void *user_data)
+mono_trace_set_log_handler (MonoLogCallback *callback, const char *dest, void *user_data)
{
g_assert (callback);
- log_callback = callback;
- g_log_set_default_handler (log_adapter, user_data);
+ logCallback.opener = callback->opener;
+ logCallback.writer = callback->writer;
+ logCallback.closer = callback->closer;
+ logCallback.header = mono_trace_log_header;
+ logCallback.opener(dest, user_data);
}
static void
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 (*MonoLoggerOpen) (const char *, void *);
+typedef void (*MonoLoggerWrite) (const char *, GLogLevelFlags, mono_bool, const char *, va_list);
+typedef void (*MonoLoggerClose) (void);
+
+typedef struct _MonoLogCallback_ {
+ MonoLoggerOpen opener; /* Routine to open logging */
+ MonoLoggerWrite writer; /* Routine to write log data */
+ MonoLoggerClose closer; /* Routine to close logging */
+ mono_bool header; /* Whether we want pid/time/date in log message */
+} MonoLogCallback;
+
MONO_API void
-mono_trace_set_log_handler (MonoLogCallback callback, void *user_data);
+mono_trace_set_log_handler (MonoLogCallback *callback, const char *dest, void *user_data);
MONO_API void
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
/*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
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));
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);
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);
<ClCompile Include="..\mono\utils\mono-io-portability.c" />\r
<ClCompile Include="..\mono\utils\mono-linked-list-set.c" />\r
<ClCompile Include="..\mono\utils\mono-logger.c" />\r
+ <ClCompile Include="..\mono\utils\mono-log-windows.c" />\r
+ <ClCompile Include="..\mono\utils\mono-log-common.c" />\r
<ClCompile Include="..\mono\utils\mono-math.c" />\r
<ClCompile Include="..\mono\utils\mono-md5.c" />\r
<ClCompile Include="..\mono\utils\mono-mmap.c" />\r