[runtime] Coverage profiler fixes (#5698)
[mono.git] / mono / metadata / profiler.c
index ccdb022a14fda51e6538da06f85329867d83e6eb..9f65059f806699088b673fcf01a1896ae59a7576 100644 (file)
@@ -9,6 +9,7 @@
 #include <mono/metadata/mono-config-dirs.h>
 #include <mono/metadata/mono-debug.h>
 #include <mono/metadata/profiler-private.h>
+#include <mono/metadata/debug-internals.h>
 #include <mono/utils/mono-dl.h>
 #include <mono/utils/mono-error-internals.h>
 #include <mono/utils/mono-logger-internals.h>
@@ -157,23 +158,31 @@ mono_profiler_create (MonoProfiler *prof)
        return handle;
 }
 
+void
+mono_profiler_set_cleanup_callback (MonoProfilerHandle handle, MonoProfilerCleanupCallback cb)
+{
+       InterlockedWritePointer (&handle->cleanup_callback, (gpointer) cb);
+}
+
 void
 mono_profiler_set_coverage_filter_callback (MonoProfilerHandle handle, MonoProfilerCoverageFilterCallback cb)
 {
        InterlockedWritePointer (&handle->coverage_filter, (gpointer) cb);
 }
 
-static void
-initialize_coverage (void)
+mono_bool
+mono_profiler_enable_coverage (void)
 {
+       if (mono_profiler_state.startup_done)
+               return FALSE;
+
        mono_os_mutex_init (&mono_profiler_state.coverage_mutex);
        mono_profiler_state.coverage_hash = g_hash_table_new (NULL, NULL);
-}
 
-static void
-lazy_initialize_coverage (void)
-{
-       mono_lazy_initialize (&mono_profiler_state.coverage_status, initialize_coverage);
+       if (!mono_debug_enabled ())
+               mono_debug_init (MONO_DEBUG_FORMAT_MONO);
+
+       return mono_profiler_state.code_coverage = TRUE;
 }
 
 static void
@@ -188,10 +197,11 @@ coverage_unlock (void)
        mono_os_mutex_unlock (&mono_profiler_state.coverage_mutex);
 }
 
-void
+mono_bool
 mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method, MonoProfilerCoverageCallback cb)
 {
-       lazy_initialize_coverage ();
+       if (!mono_profiler_state.code_coverage)
+               return FALSE;
 
        coverage_lock ();
 
@@ -199,9 +209,6 @@ mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method,
 
        coverage_unlock ();
 
-       if (!info)
-               return;
-
        MonoError error;
        MonoMethodHeader *header = mono_method_get_header_checked (method, &error);
        mono_error_assert_ok (&error);
@@ -209,9 +216,48 @@ mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method,
        guint32 size;
 
        const unsigned char *start = mono_method_header_get_code (header, &size, NULL);
-       const unsigned char *end = start - size;
+       const unsigned char *end = start + size;
        MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method);
 
+       if (!info) {
+               char *source_file;
+               int i, n_il_offsets;
+               int *source_files;
+               GPtrArray *source_file_list;
+               MonoSymSeqPoint *sym_seq_points;
+
+               /* Return 0 counts for all locations */
+
+               mono_debug_get_seq_points (minfo, &source_file, &source_file_list, &source_files, &sym_seq_points, &n_il_offsets);
+               for (i = 0; i < n_il_offsets; ++i) {
+                       MonoSymSeqPoint *sp = &sym_seq_points [i];
+                       const char *srcfile = "";
+
+                       if (source_files [i] != -1) {
+                               MonoDebugSourceInfo *sinfo = (MonoDebugSourceInfo *)g_ptr_array_index (source_file_list, source_files [i]);
+                               srcfile = sinfo->source_file;
+                       }
+
+                       MonoProfilerCoverageData data = {
+                               .method = method,
+                               .il_offset = sp->il_offset,
+                               .counter = 0,
+                               .file_name = srcfile,
+                               .line = sp->line,
+                               .column = 0,
+                       };
+
+                       cb (handle->prof, &data);
+               }
+
+               g_free (source_files);
+               g_free (sym_seq_points);
+               g_ptr_array_free (source_file_list, TRUE);
+
+               mono_metadata_free_mh (header);
+               return TRUE;
+       }
+
        for (guint32 i = 0; i < info->entries; i++) {
                guchar *cil_code = info->data [i].cil_code;
 
@@ -245,12 +291,18 @@ mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method,
        }
 
        mono_metadata_free_mh (header);
+
+       return TRUE;
 }
 
 MonoProfilerCoverageInfo *
 mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries)
 {
-       lazy_initialize_coverage ();
+       if (!mono_profiler_state.code_coverage)
+               return FALSE;
+
+       if (method->wrapper_type)
+               return FALSE;
 
        gboolean cover = FALSE;
 
@@ -277,23 +329,6 @@ mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries)
        return info;
 }
 
-void
-mono_profiler_coverage_free (MonoMethod *method)
-{
-       lazy_initialize_coverage ();
-
-       coverage_lock ();
-
-       MonoProfilerCoverageInfo *info = g_hash_table_lookup (mono_profiler_state.coverage_hash, method);
-
-       if (info) {
-               g_hash_table_remove (mono_profiler_state.coverage_hash, method);
-               g_free (info);
-       }
-
-       coverage_unlock ();
-}
-
 mono_bool
 mono_profiler_enable_sampling (MonoProfilerHandle handle)
 {
@@ -361,9 +396,7 @@ mono_profiler_enable_allocations (void)
        if (mono_profiler_state.startup_done)
                return FALSE;
 
-       mono_profiler_state.allocations = TRUE;
-
-       return TRUE;
+       return mono_profiler_state.allocations = TRUE;
 }
 
 void
@@ -372,8 +405,61 @@ mono_profiler_set_call_instrumentation_filter_callback (MonoProfilerHandle handl
        InterlockedWritePointer (&handle->call_instrumentation_filter, (gpointer) cb);
 }
 
-gboolean
-mono_profiler_should_instrument_method (MonoMethod *method, gboolean entry)
+mono_bool
+mono_profiler_enable_call_context_introspection (void)
+{
+       if (mono_profiler_state.startup_done)
+               return FALSE;
+
+       mono_profiler_state.context_enable ();
+
+       return mono_profiler_state.call_contexts = TRUE;
+}
+
+void *
+mono_profiler_call_context_get_this (MonoProfilerCallContext *context)
+{
+       if (!mono_profiler_state.call_contexts)
+               return NULL;
+
+       return mono_profiler_state.context_get_this (context);
+}
+
+void *
+mono_profiler_call_context_get_argument (MonoProfilerCallContext *context, uint32_t position)
+{
+       if (!mono_profiler_state.call_contexts)
+               return NULL;
+
+       return mono_profiler_state.context_get_argument (context, position);
+}
+
+void *
+mono_profiler_call_context_get_local (MonoProfilerCallContext *context, uint32_t position)
+{
+       if (!mono_profiler_state.call_contexts)
+               return NULL;
+
+       return mono_profiler_state.context_get_local (context, position);
+}
+
+void *
+mono_profiler_call_context_get_result (MonoProfilerCallContext *context)
+{
+       if (!mono_profiler_state.call_contexts)
+               return NULL;
+
+       return mono_profiler_state.context_get_result (context);
+}
+
+void
+mono_profiler_call_context_free_buffer (void *buffer)
+{
+       mono_profiler_state.context_free_buffer (buffer);
+}
+
+MonoProfilerCallInstrumentationFlags
+mono_profiler_get_call_instrumentation_flags (MonoMethod *method)
 {
        MonoProfilerCallInstrumentationFlags flags = MONO_PROFILER_CALL_INSTRUMENTATION_NONE;
 
@@ -384,10 +470,7 @@ mono_profiler_should_instrument_method (MonoMethod *method, gboolean entry)
                        flags |= cb (handle->prof, method);
        }
 
-       if (entry)
-               return flags & MONO_PROFILER_CALL_INSTRUMENTATION_PROLOGUE;
-       else
-               return flags & MONO_PROFILER_CALL_INSTRUMENTATION_EPILOGUE;
+       return flags;
 }
 
 void
@@ -441,6 +524,38 @@ mono_profiler_cleanup (void)
 #undef MONO_PROFILER_EVENT_3
 #undef MONO_PROFILER_EVENT_4
 #undef _MONO_PROFILER_EVENT
+
+       MonoProfilerHandle head = mono_profiler_state.profilers;
+
+       while (head) {
+               MonoProfilerCleanupCallback cb = head->cleanup_callback;
+
+               if (cb)
+                       cb (head->prof);
+
+               MonoProfilerHandle cur = head;
+               head = head->next;
+
+               g_free (cur);
+       }
+
+       if (mono_profiler_state.code_coverage) {
+               mono_os_mutex_destroy (&mono_profiler_state.coverage_mutex);
+
+               GHashTableIter iter;
+
+               g_hash_table_iter_init (&iter, mono_profiler_state.coverage_hash);
+
+               MonoProfilerCoverageInfo *info;
+
+               while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info))
+                       g_free (info);
+
+               g_hash_table_destroy (mono_profiler_state.coverage_hash);
+       }
+
+       if (mono_profiler_state.sampling_owner)
+               mono_os_sem_destroy (&mono_profiler_state.sampling_semaphore);
 }
 
 static void
@@ -519,3 +634,134 @@ update_callback (volatile gpointer *location, gpointer new_, volatile gint32 *co
 #undef MONO_PROFILER_EVENT_3
 #undef MONO_PROFILER_EVENT_4
 #undef _MONO_PROFILER_EVENT
+
+/*
+ * The following code is here to maintain compatibility with a few profiler API
+ * functions used by Xamarin.{Android,iOS,Mac} so that they keep working
+ * regardless of which system Mono version is used.
+ *
+ * TODO: Remove this some day if we're OK with breaking compatibility.
+ */
+
+typedef void *MonoLegacyProfiler;
+
+typedef void (*MonoLegacyProfileFunc) (MonoLegacyProfiler *prof);
+typedef void (*MonoLegacyProfileThreadFunc) (MonoLegacyProfiler *prof, uintptr_t tid);
+typedef void (*MonoLegacyProfileGCFunc) (MonoLegacyProfiler *prof, MonoProfilerGCEvent event, int generation);
+typedef void (*MonoLegacyProfileGCResizeFunc) (MonoLegacyProfiler *prof, int64_t new_size);
+typedef void (*MonoLegacyProfileJitResult) (MonoLegacyProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result);
+
+struct _MonoProfiler {
+       MonoProfilerHandle handle;
+       MonoLegacyProfiler *profiler;
+       MonoLegacyProfileFunc shutdown_callback;
+       MonoLegacyProfileThreadFunc thread_start, thread_end;
+       MonoLegacyProfileGCFunc gc_event;
+       MonoLegacyProfileGCResizeFunc gc_heap_resize;
+       MonoLegacyProfileJitResult jit_end2;
+};
+
+static MonoProfiler *current;
+
+MONO_API void mono_profiler_install (MonoLegacyProfiler *prof, MonoLegacyProfileFunc callback);
+MONO_API void mono_profiler_install_thread (MonoLegacyProfileThreadFunc start, MonoLegacyProfileThreadFunc end);
+MONO_API void mono_profiler_install_gc (MonoLegacyProfileGCFunc callback, MonoLegacyProfileGCResizeFunc heap_resize_callback);
+MONO_API void mono_profiler_install_jit_end (MonoLegacyProfileJitResult end);
+MONO_API void mono_profiler_set_events (int flags);
+
+static void
+shutdown_cb (MonoProfiler *prof)
+{
+       prof->shutdown_callback (prof->profiler);
+}
+
+void
+mono_profiler_install (MonoLegacyProfiler *prof, MonoLegacyProfileFunc callback)
+{
+       current = g_new0 (MonoProfiler, 1);
+       current->handle = mono_profiler_create (current);
+       current->profiler = prof;
+       current->shutdown_callback = callback;
+
+       if (callback)
+               mono_profiler_set_runtime_shutdown_end_callback (current->handle, shutdown_cb);
+}
+
+static void
+thread_start_cb (MonoProfiler *prof, uintptr_t tid)
+{
+       prof->thread_start (prof->profiler, tid);
+}
+
+static void
+thread_stop_cb (MonoProfiler *prof, uintptr_t tid)
+{
+       prof->thread_end (prof->profiler, tid);
+}
+
+void
+mono_profiler_install_thread (MonoLegacyProfileThreadFunc start, MonoLegacyProfileThreadFunc end)
+{
+       current->thread_start = start;
+       current->thread_end = end;
+
+       if (start)
+               mono_profiler_set_thread_started_callback (current->handle, thread_start_cb);
+
+       if (end)
+               mono_profiler_set_thread_stopped_callback (current->handle, thread_stop_cb);
+}
+
+static void
+gc_event_cb (MonoProfiler *prof, MonoProfilerGCEvent event, uint32_t generation)
+{
+       prof->gc_event (prof->profiler, event, generation);
+}
+
+static void
+gc_resize_cb (MonoProfiler *prof, uintptr_t size)
+{
+       prof->gc_heap_resize (prof->profiler, size);
+}
+
+void
+mono_profiler_install_gc (MonoLegacyProfileGCFunc callback, MonoLegacyProfileGCResizeFunc heap_resize_callback)
+{
+       current->gc_event = callback;
+       current->gc_heap_resize = heap_resize_callback;
+
+       if (callback)
+               mono_profiler_set_gc_event_callback (current->handle, gc_event_cb);
+
+       if (heap_resize_callback)
+               mono_profiler_set_gc_resize_callback (current->handle, gc_resize_cb);
+}
+
+static void
+jit_done_cb (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo)
+{
+       prof->jit_end2 (prof->profiler, method, jinfo, 0);
+}
+
+static void
+jit_failed_cb (MonoProfiler *prof, MonoMethod *method)
+{
+       prof->jit_end2 (prof->profiler, method, NULL, 1);
+}
+
+void
+mono_profiler_install_jit_end (MonoLegacyProfileJitResult end)
+{
+       current->jit_end2 = end;
+
+       if (end) {
+               mono_profiler_set_jit_done_callback (current->handle, jit_done_cb);
+               mono_profiler_set_jit_failed_callback (current->handle, jit_failed_cb);
+       }
+}
+
+void
+mono_profiler_set_events (int flags)
+{
+       /* Do nothing. */
+}