Interp sdb (#4911)
[mono.git] / mono / mini / mini-runtime.c
index 1fcab2f3e1fb97ef03fe17151fb5324bb887c049..7d427a915ee53263ccff6c19689782a15c5c48d7 100644 (file)
@@ -484,7 +484,11 @@ mono_tramp_info_register_internal (MonoTrampInfo *info, MonoDomain *domain, gboo
        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);
@@ -492,16 +496,19 @@ mono_tramp_info_register_internal (MonoTrampInfo *info, MonoDomain *domain, gboo
        if (info->unwind_ops) {
                copy->uw_info = mono_unwind_ops_encode (info->unwind_ops, &copy->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);
 
@@ -510,9 +517,15 @@ mono_tramp_info_register_internal (MonoTrampInfo *info, MonoDomain *domain, gboo
                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);
@@ -570,23 +583,23 @@ break_count (void)
 G_GNUC_UNUSED gboolean
 mono_debug_count (void)
 {
-       static int count = 0;
+       static int count = 0, int_val = 0;
        static gboolean inited;
-       static char *value;
 
        count ++;
 
        if (!inited) {
-               value = g_getenv ("COUNT");
+               char *value = g_getenv ("COUNT");
+               if (value) {
+                       int_val = atoi (value);
+                       g_free (value);
+               }
                inited = TRUE;
        }
 
-       if (!value)
+       if (!int_val)
                return TRUE;
 
-       int int_val = atoi (value);
-       g_free (value);
-
        if (count == int_val)
                break_count ();
 
@@ -791,6 +804,38 @@ mono_set_lmf_addr (gpointer lmf_addr)
                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:
  *
@@ -1760,9 +1805,6 @@ no_gsharedvt_in_wrapper (void)
 /*
 Overall algorithm:
 
-Limit JITing to mono_cpu_count
-       This ensures there's always room for application progress and not just JITing.
-
 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.
@@ -1770,22 +1812,25 @@ When a JIT request is made, we check if there's an outstanding one for that meth
 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 invocatio from within the JIT, it increases JIT duration and complicates things A LOT.
-       Verify that we don't have too many spurious wakeups.
-       Experiment with limiting to values around mono_cpu_count +/- 1 as this would enable progress during warmup.
+       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 count;
+       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*
-       int active_threads;
-       int threads_waiting;
        MonoCoopMutex lock;
-       MonoCoopCond cond;
 } JitCompilationData;
 
 static JitCompilationData compilation_data;
@@ -1795,7 +1840,6 @@ static void
 mini_jit_init_job_control (void)
 {
        mono_coop_mutex_init (&compilation_data.lock);
-       mono_coop_cond_init (&compilation_data.cond);
        compilation_data.in_flight_methods = g_ptr_array_new ();
 }
 
@@ -1827,27 +1871,18 @@ find_method (MonoMethod *method, MonoDomain *domain)
 static void
 add_current_thread (MonoJitTlsData *jit_tls)
 {
-       if (jit_tls->active_jit_methods == 0)
-               ++compilation_data.active_threads;
        ++jit_tls->active_jit_methods;
 }
 
-//Returns true if this thread should wait
-static gboolean
-should_wait_for_available_cpu_capacity (void)
+static void
+unref_jit_entry (JitCompilationEntry *entry)
 {
-       MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
-
-       //We can't suspend threads that are already JIT'ing something or we risk deadlocking
-       if (jit_tls->active_jit_methods > 0)
-               return FALSE;
-
-       //If there are as many active threads as cores, JITing more will cause thrashing
-       if (compilation_data.active_threads >= mono_cpu_count ()) {
-               ++jit_methods_overload;
-               return TRUE;
-       }
-       return FALSE;
+       --entry->ref_count;
+       if (entry->ref_count)
+               return;
+       if (entry->has_cond)
+               mono_coop_cond_destroy (&entry->cond);
+       g_free (entry);
 }
 
 /*
@@ -1870,23 +1905,13 @@ wait_or_register_method_to_compile (MonoMethod *method, MonoDomain *domain)
 
        lock_compilation_data ();
 
-       int waits = 0;
-       while (should_wait_for_available_cpu_capacity ()) {
-               fflush (stdout);
-               ++compilation_data.threads_waiting;
-               mono_coop_cond_wait (&compilation_data.cond, &compilation_data.lock);
-               --compilation_data.threads_waiting;
-               if (waits)
-                       ++jit_spurious_wakeups;
-               ++waits;
-       }
-
        if (!(entry = find_method (method, domain))) {
-               entry = g_new (JitCompilationEntry, 1);
+               entry = g_new0 (JitCompilationEntry, 1);
                entry->method = method;
                entry->domain = domain;
-               entry->count = 1;
+               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 ();
@@ -1894,7 +1919,7 @@ wait_or_register_method_to_compile (MonoMethod *method, MonoDomain *domain)
        } 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->count;
+               ++entry->compilation_count;
                ++jit_methods_multiple;
                ++jit_tls->active_jit_methods;
 
@@ -1902,13 +1927,22 @@ wait_or_register_method_to_compile (MonoMethod *method, MonoDomain *domain)
                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) {
-                       fflush (stdout);
-                       ++compilation_data.threads_waiting;
-                       mono_coop_cond_wait (&compilation_data.cond, &compilation_data.lock);
-                       --compilation_data.threads_waiting;
+                       ++entry->threads_waiting;
+
+                       g_assert (entry->has_cond);
+                       mono_coop_cond_wait (&entry->cond, &compilation_data.lock);
+                       --entry->threads_waiting;
 
-                       if (!find_method (method, domain)) {
+                       if (entry->done) {
+                               unref_jit_entry (entry);
                                unlock_compilation_data ();
                                return TRUE;
                        } else {
@@ -1927,18 +1961,21 @@ unregister_method_for_compile (MonoMethod *method, MonoDomain *target_domain)
 
        g_assert (jit_tls->active_jit_methods > 0);
        --jit_tls->active_jit_methods;
-       if (jit_tls->active_jit_methods == 0)
-               --compilation_data.active_threads;
 
        JitCompilationEntry *entry = find_method (method, target_domain);
        g_assert (entry); // It would be weird to fail
-       if (--entry->count == 0) {
+       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);
-               g_free (entry);
+               unref_jit_entry (entry);
        }
 
-       if (compilation_data.threads_waiting)
-               mono_coop_cond_broadcast (&compilation_data.cond);
        unlock_compilation_data ();
 }
 
@@ -3437,19 +3474,14 @@ mini_get_debug_options (void)
 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));
 
@@ -3465,7 +3497,7 @@ mini_create_ftnptr (MonoDomain *domain, gpointer addr)
 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;
@@ -3649,6 +3681,7 @@ mini_free_jit_domain_info (MonoDomain *domain)
                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
@@ -3737,6 +3770,10 @@ mini_init (const char *filename, const char *runtime_version)
        }
 #endif
 
+#ifdef ENABLE_INTERPRETER
+       mono_interp_init ();
+#endif
+
        mono_os_mutex_init_recursive (&jit_mutex);
 
        mono_cross_helpers_run ();
@@ -4201,7 +4238,7 @@ register_icalls (void)
        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);
+       register_icall_no_wrapper (mono_gc_get_range_copy_func (), "mono_gc_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);
@@ -4510,6 +4547,56 @@ mono_personality (void)
        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