#include <mono/metadata/threads.h>
#include <mono/metadata/appdomain.h>
#include <mono/metadata/debug-helpers.h>
-#include "mono/metadata/profiler.h"
#include <mono/metadata/profiler-private.h>
#include <mono/metadata/mono-config.h>
#include <mono/metadata/environment.h>
#include <mono/utils/mono-threads.h>
#include <mono/utils/mono-threads-coop.h>
#include <mono/utils/checked-build.h>
+#include <mono/utils/mono-compiler.h>
+#include <mono/utils/mono-proclib.h>
#include <mono/metadata/w32handle.h>
#include <mono/metadata/threadpool.h>
#endif
GList* mono_aot_paths;
-static gboolean mini_enable_profiler = FALSE;
-static char* mini_profiler_options = NULL;
+static GPtrArray *profile_options;
static GSList *tramp_infos;
static void register_icalls (void);
-static gboolean mini_profiler_enabled (void) { return mini_enable_profiler; }
-static const char* mini_profiler_get_options (void) { return mini_profiler_options; }
-
gboolean
mono_running_on_valgrind (void)
{
mono_jit_unlock ();
}
-#if defined(__native_client_codegen__) && defined(__native_client__)
-void
-mono_nacl_gc()
-{
-#ifdef __native_client_gc__
- __nacl_suspend_thread_if_needed();
-#endif
-}
-#endif /* __native_client__ */
-
/**
* mono_create_unwind_op:
*
if (!domain)
domain = mono_get_root_domain ();
- copy = g_new0 (MonoTrampInfo, 1);
+ if (domain)
+ copy = mono_domain_alloc0 (domain, sizeof (MonoTrampInfo));
+ else
+ copy = g_new0 (MonoTrampInfo, 1);
+
copy->code = info->code;
copy->code_size = info->code_size;
copy->name = g_strdup (info->name);
if (info->unwind_ops) {
copy->uw_info = mono_unwind_ops_encode (info->unwind_ops, ©->uw_info_len);
copy->owns_uw_info = TRUE;
+ if (domain) {
+ /* Move unwind info into the domain's memory pool so that it is removed once the domain is released. */
+ guint8 *temp = copy->uw_info;
+ copy->uw_info = mono_domain_alloc (domain, copy->uw_info_len);
+ memcpy (copy->uw_info, temp, copy->uw_info_len);
+ g_free (temp);
+ }
} else {
/* Trampolines from aot have the unwind ops already encoded */
copy->uw_info = info->uw_info;
copy->uw_info_len = info->uw_info_len;
}
- mono_jit_lock ();
- tramp_infos = g_slist_prepend (tramp_infos, copy);
- mono_jit_unlock ();
-
mono_save_trampoline_xdebug_info (info);
mono_lldb_save_trampoline_info (info);
mono_arch_unwindinfo_install_tramp_unwind_info (info->unwind_ops, info->code, info->code_size);
#endif
- /* Only register trampolines that have unwind infos */
- if (mono_get_root_domain () && copy->uw_info)
+ if (!domain) {
+ /* If no root domain has been created yet, postpone the registration. */
+ mono_jit_lock ();
+ tramp_infos = g_slist_prepend (tramp_infos, copy);
+ mono_jit_unlock ();
+ } else if (copy->uw_info) {
+ /* Only register trampolines that have unwind infos */
register_trampoline_jit_info (domain, copy);
+ }
if (mono_jit_map_is_enabled ())
mono_emit_jit_tramp (info->code, info->code_size, info->name);
G_GNUC_UNUSED gboolean
mono_debug_count (void)
{
- static int count = 0;
- static gboolean inited;
- static char *value;
+ static int count = 0, int_val = 0;
+ static gboolean inited, has_value = FALSE;
count ++;
if (!inited) {
- value = g_getenv ("COUNT");
+ char *value = g_getenv ("COUNT");
+ if (value) {
+ int_val = atoi (value);
+ g_free (value);
+ has_value = TRUE;
+ }
inited = TRUE;
}
- if (!value)
+ if (!has_value)
return TRUE;
- int int_val = atoi (value);
- g_free (value);
-
if (count == int_val)
break_count ();
mono_thread_info_tls_set (info, TLS_KEY_LMF_ADDR, lmf_addr);
}
+/*
+ * mono_push_lmf:
+ *
+ * Push an MonoLMFExt frame on the LMF stack.
+ */
+void
+mono_push_lmf (MonoLMFExt *ext)
+{
+#ifdef MONO_ARCH_HAVE_INIT_LMF_EXT
+ MonoLMF **lmf_addr;
+
+ lmf_addr = mono_get_lmf_addr ();
+
+ mono_arch_init_lmf_ext (ext, *lmf_addr);
+
+ mono_set_lmf ((MonoLMF*)ext);
+#else
+ NOT_IMPLEMENTED;
+#endif
+}
+
+/*
+ * mono_push_lmf:
+ *
+ * Pop the last frame from the LMF stack.
+ */
+void
+mono_pop_lmf (MonoLMF *lmf)
+{
+ mono_set_lmf ((MonoLMF *)(((gssize)lmf->previous_lmf) & ~3));
+}
+
/*
* mono_jit_thread_attach:
*
MonoDomain *orig;
gboolean attached;
- g_assert (!mono_threads_is_coop_enabled ());
+ g_assert (!mono_threads_is_blocking_transition_enabled ());
if (!domain) {
/* Happens when called from AOTed code which is only used in the root domain. */
void
mono_jit_set_domain (MonoDomain *domain)
{
- g_assert (!mono_threads_is_coop_enabled ());
+ g_assert (!mono_threads_is_blocking_transition_enabled ());
if (domain)
mono_domain_set (domain, TRUE);
static void
mono_thread_abort (MonoObject *obj)
{
- /* MonoJitTlsData *jit_tls = mono_native_tls_get_value (mono_jit_tls_id); */
+ /* MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls (); */
/* handle_remove should be eventually called for this thread, too
g_free (jit_tls);*/
case MONO_PATCH_INFO_GC_SAFE_POINT_FLAG:
case MONO_PATCH_INFO_AOT_MODULE:
case MONO_PATCH_INFO_JIT_THREAD_ATTACH:
+ case MONO_PATCH_INFO_PROFILER_ALLOCATION_COUNT:
return (ji->type << 8);
case MONO_PATCH_INFO_CASTCLASS_CACHE:
return (ji->type << 8) | (ji->data.index);
return (ji->type << 8) | (gssize)info->klass | (gssize)info->method;
}
case MONO_PATCH_INFO_JIT_ICALL_ADDR:
+ case MONO_PATCH_INFO_JIT_ICALL_ADDR_NOCALL:
return (ji->type << 8) | g_str_hash (ji->data.target);
case MONO_PATCH_INFO_GSHAREDVT_IN_WRAPPER:
return (ji->type << 8) | mono_signature_hash (ji->data.sig);
case MONO_PATCH_INFO_VIRT_METHOD:
return ji1->data.virt_method->klass == ji2->data.virt_method->klass && ji1->data.virt_method->method == ji2->data.virt_method->method;
case MONO_PATCH_INFO_JIT_ICALL_ADDR:
+ case MONO_PATCH_INFO_JIT_ICALL_ADDR_NOCALL:
if (ji1->data.target == ji2->data.target)
return 1;
return strcmp (ji1->data.target, ji2->data.target) == 0 ? 1 : 0;
target = mono_icall_get_wrapper (mi);
break;
}
- case MONO_PATCH_INFO_JIT_ICALL_ADDR: {
+ case MONO_PATCH_INFO_JIT_ICALL_ADDR:
+ case MONO_PATCH_INFO_JIT_ICALL_ADDR_NOCALL: {
MonoJitICallInfo *mi = mono_find_jit_icall_by_name (patch_info->data.name);
if (!mi) {
g_warning ("unknown MONO_PATCH_INFO_JIT_ICALL_ADDR %s", patch_info->data.name);
break;
}
case MONO_PATCH_INFO_GC_SAFE_POINT_FLAG:
-#if defined(__native_client_codegen__)
- target = (gpointer)&__nacl_thread_suspension_needed;
-#else
g_assert (mono_threads_is_coop_enabled ());
target = (gpointer)&mono_polling_required;
-#endif
break;
case MONO_PATCH_INFO_SWITCH: {
gpointer *jump_table;
int i;
-#if defined(__native_client__) && defined(__native_client_codegen__)
- /* This memory will leak, but we don't care if we're */
- /* not deleting JIT'd methods anyway */
- jump_table = g_malloc0 (sizeof(gpointer) * patch_info->data.table->table_size);
-#else
if (method && method->dynamic) {
jump_table = (void **)mono_code_manager_reserve (mono_dynamic_code_hash_lookup (domain, method)->code_mp, sizeof (gpointer) * patch_info->data.table->table_size);
} else {
jump_table = (void **)mono_domain_code_reserve (domain, sizeof (gpointer) * patch_info->data.table->table_size);
}
}
-#endif
for (i = 0; i < patch_info->data.table->table_size; i++) {
jump_table [i] = code + GPOINTER_TO_INT (patch_info->data.table->table [i]);
target = mi->func;
break;
}
+ case MONO_PATCH_INFO_PROFILER_ALLOCATION_COUNT: {
+ target = (gpointer) &mono_profiler_state.gc_allocation_count;
+ break;
+ }
default:
g_assert_not_reached ();
}
return ji;
}
-MonoJitInfo *
-mono_get_jit_info_from_method (MonoDomain *domain, MonoMethod *method)
+MonoClass*
+mini_get_class (MonoMethod *method, guint32 token, MonoGenericContext *context)
{
- return lookup_method (domain, method);
+ MonoError error;
+ MonoClass *klass;
+
+ if (method->wrapper_type != MONO_WRAPPER_NONE) {
+ klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
+ if (context) {
+ klass = mono_class_inflate_generic_class_checked (klass, context, &error);
+ mono_error_cleanup (&error); /* FIXME don't swallow the error */
+ }
+ } else {
+ klass = mono_class_get_and_inflate_typespec_checked (method->klass->image, token, context, &error);
+ mono_error_cleanup (&error); /* FIXME don't swallow the error */
+ }
+ if (klass)
+ mono_class_init (klass);
+ return klass;
}
#if ENABLE_JIT_MAP
g_assert_not_reached ();
}
+/*
+Overall algorithm:
+
+When a JIT request is made, we check if there's an outstanding one for that method and, if it exits, put the thread to sleep.
+ If the current thread is already JITing another method, don't wait as it might cause a deadlock.
+ Dependency management in this case is too complex to justify implementing it.
+
+If there are no outstanding requests, the current thread is doing nothing and there are already mono_cpu_count threads JITing, go to sleep.
+
+TODO:
+ Get rid of cctor invocations from within the JIT, it increases JIT duration and complicates things A LOT.
+ Can we get rid of ref_count and use `done && threads_waiting == 0` as the equivalent of `ref_count == 0`?
+ Reduce amount of dynamically allocated - possible once the JIT is no longer reentrant
+ Maybe pool JitCompilationEntry, specially those with an inited cond var;
+*/
+typedef struct {
+ MonoMethod *method;
+ MonoDomain *domain;
+ int compilation_count; /* Number of threads compiling this method - This happens due to the JIT being reentrant */
+ int ref_count; /* Number of threads using this JitCompilationEntry, roughtly 1 + threads_waiting */
+ int threads_waiting; /* Number of threads waiting on this job */
+ gboolean has_cond; /* True if @cond was initialized */
+ gboolean done; /* True if the method finished JIT'ing */
+ MonoCoopCond cond; /* Cond sleeping threads wait one */
+} JitCompilationEntry;
+
+typedef struct {
+ GPtrArray *in_flight_methods; //JitCompilationEntry*
+ MonoCoopMutex lock;
+} JitCompilationData;
+
+static JitCompilationData compilation_data;
+static int jit_methods_waited, jit_methods_multiple, jit_methods_overload, jit_spurious_wakeups;
+
+static void
+mini_jit_init_job_control (void)
+{
+ mono_coop_mutex_init (&compilation_data.lock);
+ compilation_data.in_flight_methods = g_ptr_array_new ();
+}
+
+static void
+lock_compilation_data (void)
+{
+ mono_coop_mutex_lock (&compilation_data.lock);
+}
+
+static void
+unlock_compilation_data (void)
+{
+ mono_coop_mutex_unlock (&compilation_data.lock);
+}
+
+static JitCompilationEntry*
+find_method (MonoMethod *method, MonoDomain *domain)
+{
+ int i;
+ for (i = 0; i < compilation_data.in_flight_methods->len; ++i){
+ JitCompilationEntry *e = compilation_data.in_flight_methods->pdata [i];
+ if (e->method == method && e->domain == domain)
+ return e;
+ }
+
+ return NULL;
+}
+
+static void
+add_current_thread (MonoJitTlsData *jit_tls)
+{
+ ++jit_tls->active_jit_methods;
+}
+
+static void
+unref_jit_entry (JitCompilationEntry *entry)
+{
+ --entry->ref_count;
+ if (entry->ref_count)
+ return;
+ if (entry->has_cond)
+ mono_coop_cond_destroy (&entry->cond);
+ g_free (entry);
+}
+
+/*
+ * Returns true if this method waited successfully for another thread to JIT it
+ */
+static gboolean
+wait_or_register_method_to_compile (MonoMethod *method, MonoDomain *domain)
+{
+ MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
+ JitCompilationEntry *entry;
+
+ static gboolean inited;
+ if (!inited) {
+ mono_counters_register ("JIT compile waited others", MONO_COUNTER_INT|MONO_COUNTER_JIT, &jit_methods_waited);
+ mono_counters_register ("JIT compile 1+ jobs", MONO_COUNTER_INT|MONO_COUNTER_JIT, &jit_methods_multiple);
+ mono_counters_register ("JIT compile overload wait", MONO_COUNTER_INT|MONO_COUNTER_JIT, &jit_methods_overload);
+ mono_counters_register ("JIT compile spurious wakeups", MONO_COUNTER_INT|MONO_COUNTER_JIT, &jit_spurious_wakeups);
+ inited = TRUE;
+ }
+
+ lock_compilation_data ();
+
+ if (!(entry = find_method (method, domain))) {
+ entry = g_new0 (JitCompilationEntry, 1);
+ entry->method = method;
+ entry->domain = domain;
+ entry->compilation_count = entry->ref_count = 1;
+ g_ptr_array_add (compilation_data.in_flight_methods, entry);
+ g_assert (find_method (method, domain) == entry);
+ add_current_thread (jit_tls);
+
+ unlock_compilation_data ();
+ return FALSE;
+ } else if (jit_tls->active_jit_methods > 0) {
+ //We can't suspend the current thread if it's already JITing a method.
+ //Dependency management is too compilated and we want to get rid of this anyways.
+ ++entry->compilation_count;
+ ++jit_methods_multiple;
+ ++jit_tls->active_jit_methods;
+
+ unlock_compilation_data ();
+ return FALSE;
+ } else {
+ ++jit_methods_waited;
+ ++entry->ref_count;
+
+ if (!entry->has_cond) {
+ mono_coop_cond_init (&entry->cond);
+ entry->has_cond = TRUE;
+ }
+
+ while (TRUE) {
+ ++entry->threads_waiting;
+
+ g_assert (entry->has_cond);
+ mono_coop_cond_wait (&entry->cond, &compilation_data.lock);
+ --entry->threads_waiting;
+
+ if (entry->done) {
+ unref_jit_entry (entry);
+ unlock_compilation_data ();
+ return TRUE;
+ } else {
+ ++jit_spurious_wakeups;
+ }
+ }
+ }
+}
+
+static void
+unregister_method_for_compile (MonoMethod *method, MonoDomain *target_domain)
+{
+ MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
+
+ lock_compilation_data ();
+
+ g_assert (jit_tls->active_jit_methods > 0);
+ --jit_tls->active_jit_methods;
+
+ JitCompilationEntry *entry = find_method (method, target_domain);
+ g_assert (entry); // It would be weird to fail
+ entry->done = TRUE;
+
+ if (entry->threads_waiting) {
+ g_assert (entry->has_cond);
+ mono_coop_cond_broadcast (&entry->cond);
+ }
+
+ if (--entry->compilation_count == 0) {
+ g_ptr_array_remove (compilation_data.in_flight_methods, entry);
+ unref_jit_entry (entry);
+ }
+
+ unlock_compilation_data ();
+}
+
+
static gpointer
mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt, gboolean jit_only, MonoError *error)
{
}
}
+lookup_start:
info = lookup_method (target_domain, method);
if (info) {
/* We can't use a domain specific method in another domain */
if (! ((domain != target_domain) && !info->domain_neutral)) {
MonoVTable *vtable;
- mono_jit_stats.methods_lookups++;
+ InterlockedIncrement (&mono_jit_stats.methods_lookups);
vtable = mono_class_vtable_full (domain, method->klass, error);
if (!is_ok (error))
return NULL;
#ifdef MONO_USE_AOT_COMPILER
if (opt & MONO_OPT_AOT) {
- MonoDomain *domain = mono_domain_get ();
+ MonoDomain *domain = NULL;
+
+ if (mono_aot_mode == MONO_AOT_MODE_INTERP && method->wrapper_type == MONO_WRAPPER_UNKNOWN) {
+ WrapperInfo *info = mono_marshal_get_wrapper_info (method);
+ g_assert (info);
+ if (info->subtype == WRAPPER_SUBTYPE_INTERP_IN)
+ /* AOT'd wrappers for interp must be owned by root domain */
+ domain = mono_get_root_domain ();
+ }
+
+ if (!domain)
+ domain = mono_domain_get ();
mono_class_init (method->klass);
if ((code = mono_aot_get_method_checked (domain, method, error))) {
MonoVTable *vtable;
- if (mono_runtime_is_critical_method (method) || mono_gc_is_critical_method (method)) {
+ if (mono_gc_is_critical_method (method)) {
/*
* The suspend code needs to be able to lookup these methods by ip in async context,
* so preload their jit info.
}
}
- if (!code)
+ if (!code) {
+ if (wait_or_register_method_to_compile (method, target_domain))
+ goto lookup_start;
code = mono_jit_compile_method_inner (method, target_domain, opt, error);
+ unregister_method_for_compile (method, target_domain);
+ }
if (!mono_error_ok (error))
return NULL;
if (info) {
/* We can't use a domain specific method in another domain */
if (! ((domain != target_domain) && !info->domain_neutral)) {
- mono_jit_stats.methods_lookups++;
+ InterlockedIncrement (&mono_jit_stats.methods_lookups);
if (ji)
*ji = info;
return info->code_start;
#endif
error_init (error);
+ if (exc)
+ *exc = NULL;
if (obj == NULL && !(method->flags & METHOD_ATTRIBUTE_STATIC) && !method->string_ctor && (method->wrapper_type == 0)) {
g_warning ("Ignoring invocation of an instance method on a NULL instance.\n");
static gpointer
mini_create_ftnptr (MonoDomain *domain, gpointer addr)
{
-#if !defined(__ia64__) && (!defined(__ppc64__) && !defined(__powerpc64__) || _CALL_ELF == 2)
+#if (!defined(__ppc64__) && !defined(__powerpc64__) || _CALL_ELF == 2)
return addr;
#else
gpointer* desc = NULL;
if ((desc = g_hash_table_lookup (domain->ftnptrs_hash, addr)))
return desc;
-# ifdef __ia64__
- desc = mono_domain_code_reserve (domain, 2 * sizeof (gpointer));
-
- desc [0] = addr;
- desc [1] = NULL;
-# elif defined(__ppc64__) || defined(__powerpc64__)
+# if defined(__ppc64__) || defined(__powerpc64__)
desc = mono_domain_alloc0 (domain, 3 * sizeof (gpointer));
static gpointer
mini_get_addr_from_ftnptr (gpointer descr)
{
-#if defined(__ia64__) || ((defined(__ppc64__) || defined(__powerpc64__)) && _CALL_ELF != 2)
+#if ((defined(__ppc64__) || defined(__powerpc64__)) && _CALL_ELF != 2)
return *(gpointer*)descr;
#else
return descr;
g_hash_table_foreach (info->llvm_jit_callees, free_jit_callee_list, NULL);
g_hash_table_destroy (info->llvm_jit_callees);
}
+ mono_internal_hash_table_destroy (&info->interp_code_hash);
#ifdef ENABLE_LLVM
mono_llvm_free_domain_info (domain);
#endif
}
void
-mini_profiler_enable_with_options (const char* profile_options)
+mini_add_profiler_argument (const char *desc)
{
- mini_enable_profiler = TRUE;
- mini_profiler_options = g_strdup (profile_options);
+ if (!profile_options)
+ profile_options = g_ptr_array_new ();
+
+ g_ptr_array_add (profile_options, (gpointer) desc);
}
MonoDomain *
CHECKED_MONO_INIT ();
-#if defined(__linux__) && !defined(__native_client__)
+#if defined(__linux__)
if (access ("/proc/self/maps", F_OK) != 0) {
g_print ("Mono requires /proc to be mounted.\n");
exit (1);
}
#endif
+#ifdef ENABLE_INTERPRETER
+ mono_interp_init ();
+#endif
+
mono_os_mutex_init_recursive (&jit_mutex);
mono_cross_helpers_run ();
mini_jit_init ();
+ mini_jit_init_job_control ();
+
/* Happens when using the embedding interface */
if (!default_opt_set)
default_opt = mono_parse_default_optimizations (NULL);
mono_w32handle_init ();
#endif
- mono_threads_runtime_init (&ticallbacks);
+ mono_thread_info_runtime_init (&ticallbacks);
if (g_hasenv ("MONO_DEBUG")) {
mini_parse_debug_options ();
mono_set_generic_sharing_supported (TRUE);
#endif
- mono_threads_signals_init ();
+ mono_thread_info_signals_init ();
#ifndef MONO_CROSS_COMPILE
mono_runtime_install_handlers ();
mono_install_get_class_from_name (mono_aot_get_class_from_name);
mono_install_jit_info_find_in_aot (mono_aot_find_jit_info);
- if (mini_profiler_enabled ()) {
- mono_profiler_load (mini_profiler_get_options ());
- mono_profiler_thread_name (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()), "Main");
- }
+ mono_profiler_state.context_enable = mini_profiler_context_enable;
+ mono_profiler_state.context_get_this = mini_profiler_context_get_this;
+ mono_profiler_state.context_get_argument = mini_profiler_context_get_argument;
+ mono_profiler_state.context_get_local = mini_profiler_context_get_local;
+ mono_profiler_state.context_get_result = mini_profiler_context_get_result;
+ mono_profiler_state.context_free_buffer = mini_profiler_context_free_buffer;
+
+ if (profile_options)
+ for (guint i = 0; i < profile_options->len; i++)
+ mono_profiler_load ((const char *) g_ptr_array_index (profile_options, i));
+
+ mono_profiler_started ();
if (debug_options.collect_pagefault_stats)
mono_aot_set_make_unreadable (TRUE);
mono_runtime_init_checked (domain, mono_thread_start_cb, mono_thread_attach_cb, &error);
mono_error_assert_ok (&error);
mono_thread_attach (domain);
+ MONO_PROFILER_RAISE (thread_name, (MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ()), "Main"));
#endif
- if (mono_profiler_get_events () & MONO_PROFILE_STATISTICAL)
+ if (mono_profiler_sampling_enabled ())
mono_runtime_setup_stat_profiler ();
- mono_profiler_runtime_initialized ();
+ MONO_PROFILER_RAISE (runtime_initialized, ());
MONO_VES_INIT_END ();
mono_add_internal_call ("Mono.Runtime::mono_runtime_cleanup_handlers",
mono_runtime_cleanup_handlers);
-#if defined(PLATFORM_ANDROID) || defined(TARGET_ANDROID)
+#if defined(HOST_ANDROID) || defined(TARGET_ANDROID)
mono_add_internal_call ("System.Diagnostics.Debugger::Mono_UnhandledException_internal",
mono_debugger_agent_unhandled_exception);
#endif
* the wrapper would call the icall which would call the wrapper and
* so on.
*/
- register_icall (mono_profiler_method_enter, "mono_profiler_method_enter", "void ptr", TRUE);
- register_icall (mono_profiler_method_leave, "mono_profiler_method_leave", "void ptr", TRUE);
+ register_icall (mono_profiler_raise_method_enter, "mono_profiler_raise_method_enter", "void ptr ptr", TRUE);
+ register_icall (mono_profiler_raise_method_leave, "mono_profiler_raise_method_leave", "void ptr ptr", TRUE);
+ register_icall (mono_profiler_raise_method_tail_call, "mono_profiler_raise_method_tail_call", "void ptr ptr", TRUE);
register_icall (mono_trace_enter_method, "mono_trace_enter_method", NULL, TRUE);
register_icall (mono_trace_leave_method, "mono_trace_leave_method", NULL, TRUE);
register_dyn_icall (mono_get_rethrow_exception (), "mono_arch_rethrow_exception", "void object", TRUE);
register_dyn_icall (mono_get_throw_corlib_exception (), "mono_arch_throw_corlib_exception", "void ptr", TRUE);
register_icall (mono_thread_get_undeniable_exception, "mono_thread_get_undeniable_exception", "object", FALSE);
+ register_icall (mono_thread_self_abort, "mono_thread_self_abort", "void", FALSE);
register_icall (mono_thread_interruption_checkpoint, "mono_thread_interruption_checkpoint", "object", FALSE);
register_icall (mono_thread_force_interruption_checkpoint_noraise, "mono_thread_force_interruption_checkpoint_noraise", "object", FALSE);
-#if defined(__native_client__) || defined(__native_client_codegen__)
- register_icall (mono_nacl_gc, "mono_nacl_gc", "void", FALSE);
-#endif
-
if (mono_threads_is_coop_enabled ())
register_icall (mono_threads_state_poll, "mono_threads_state_poll", "void", FALSE);
register_opcode_emulation (OP_LCONV_TO_R_UN, "__emul_lconv_to_r8_un", "double long", mono_lconv_to_r8_un, "mono_lconv_to_r8_un", FALSE);
#endif
#ifdef MONO_ARCH_EMULATE_FREM
-#if !defined(__native_client__)
register_opcode_emulation (OP_FREM, "__emul_frem", "double double double", fmod, "fmod", FALSE);
register_opcode_emulation (OP_RREM, "__emul_rrem", "float float float", fmodf, "fmodf", FALSE);
-#else
- register_opcode_emulation (OP_FREM, "__emul_frem", "double double double", mono_fmod, "mono_fmod", FALSE);
-#endif
#endif
#ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
register_icall (mono_gsharedvt_constrained_call, "mono_gsharedvt_constrained_call", "object ptr ptr ptr ptr ptr", FALSE);
register_icall (mono_gsharedvt_value_copy, "mono_gsharedvt_value_copy", "void ptr ptr ptr", TRUE);
- register_icall (mono_gc_wbarrier_value_copy_bitmap, "mono_gc_wbarrier_value_copy_bitmap", "void ptr ptr int int", FALSE);
+ //WARNING We do runtime selection here but the string *MUST* be to a fallback function that has same signature and behavior
+ register_icall_no_wrapper (mono_gc_get_range_copy_func (), "mono_gc_wbarrier_range_copy", "void ptr ptr int");
register_icall (mono_object_castclass_with_cache, "mono_object_castclass_with_cache", "object object ptr ptr", FALSE);
register_icall (mono_object_isinst_with_cache, "mono_object_isinst_with_cache", "object object ptr ptr", FALSE);
MonoJitStats mono_jit_stats = {0};
+/**
+ * Counters of mono_stats and mono_jit_stats can be read without locking here.
+ * MONO_NO_SANITIZE_THREAD tells Clang's ThreadSanitizer to hide all reports of these (known) races.
+ */
+MONO_NO_SANITIZE_THREAD
static void
print_jit_stats (void)
{
if (mono_jit_stats.enabled) {
g_print ("Mono Jit statistics\n");
- g_print ("Max code size ratio: %.2f (%s)\n", mono_jit_stats.max_code_size_ratio/100.0,
+ g_print ("Max code size ratio: %.2f (%s)\n", mono_jit_stats.max_code_size_ratio / 100.0,
mono_jit_stats.max_ratio_method);
- g_print ("Biggest method: %ld (%s)\n", mono_jit_stats.biggest_method_size,
+ g_print ("Biggest method: %" G_GINT32_FORMAT " (%s)\n", mono_jit_stats.biggest_method_size,
mono_jit_stats.biggest_method);
- g_print ("Delegates created: %ld\n", mono_stats.delegate_creations);
- g_print ("Initialized classes: %ld\n", mono_stats.initialized_class_count);
- g_print ("Used classes: %ld\n", mono_stats.used_class_count);
- g_print ("Generic vtables: %ld\n", mono_stats.generic_vtable_count);
- g_print ("Methods: %ld\n", mono_stats.method_count);
- g_print ("Static data size: %ld\n", mono_stats.class_static_data_size);
- g_print ("VTable data size: %ld\n", mono_stats.class_vtable_size);
+ g_print ("Delegates created: %" G_GINT32_FORMAT "\n", mono_stats.delegate_creations);
+ g_print ("Initialized classes: %" G_GINT32_FORMAT "\n", mono_stats.initialized_class_count);
+ g_print ("Used classes: %" G_GINT32_FORMAT "\n", mono_stats.used_class_count);
+ g_print ("Generic vtables: %" G_GINT32_FORMAT "\n", mono_stats.generic_vtable_count);
+ g_print ("Methods: %" G_GINT32_FORMAT "\n", mono_stats.method_count);
+ g_print ("Static data size: %" G_GINT32_FORMAT "\n", mono_stats.class_static_data_size);
+ g_print ("VTable data size: %" G_GINT32_FORMAT "\n", mono_stats.class_vtable_size);
g_print ("Mscorlib mempool size: %d\n", mono_mempool_get_allocated (mono_defaults.corlib->mempool));
- g_print ("\nInitialized classes: %ld\n", mono_stats.generic_class_count);
- g_print ("Inflated types: %ld\n", mono_stats.inflated_type_count);
+ g_print ("\nInitialized classes: %" G_GINT32_FORMAT "\n", mono_stats.generic_class_count);
+ g_print ("Inflated types: %" G_GINT32_FORMAT "\n", mono_stats.inflated_type_count);
g_print ("Generics virtual invokes: %ld\n", mono_jit_stats.generic_virtual_invocations);
- g_print ("Sharable generic methods: %ld\n", mono_stats.generics_sharable_methods);
- g_print ("Unsharable generic methods: %ld\n", mono_stats.generics_unsharable_methods);
- g_print ("Shared generic methods: %ld\n", mono_stats.generics_shared_methods);
- g_print ("Shared vtype generic methods: %ld\n", mono_stats.gsharedvt_methods);
+ g_print ("Sharable generic methods: %" G_GINT32_FORMAT "\n", mono_stats.generics_sharable_methods);
+ g_print ("Unsharable generic methods: %" G_GINT32_FORMAT "\n", mono_stats.generics_unsharable_methods);
+ g_print ("Shared generic methods: %" G_GINT32_FORMAT "\n", mono_stats.generics_shared_methods);
+ g_print ("Shared vtype generic methods: %" G_GINT32_FORMAT "\n", mono_stats.gsharedvt_methods);
- g_print ("IMT tables size: %ld\n", mono_stats.imt_tables_size);
- g_print ("IMT number of tables: %ld\n", mono_stats.imt_number_of_tables);
- g_print ("IMT number of methods: %ld\n", mono_stats.imt_number_of_methods);
- g_print ("IMT used slots: %ld\n", mono_stats.imt_used_slots);
- g_print ("IMT colliding slots: %ld\n", mono_stats.imt_slots_with_collisions);
- g_print ("IMT max collisions: %ld\n", mono_stats.imt_max_collisions_in_slot);
- g_print ("IMT methods at max col: %ld\n", mono_stats.imt_method_count_when_max_collisions);
- g_print ("IMT trampolines size: %ld\n", mono_stats.imt_trampolines_size);
+ g_print ("IMT tables size: %" G_GINT32_FORMAT "\n", mono_stats.imt_tables_size);
+ g_print ("IMT number of tables: %" G_GINT32_FORMAT "\n", mono_stats.imt_number_of_tables);
+ g_print ("IMT number of methods: %" G_GINT32_FORMAT "\n", mono_stats.imt_number_of_methods);
+ g_print ("IMT used slots: %" G_GINT32_FORMAT "\n", mono_stats.imt_used_slots);
+ g_print ("IMT colliding slots: %" G_GINT32_FORMAT "\n", mono_stats.imt_slots_with_collisions);
+ g_print ("IMT max collisions: %" G_GINT32_FORMAT "\n", mono_stats.imt_max_collisions_in_slot);
+ g_print ("IMT methods at max col: %" G_GINT32_FORMAT "\n", mono_stats.imt_method_count_when_max_collisions);
+ g_print ("IMT trampolines size: %" G_GINT32_FORMAT "\n", mono_stats.imt_trampolines_size);
- g_print ("JIT info table inserts: %ld\n", mono_stats.jit_info_table_insert_count);
- g_print ("JIT info table removes: %ld\n", mono_stats.jit_info_table_remove_count);
- g_print ("JIT info table lookups: %ld\n", mono_stats.jit_info_table_lookup_count);
+ g_print ("JIT info table inserts: %" G_GINT32_FORMAT "\n", mono_stats.jit_info_table_insert_count);
+ g_print ("JIT info table removes: %" G_GINT32_FORMAT "\n", mono_stats.jit_info_table_remove_count);
+ g_print ("JIT info table lookups: %" G_GINT32_FORMAT "\n", mono_stats.jit_info_table_lookup_count);
g_free (mono_jit_stats.max_ratio_method);
mono_jit_stats.max_ratio_method = NULL;
void
mini_cleanup (MonoDomain *domain)
{
- if (mono_profiler_get_events () & MONO_PROFILE_STATISTICAL)
+ if (mono_profiler_sampling_enabled ())
mono_runtime_shutdown_stat_profiler ();
+ MONO_PROFILER_RAISE (runtime_shutdown_begin, ());
+
#ifndef DISABLE_COM
cominterop_release_all_rcws ();
#endif
mono_threadpool_cleanup ();
- mono_profiler_shutdown ();
+ MONO_PROFILER_RAISE (runtime_shutdown_end, ());
+
+ mono_profiler_cleanup ();
+
+ if (profile_options)
+ g_ptr_array_free (profile_options, TRUE);
free_jit_tls_data ((MonoJitTlsData *)mono_tls_get_jit_tls ());
g_assert_not_reached ();
}
+
+static MonoBreakPolicy
+always_insert_breakpoint (MonoMethod *method)
+{
+ return MONO_BREAK_POLICY_ALWAYS;
+}
+
+static MonoBreakPolicyFunc break_policy_func = always_insert_breakpoint;
+
+/**
+ * mono_set_break_policy:
+ * \param policy_callback the new callback function
+ *
+ * Allow embedders to decide whether to actually obey breakpoint instructions
+ * (both break IL instructions and \c Debugger.Break method calls), for example
+ * to not allow an app to be aborted by a perfectly valid IL opcode when executing
+ * untrusted or semi-trusted code.
+ *
+ * \p policy_callback will be called every time a break point instruction needs to
+ * be inserted with the method argument being the method that calls \c Debugger.Break
+ * or has the IL \c break instruction. The callback should return \c MONO_BREAK_POLICY_NEVER
+ * if it wants the breakpoint to not be effective in the given method.
+ * \c MONO_BREAK_POLICY_ALWAYS is the default.
+ */
+void
+mono_set_break_policy (MonoBreakPolicyFunc policy_callback)
+{
+ if (policy_callback)
+ break_policy_func = policy_callback;
+ else
+ break_policy_func = always_insert_breakpoint;
+}
+
+gboolean
+mini_should_insert_breakpoint (MonoMethod *method)
+{
+ switch (break_policy_func (method)) {
+ case MONO_BREAK_POLICY_ALWAYS:
+ return TRUE;
+ case MONO_BREAK_POLICY_NEVER:
+ return FALSE;
+ case MONO_BREAK_POLICY_ON_DBG:
+ g_warning ("mdb no longer supported");
+ return FALSE;
+ default:
+ g_warning ("Incorrect value returned from break policy callback");
+ return FALSE;
+ }
+}
+
// Custom handlers currently only implemented by Windows.
#ifndef HOST_WIN32
gboolean