[runtime] Prioritize loading a profiler library from the installation dir over standa...
[mono.git] / mono / metadata / profiler.c
index 0e4a0ce4bdcffbefb3cf434e32edeaf0f3ad141b..05e8d367a8bdf3a021b697608ec9e61f78f6c4ad 100644 (file)
@@ -6,11 +6,12 @@
  *
  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
+ * Copyright 2011 Xamarin Inc (http://www.xamarin.com).
  */
 
 #include "config.h"
 #include "mono/metadata/profiler-private.h"
-#include "mono/metadata/profiler-default.h"
+#include "mono/metadata/assembly.h"
 #include "mono/metadata/debug-helpers.h"
 #include "mono/metadata/mono-debug.h"
 #include "mono/metadata/debug-mono-symfile.h"
@@ -102,9 +103,9 @@ struct _ProfilerDesc {
 
 static ProfilerDesc *prof_list = NULL;
 
-#define mono_profiler_coverage_lock() EnterCriticalSection (&profiler_coverage_mutex)
-#define mono_profiler_coverage_unlock() LeaveCriticalSection (&profiler_coverage_mutex)
-static CRITICAL_SECTION profiler_coverage_mutex;
+#define mono_profiler_coverage_lock() mono_mutex_lock (&profiler_coverage_mutex)
+#define mono_profiler_coverage_unlock() mono_mutex_unlock (&profiler_coverage_mutex)
+static mono_mutex_t profiler_coverage_mutex;
 
 /* this is directly accessible to other mono libs.
  * It is the ORed value of all the profiler's events.
@@ -127,7 +128,7 @@ mono_profiler_install (MonoProfiler *prof, MonoProfileFunc callback)
 {
        ProfilerDesc *desc = g_new0 (ProfilerDesc, 1);
        if (!prof_list)
-               InitializeCriticalSection (&profiler_coverage_mutex);
+               mono_mutex_init_recursive (&profiler_coverage_mutex);
        desc->profiler = prof;
        desc->shutdown_callback = callback;
        desc->next = prof_list;
@@ -272,6 +273,28 @@ mono_profiler_install_monitor  (MonoProfileMonitorFunc callback)
        prof_list->monitor_event_cb = callback;
 }
 
+static MonoProfileSamplingMode sampling_mode = MONO_PROFILER_STAT_MODE_PROCESS;
+static int64_t sampling_frequency = 1000; //1ms
+
+/**
+ * mono_profiler_set_statistical_mode:
+ * @mode the sampling mode used.
+ * @sample_frequency_is_us the sampling frequency in microseconds.
+ *
+ * Set the sampling parameters for the profiler. Sampling mode affects the effective sampling rate as in samples/s you'll witness.
+ * The default sampling mode is process mode, which only reports samples when there's activity in the process.
+ *
+ * Sampling frequency should be interpreted as a suggestion that can't always be honored due to how most kernels expose alarms.
+ *
+ * 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)
+{
+       sampling_mode = mode;
+       sampling_frequency = sampling_frequency_is_us;
+}
+
 void 
 mono_profiler_install_statistical (MonoProfileStatFunc callback)
 {
@@ -280,6 +303,18 @@ mono_profiler_install_statistical (MonoProfileStatFunc callback)
        prof_list->statistical_cb = callback;
 }
 
+int64_t
+mono_profiler_get_sampling_rate (void)
+{
+       return sampling_frequency;
+}
+
+MonoProfileSamplingMode
+mono_profiler_get_sampling_mode (void)
+{
+       return sampling_mode;
+}
+
 void 
 mono_profiler_install_statistical_call_chain (MonoProfileStatCallChainFunc callback, int call_chain_depth, MonoProfilerCallChainStrategy call_chain_strategy) {
        if (!prof_list)
@@ -901,11 +936,11 @@ mono_profiler_install_iomap (MonoProfileIomapFunc callback)
 }
 
 void
-mono_profiler_code_buffer_new (gpointer buffer, int size, MonoProfilerCodeBufferType type, void *data) {
+mono_profiler_code_buffer_new (gpointer buffer, int size, MonoProfilerCodeBufferType type, gconstpointer data) {
        ProfilerDesc *prof;
        for (prof = prof_list; prof; prof = prof->next) {
                if (prof->code_buffer_new)
-                       prof->code_buffer_new (prof->profiler, buffer, size, type, data);
+                       prof->code_buffer_new (prof->profiler, buffer, size, type, (void*)data);
        }
 }
 
@@ -1030,6 +1065,79 @@ mono_profiler_coverage_get (MonoProfiler *prof, MonoMethod *method, MonoProfileC
 typedef void (*ProfilerInitializer) (const char*);
 #define INITIALIZER_NAME "mono_profiler_startup"
 
+
+static gboolean
+load_profiler (MonoDl *pmodule, const char *desc, const char *symbol)
+{
+       char *err;
+       ProfilerInitializer func;
+
+       if (!pmodule)
+               return FALSE;
+
+       if ((err = mono_dl_symbol (pmodule, symbol, (gpointer *) &func))) {
+               g_free (err);
+               return FALSE;
+       } else {
+               func (desc);
+       }
+       return TRUE;
+}
+
+static gboolean
+load_embedded_profiler (const char *desc, const char *name)
+{
+       char *err = NULL;
+       char *symbol;
+       MonoDl *pmodule = NULL;
+       gboolean result;
+
+       pmodule = mono_dl_open (NULL, MONO_DL_LAZY, &err);
+       if (!pmodule) {
+               g_warning ("Could not open main executable (%s)", err);
+               g_free (err);
+               return FALSE;
+       }
+
+       symbol = g_strdup_printf (INITIALIZER_NAME "_%s", name);
+       result = load_profiler (pmodule, desc, symbol);
+       g_free (symbol);
+
+       return result;
+}
+
+static gboolean
+load_profiler_from_directory (const char *directory, const char *libname, const char *desc)
+{
+       MonoDl *pmodule = NULL;
+       char* path;
+       char *err;
+       void *iter;
+
+       iter = NULL;
+       err = NULL;
+       while ((path = mono_dl_build_path (directory, libname, &iter))) {
+               pmodule = mono_dl_open (path, MONO_DL_LAZY, &err);
+               g_free (path);
+               g_free (err);
+               if (pmodule)
+                       return load_profiler (pmodule, desc, INITIALIZER_NAME);
+       }
+               
+       return FALSE;
+}
+
+static gboolean
+load_profiler_from_mono_instalation (const char *libname, const char *desc)
+{
+       char *err = NULL;
+       MonoDl *pmodule = mono_dl_open_runtime_lib (libname, MONO_DL_LAZY, &err);
+       g_free (err);
+       if (pmodule)
+               return load_profiler (pmodule, desc, INITIALIZER_NAME);
+       return FALSE;
+}
+
 /**
  * mono_profiler_load:
  * @desc: arguments to configure the profiler
@@ -1043,58 +1151,64 @@ typedef void (*ProfilerInitializer) (const char*);
 void 
 mono_profiler_load (const char *desc)
 {
+       char *cdesc = NULL;
        mono_gc_base_init ();
 
-#ifndef DISABLE_PROFILER
-       if (!desc || (strcmp ("default", desc) == 0) || (strncmp (desc, "default:", 8) == 0)) {
-               mono_profiler_install_simple (desc);
-               return;
+       if (!desc || (strcmp ("default", desc) == 0)) {
+               desc = "log:report";
        }
-#else
-       if (!desc) {
-               desc = "default";
+       /* we keep command-line compat with the old version here */
+       if (strncmp (desc, "default:", 8) == 0) {
+               gchar **args, **ptr;
+               GString *str = g_string_new ("log:report");
+               args = g_strsplit (desc + 8, ",", -1);
+               for (ptr = args; ptr && *ptr; ptr++) {
+                       const char *arg = *ptr;
+
+                       if (!strcmp (arg, "time"))
+                               g_string_append (str, ",calls");
+                       else if (!strcmp (arg, "alloc"))
+                               g_string_append (str, ",alloc");
+                       else if (!strcmp (arg, "stat"))
+                               g_string_append (str, ",sample");
+                       else if (!strcmp (arg, "jit"))
+                               continue; /* accept and do nothing */
+                       else if (strncmp (arg, "file=", 5) == 0) {
+                               g_string_append_printf (str, ",output=%s", arg + 5);
+                       } else {
+                               fprintf (stderr, "profiler : Unknown argument '%s'.\n", arg);
+                               return;
+                       }
+               }
+               desc = cdesc = g_string_free (str, FALSE);
        }
-#endif
        {
-               MonoDl *pmodule = NULL;
                const char* col = strchr (desc, ':');
                char* libname;
-               char* path;
                char *mname;
-               char *err;
-               void *iter;
+               gboolean res = FALSE;
+
                if (col != NULL) {
                        mname = g_memdup (desc, col - desc + 1);
                        mname [col - desc] = 0;
                } else {
                        mname = g_strdup (desc);
                }
-               libname = g_strdup_printf ("mono-profiler-%s", mname);
-               iter = NULL;
-               err = NULL;
-               while ((path = mono_dl_build_path (NULL, libname, &iter))) {
-                       g_free (err);
-                       pmodule = mono_dl_open (path, MONO_DL_LAZY, &err);
-                       if (pmodule) {
-                               ProfilerInitializer func;
-                               if ((err = mono_dl_symbol (pmodule, INITIALIZER_NAME, (gpointer *)&func))) {
-                                       g_warning ("Cannot find initializer function %s in profiler module: %s (%s)", INITIALIZER_NAME, libname, err);
-                                       g_free (err);
-                                       err = NULL;
-                               } else {
-                                       func (desc);
-                               }
-                               break;
-                       }
-                       g_free (path);
-               }
-               if (!pmodule) {
-                       g_warning ("Error loading profiler module '%s': %s", libname, err);
-                       g_free (err);
+               if (!load_embedded_profiler (desc, mname)) {
+                       libname = g_strdup_printf ("mono-profiler-%s", mname);
+#if defined (MONO_ASSEMBLIES)
+                       res = load_profiler_from_directory (mono_assembly_getrootdir (), libname, desc);
+#endif
+                       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);
                }
-               g_free (libname);
                g_free (mname);
-               g_free (path);
        }
+       g_free (cdesc);
 }