.\" Author:
.\" Miguel de Icaza (miguel@gnu.org)
.\"
-.TH Mono "Mono 3.0"
+.TH Mono "Mono 4.5.2"
.SH NAME
mono \- Mono's ECMA-CLI native code generator (Just-in-Time and Ahead-of-Time)
.SH SYNOPSIS
this mode can be enabled at compile time by using the --with-cooperative-gc
flag when calling configure.
.TP
-\fBMONO_ENABLE_SHM\fR
-Unix only: Enable support for cross-process handles. Cross-process
-handles are used to expose process handles, thread handles, named
-mutexes, named events and named semaphores across Unix processes.
-.TP
\fBMONO_ENV_OPTIONS\fR
This environment variable allows you to pass command line arguments to
a Mono process through the environment. This is useful for example
deployment, see
http://www.mono-project.com/docs/getting-started/application-deployment/
.TP
-\fBMONO_RTC\fR
-Experimental RTC support in the statistical profiler: if the user has
-the permission, more accurate statistics are gathered. The MONO_RTC
-value must be restricted to what the Linux rtc allows: power of two
-from 64 to 8192 Hz. To enable higher frequencies like 4096 Hz, run as root:
-.nf
-
- echo 4096 > /proc/sys/dev/rtc/max-user-freq
-
-.fi
-.Sp
-For example:
-.nf
-
- MONO_RTC=4096 mono --profiler=default:stat program.exe
-
-.fi
-.TP
\fBMONO_SHARED_DIR\fR
If set its the directory where the ".wapi" handle state is stored.
This is the directory where the Windows I/O Emulation layer stores its
When this option is set, the runtime will invalidate the domain memory
pool instead of destroying it.
.TP
+\fBdisable_omit_fp\fR
+Disables a compiler optimization where the frame pointer is omitted
+from the stack. This optimization can interact badly with debuggers.
+.TP
\fBdont-free-domains\fR
This is an Optimization for multi-AppDomain applications (most
commonly ASP.NET applications). Due to internal limitations Mono,
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:
very helpfull to diagnose a miscompilation problems of a specific
method.
.TP
+\fBMONO_JIT_DUMP_METHOD\fR
+Enables sending of the JITs intermediate representation for a specified
+method to the IdealGraphVisualizer tool.
+.TP
\fBMONO_VERBOSE_HWCAP\fR
If set, makes the JIT output information about detected CPU features
(such as SSE, CMOV, FCMOV, etc) to stdout.
* Copyright 2011 Xamarin Inc (http://www.xamarin.com)
* 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.
*/
#ifndef __MONO_SGENGC_H__
#define __MONO_SGENGC_H__
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;
INTERNAL_MEM_CARDTABLE_MOD_UNION,
INTERNAL_MEM_BINARY_PROTOCOL,
INTERNAL_MEM_TEMPORARY,
+ INTERNAL_MEM_LOG_ENTRY,
INTERNAL_MEM_FIRST_CLIENT
};
typedef void (*CopyOrMarkObjectFunc) (GCObject**, SgenGrayQueue*);
typedef void (*ScanObjectFunc) (GCObject *obj, SgenDescriptor desc, SgenGrayQueue*);
typedef void (*ScanVTypeFunc) (GCObject *full_object, char *start, SgenDescriptor desc, SgenGrayQueue* BINARY_PROTOCOL_ARG (size_t size));
+typedef void (*ScanPtrFieldFunc) (GCObject *obj, GCObject **ptr, SgenGrayQueue* queue);
typedef gboolean (*DrainGrayStackFunc) (SgenGrayQueue *queue);
typedef struct {
CopyOrMarkObjectFunc copy_or_mark_object;
ScanObjectFunc scan_object;
ScanVTypeFunc scan_vtype;
+ ScanPtrFieldFunc scan_ptr_field;
/* Drain stack optimized for the above functions */
DrainGrayStackFunc drain_gray_stack;
/*FIXME add allocation function? */
void sgen_free_internal_dynamic (void *addr, size_t size, int type);
void sgen_pin_stats_enable (void);
-void sgen_pin_stats_register_object (GCObject *obj, size_t size);
+void sgen_pin_stats_register_object (GCObject *obj, int generation);
void sgen_pin_stats_register_global_remset (GCObject *obj);
-void sgen_pin_stats_print_class_stats (void);
+void sgen_pin_stats_report (void);
void sgen_sort_addresses (void **array, size_t size);
void sgen_add_to_global_remset (gpointer ptr, GCObject *obj);
/* Updating references */
#ifdef SGEN_CHECK_UPDATE_REFERENCE
-gboolean sgen_thread_pool_is_thread_pool_thread (MonoNativeThreadId some_thread) MONO_INTERNAL;
+gboolean sgen_thread_pool_is_thread_pool_thread (MonoNativeThreadId some_thread);
+
static inline void
sgen_update_reference (GCObject **p, GCObject *o, gboolean allow_null)
{
typedef void (*sgen_cardtable_block_callback) (mword start, mword size);
void sgen_major_collector_iterate_live_block_ranges (sgen_cardtable_block_callback callback);
+void sgen_major_collector_iterate_block_ranges (sgen_cardtable_block_callback callback);
typedef enum {
ITERATE_OBJECTS_SWEEP = 1,
size_t num_unique_scanned_objects;
} ScannedObjectCounts;
+typedef enum {
+ CARDTABLE_SCAN_GLOBAL = 0,
+ CARDTABLE_SCAN_MOD_UNION = 1,
+ CARDTABLE_SCAN_MOD_UNION_PRECLEAN = CARDTABLE_SCAN_MOD_UNION | 2,
+} CardTableScanType;
+
typedef struct _SgenMajorCollector SgenMajorCollector;
struct _SgenMajorCollector {
size_t section_size;
void (*free_non_pinned_object) (GCObject *obj, size_t size);
void (*pin_objects) (SgenGrayQueue *queue);
void (*pin_major_object) (GCObject *obj, SgenGrayQueue *queue);
- void (*scan_card_table) (gboolean mod_union, ScanCopyContext ctx);
+ void (*scan_card_table) (CardTableScanType scan_type, ScanCopyContext ctx);
void (*iterate_live_block_ranges) (sgen_cardtable_block_callback callback);
+ void (*iterate_block_ranges) (sgen_cardtable_block_callback callback);
void (*update_cardtable_mod_union) (void);
void (*init_to_space) (void);
void (*sweep) (void);
/* STW */
-typedef struct {
- int generation;
- const char *reason;
- gboolean is_overflow;
- gint64 total_time;
- gint64 stw_time;
- gint64 bridge_time;
-} GGTimingInfo;
-
void sgen_stop_world (int generation);
-void sgen_restart_world (int generation, GGTimingInfo *timing);
+void sgen_restart_world (int generation);
gboolean sgen_is_world_stopped (void);
gboolean sgen_set_allow_synchronous_major (gboolean flag);
extern LOSObject *los_object_list;
extern mword los_memory_usage;
+extern mword los_memory_usage_total;
void sgen_los_free_object (LOSObject *obj);
void* sgen_los_alloc_large_inner (GCVTable vtable, size_t size);
gboolean sgen_ptr_is_in_los (char *ptr, char **start);
void sgen_los_iterate_objects (IterateObjectCallbackFunc cb, void *user_data);
void sgen_los_iterate_live_block_ranges (sgen_cardtable_block_callback callback);
-void sgen_los_scan_card_table (gboolean mod_union, ScanCopyContext ctx);
+void sgen_los_scan_card_table (CardTableScanType scan_type, ScanCopyContext ctx);
void sgen_los_update_cardtable_mod_union (void);
void sgen_los_count_cards (long long *num_total_cards, long long *num_marked_cards);
gboolean sgen_los_is_valid_object (char *object);
extern guint32 tlab_size;
extern NurseryClearPolicy nursery_clear_policy;
extern gboolean sgen_try_free_some_memory;
+extern mword total_promoted_size;
+extern mword total_allocated_major;
extern MonoCoopMutex gc_mutex;
*
* Copyright 2011 Novell, Inc (http://www.novell.com)
* Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
#include <config.h>
#include <mono/utils/mono-lazy-init.h>
#include <mono/utils/mono-coop-mutex.h>
#include <mono/utils/mono-coop-semaphore.h>
+#include <mono/utils/mono-threads-coop.h>
#include <errno.h>
The GC has to acquire this lock before starting a STW to make sure
a runtime suspend won't make it wronly see a thread in a safepoint
when it is in fact not.
+
+This has to be a naked locking primitive, and not a coop aware one, as
+it needs to be usable when destroying thread_info_key, the TLS key for
+the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
+(which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
+to an assertion error. We then simply switch state manually in
+mono_thread_info_suspend_lock_with_info.
*/
-static MonoCoopSem global_suspend_semaphore;
+static MonoSemType global_suspend_semaphore;
static size_t thread_info_size;
static MonoThreadInfoCallbacks threads_callbacks;
/*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
static void
dump_threads (void)
{
- MonoThreadInfo *info;
MonoThreadInfo *cur = mono_thread_info_current ();
MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
#else
MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] state %x %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, info->thread_state, info == cur ? "GC INITIATOR" : "" );
#endif
-
- } END_FOREACH_THREAD_SAFE
+ } FOREACH_THREAD_SAFE_END
}
gboolean
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));
{
MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
- if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
+ if (!mono_lls_find (&thread_list, hp, (uintptr_t)id, HAZARD_FREE_ASYNC_CTX)) {
mono_hazard_pointer_clear_all (hp, -1);
return NULL;
}
{
MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
- if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
+ if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX)) {
mono_hazard_pointer_clear_all (hp, -1);
return FALSE;
}
gboolean res;
THREADS_DEBUG ("removing info %p\n", info);
- res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
+ res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX);
mono_hazard_pointer_clear_all (hp, -1);
return res;
}
return info;
}
+static void
+mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
+
static void
unregister_thread (void *arg)
{
- MonoThreadInfo *info = (MonoThreadInfo *) arg;
- int small_id = info->small_id;
+ gpointer gc_unsafe_stackdata;
+ MonoThreadInfo *info;
+ int small_id;
+
+ info = (MonoThreadInfo *) arg;
g_assert (info);
+ small_id = info->small_id;
+
+ /* We only enter the GC unsafe region, as when exiting this function, the thread
+ * will be detached, and the current MonoThreadInfo* will be destroyed. */
+ mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
+
THREADS_DEBUG ("unregistering info %p\n", info);
mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
if (threads_callbacks.thread_detach)
threads_callbacks.thread_detach (info);
- mono_thread_info_suspend_lock ();
+ mono_thread_info_suspend_lock_with_info (info);
/*
Now perform the callback that must be done under locks.
g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
/*now it's safe to free the thread info.*/
- mono_thread_hazardous_free_or_queue (info, free_thread_info, TRUE, FALSE);
+ mono_thread_hazardous_try_free (info, free_thread_info);
+ /* Pump the HP queue */
+ mono_thread_hazardous_try_free_some ();
+
mono_thread_small_id_free (small_id);
}
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_coop_sem_init (&global_suspend_semaphore, 1);
+ mono_os_sem_init (&global_suspend_semaphore, 1);
mono_os_sem_init (&suspend_semaphore, 0);
- mono_lls_init (&thread_list, NULL);
+ mono_lls_init (&thread_list, NULL, HAZARD_FREE_NO_LOCK);
mono_thread_smr_init ();
mono_threads_init_platform ();
mono_threads_init_coop ();
return mono_thread_info_core_resume (info);
}
-gboolean
-mono_thread_info_check_suspend_result (MonoThreadInfo *info)
-{
- return check_async_suspend (info);
-}
-
/*
FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
mono_threads_wait_pending_operations ();
if (!check_async_suspend (info)) {
+ mono_thread_info_core_resume (info);
+ mono_threads_wait_pending_operations ();
mono_hazard_pointer_clear (hp, 1);
return NULL;
}
A GC that has safepoints must take this lock as part of its
STW to make sure no unsafe pending suspend is in progress.
*/
+
+static void
+mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
+{
+ g_assert (info);
+
+ MONO_ENTER_GC_SAFE_WITH_INFO(info);
+
+ int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
+ g_assert (res != -1);
+
+ MONO_EXIT_GC_SAFE_WITH_INFO;
+}
+
void
mono_thread_info_suspend_lock (void)
{
- int res = mono_coop_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
- g_assert (res != -1);
+ mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
}
void
mono_thread_info_suspend_unlock (void)
{
- mono_coop_sem_post (&global_suspend_semaphore);
+ mono_os_sem_post (&global_suspend_semaphore);
}
/*
static inline guint32
sleep_interruptable (guint32 ms, gboolean *alerted)
{
- guint32 start, now, end;
+ gint64 now, end;
g_assert (INFINITE == G_MAXUINT32);
g_assert (alerted);
*alerted = FALSE;
- start = mono_msec_ticks ();
-
- if (start < G_MAXUINT32 - ms) {
- end = start + ms;
- } else {
- /* start + ms would overflow guint32 */
- end = G_MAXUINT32;
- }
+ if (ms != INFINITE)
+ end = mono_100ns_ticks () + (ms * 1000 * 10);
mono_lazy_initialize (&sleep_init, sleep_initialize);
mono_coop_mutex_lock (&sleep_mutex);
- for (now = mono_msec_ticks (); ms == INFINITE || now - start < ms; now = mono_msec_ticks ()) {
+ for (;;) {
+ if (ms != INFINITE) {
+ now = mono_100ns_ticks ();
+ if (now > end)
+ break;
+ }
+
mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
if (*alerted) {
mono_coop_mutex_unlock (&sleep_mutex);
return WAIT_IO_COMPLETION;
}
- if (ms < INFINITE)
- mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
+ if (ms != INFINITE)
+ mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, (end - now) / 10 / 1000);
else
mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
if (alerted)
return sleep_interruptable (ms, alerted);
- MONO_PREPARE_BLOCKING;
+ MONO_ENTER_GC_SAFE;
if (ms == INFINITE) {
do {
#endif /* __linux__ */
}
- MONO_FINISH_BLOCKING;
+ MONO_EXIT_GC_SAFE;
return 0;
}
gint
mono_thread_info_usleep (guint64 us)
{
- MONO_PREPARE_BLOCKING;
+ MONO_ENTER_GC_SAFE;
g_usleep (us);
- MONO_FINISH_BLOCKING;
+ MONO_EXIT_GC_SAFE;
return 0;
}
return mono_threads_core_open_thread_handle (handle, tid);
}
-void
-mono_thread_info_set_name (MonoNativeThreadId tid, const char *name)
-{
- mono_threads_core_set_name (tid, name);
-}
-
#define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
struct _MonoThreadInfoInterruptToken {
g_string_append_printf (text, "waiting");
}
-/* info must be self or be held in a hazard pointer. */
gboolean
-mono_threads_add_async_job (MonoThreadInfo *info, MonoAsyncJob job)
+mono_thread_info_is_current (MonoThreadInfo *info)
{
- MonoAsyncJob old_job;
- do {
- old_job = (MonoAsyncJob) info->service_requests;
- if (old_job & job)
- return FALSE;
- } while (InterlockedCompareExchange (&info->service_requests, old_job | job, old_job) != old_job);
- return TRUE;
-}
-
-MonoAsyncJob
-mono_threads_consume_async_jobs (void)
-{
- MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
-
- if (!info)
- return (MonoAsyncJob) 0;
-
- return (MonoAsyncJob) InterlockedExchange (&info->service_requests, 0);
+ return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
}