[profiler] Add GC finalization callbacks to the profiler API.
[mono.git] / mono / metadata / profiler.c
index d74a273d085bf0825656642a845a683722b5ea89..fb4cdeca08b8632bf1f746c8ea5bda5b26a1517a 100644 (file)
@@ -102,6 +102,11 @@ struct _ProfilerDesc {
        MonoProfileGCHandleFunc  gc_handle;
        MonoProfileGCRootFunc    gc_roots;
 
+       MonoProfileGCFinalizeFunc gc_finalize_begin;
+       MonoProfileGCFinalizeObjectFunc gc_finalize_object_begin;
+       MonoProfileGCFinalizeObjectFunc gc_finalize_object_end;
+       MonoProfileGCFinalizeFunc gc_finalize_end;
+
        MonoProfileFunc          runtime_initialized_event;
 
        MonoProfilerCodeChunkNew code_chunk_new;
@@ -283,7 +288,7 @@ mono_profiler_install_monitor  (MonoProfileMonitorFunc callback)
 }
 
 static MonoProfileSamplingMode sampling_mode = MONO_PROFILER_STAT_MODE_PROCESS;
-static int64_t sampling_frequency = 1000; //1ms
+static int64_t sampling_frequency = 100; // Hz
 
 /**
  * mono_profiler_set_statistical_mode:
@@ -298,10 +303,10 @@ static int64_t sampling_frequency = 1000; //1ms
  * Said that, when using statistical sampling, always assume variable rate sampling as all sort of external factors can interfere.
  */
 void
-mono_profiler_set_statistical_mode (MonoProfileSamplingMode mode, int64_t sampling_frequency_is_us)
+mono_profiler_set_statistical_mode (MonoProfileSamplingMode mode, int64_t sampling_frequency_hz)
 {
        sampling_mode = mode;
-       sampling_frequency = sampling_frequency_is_us;
+       sampling_frequency = sampling_frequency_hz;
 }
 
 void 
@@ -925,6 +930,50 @@ mono_profiler_install_gc_roots (MonoProfileGCHandleFunc handle_callback, MonoPro
        prof_list->gc_roots = roots_callback;
 }
 
+void
+mono_profiler_gc_finalize_begin (void)
+{
+       for (ProfilerDesc *prof = prof_list; prof; prof = prof->next)
+               if ((prof->events & MONO_PROFILE_GC_FINALIZATION) && prof->gc_finalize_begin)
+                       prof->gc_finalize_begin (prof->profiler);
+}
+
+void
+mono_profiler_gc_finalize_object_begin (MonoObject *obj)
+{
+       for (ProfilerDesc *prof = prof_list; prof; prof = prof->next)
+               if ((prof->events & MONO_PROFILE_GC_FINALIZATION) && prof->gc_finalize_object_begin)
+                       prof->gc_finalize_object_begin (prof->profiler, obj);
+}
+
+void
+mono_profiler_gc_finalize_object_end (MonoObject *obj)
+{
+       for (ProfilerDesc *prof = prof_list; prof; prof = prof->next)
+               if ((prof->events & MONO_PROFILE_GC_FINALIZATION) && prof->gc_finalize_object_end)
+                       prof->gc_finalize_object_end (prof->profiler, obj);
+}
+
+void
+mono_profiler_gc_finalize_end (void)
+{
+       for (ProfilerDesc *prof = prof_list; prof; prof = prof->next)
+               if ((prof->events & MONO_PROFILE_GC_FINALIZATION) && prof->gc_finalize_end)
+                       prof->gc_finalize_end (prof->profiler);
+}
+
+void
+mono_profiler_install_gc_finalize (MonoProfileGCFinalizeFunc begin, MonoProfileGCFinalizeObjectFunc begin_obj, MonoProfileGCFinalizeObjectFunc end_obj, MonoProfileGCFinalizeFunc end)
+{
+       if (!prof_list)
+               return;
+
+       prof_list->gc_finalize_begin = begin;
+       prof_list->gc_finalize_object_begin = begin_obj;
+       prof_list->gc_finalize_object_begin = end_obj;
+       prof_list->gc_finalize_end = end;
+}
+
 void
 mono_profiler_install_runtime_initialized (MonoProfileFunc runtime_initialized_callback)
 {
@@ -1147,7 +1196,15 @@ load_embedded_profiler (const char *desc, const char *name)
        MonoDl *pmodule = NULL;
        gboolean result;
 
-       pmodule = mono_dl_open (NULL, MONO_DL_LAZY, &err);
+       /*
+        * Some profilers (such as ours) may need to call back into the runtime
+        * from their sampling callback (which is called in async-signal context).
+        * They need to be able to know that all references back to the runtime
+        * have been resolved; otherwise, calling runtime functions may result in
+        * invoking the dynamic linker which is not async-signal-safe. Passing
+        * MONO_DL_EAGER will ask the dynamic linker to resolve everything upfront.
+        */
+       pmodule = mono_dl_open (NULL, MONO_DL_EAGER, &err);
        if (!pmodule) {
                g_warning ("Could not open main executable (%s)", err);
                g_free (err);
@@ -1161,6 +1218,7 @@ load_embedded_profiler (const char *desc, const char *name)
        return result;
 }
 
+// TODO: Much of the library loading code here is custom. It would be better to merge this with mono-dl
 static gboolean
 load_profiler_from_directory (const char *directory, const char *libname, const char *desc)
 {
@@ -1169,13 +1227,13 @@ load_profiler_from_directory (const char *directory, const char *libname, const
        char *err;
        void *iter;
 
-       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Attempting to load profiler %s from %s (desc %s)", libname, directory, desc);
+       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "Attempting to load profiler %s from %s (desc %s)", libname, directory, desc);
 
        iter = NULL;
        err = NULL;
        while ((path = mono_dl_build_path (directory, libname, &iter))) {
-               pmodule = mono_dl_open (path, MONO_DL_LAZY, &err);
-               mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Attempting to load profiler: %s %ssuccessful, err: %s", path, pmodule?"":"not ", err);
+               pmodule = mono_dl_open (path, MONO_DL_EAGER, &err);
+               mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "Attempting to load profiler: %s, %ssuccessful, err: %s", path, pmodule?"":"not ", err);
                g_free (path);
                g_free (err);
                if (pmodule)
@@ -1186,10 +1244,11 @@ load_profiler_from_directory (const char *directory, const char *libname, const
 }
 
 static gboolean
-load_profiler_from_mono_instalation (const char *libname, const char *desc)
+load_profiler_from_mono_installation (const char *libname, const char *desc)
 {
        char *err = NULL;
-       MonoDl *pmodule = mono_dl_open_runtime_lib (libname, MONO_DL_LAZY, &err);
+       MonoDl *pmodule = mono_dl_open_runtime_lib (libname, MONO_DL_EAGER, &err);
+       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "Attempting to load profiler from runtime libs: %s, %ssuccessful, err: %s", libname, pmodule?"":"not ", err);
        g_free (err);
        if (pmodule)
                return load_profiler (pmodule, desc, INITIALIZER_NAME);
@@ -1254,15 +1313,11 @@ mono_profiler_load (const char *desc)
                }
                if (!load_embedded_profiler (desc, mname)) {
                        libname = g_strdup_printf ("mono-profiler-%s", mname);
-                       char *profiler_lib_dir = getenv ("MONO_PROFILER_LIB_DIR");
-                       if (profiler_lib_dir)
-                               res = load_profiler_from_directory (profiler_lib_dir, libname, desc);
+                       res = load_profiler_from_mono_installation (libname, desc);
                        if (!res && mono_config_get_assemblies_dir ())
                                res = load_profiler_from_directory (mono_assembly_getrootdir (), libname, desc);
                        if (!res)
                                res = load_profiler_from_directory (NULL, libname, desc);
-                       if (!res)
-                               res = load_profiler_from_mono_instalation (libname, desc);
                        if (!res)
                                g_warning ("The '%s' profiler wasn't found in the main executable nor could it be loaded from '%s'.", mname, libname);
                        g_free (libname);