X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fprofiler.c;h=9f65059f806699088b673fcf01a1896ae59a7576;hb=baac94e4b7429eb60ec264ef9b974dc148618b64;hp=4b81e2ebd9ba34d7bc6b18bef6870f243145ba95;hpb=2099acf5f4a80c46bf4e75e9d67b8db4f2c86bbd;p=mono.git diff --git a/mono/metadata/profiler.c b/mono/metadata/profiler.c index 4b81e2ebd9b..9f65059f806 100644 --- a/mono/metadata/profiler.c +++ b/mono/metadata/profiler.c @@ -1,1414 +1,767 @@ -/** - * \file - * Profiler interface for Mono - * - * Author: - * Paolo Molaro (lupus@ximian.com) - * Alex Rønne Petersen (alexrp@xamarin.com) - * - * 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). - * Licensed under the MIT license. See LICENSE file in the project root for full license information. - */ - -#include "config.h" -#include "mono/metadata/profiler-private.h" -#include "mono/metadata/assembly.h" -#include "mono/metadata/debug-helpers.h" -#include "mono/metadata/mono-debug.h" -#include "mono/metadata/debug-internals.h" -#include "mono/metadata/metadata-internals.h" -#include "mono/metadata/class-internals.h" -#include "mono/metadata/domain-internals.h" -#include "mono/metadata/gc-internals.h" -#include "mono/metadata/mono-config-dirs.h" -#include "mono/utils/mono-dl.h" +/* + * Licensed to the .NET Foundation under one or more agreements. + * The .NET Foundation licenses this file to you under the MIT license. + * See the LICENSE file in the project root for more information. + */ + +#include +#include +#include +#include +#include +#include +#include +#include #include -#include -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_SYS_TIME_H -#include -#endif -#ifdef HAVE_BACKTRACE_SYMBOLS -#include -#endif - -typedef struct _ProfilerDesc ProfilerDesc; -struct _ProfilerDesc { - ProfilerDesc *next; - MonoProfiler *profiler; - MonoProfileFlags events; - - MonoProfileAppDomainFunc domain_start_load; - MonoProfileAppDomainResult domain_end_load; - MonoProfileAppDomainFunc domain_start_unload; - MonoProfileAppDomainFunc domain_end_unload; - MonoProfileAppDomainFriendlyNameFunc domain_name; - - MonoProfileContextFunc context_load; - MonoProfileContextFunc context_unload; - - MonoProfileAssemblyFunc assembly_start_load; - MonoProfileAssemblyResult assembly_end_load; - MonoProfileAssemblyFunc assembly_start_unload; - MonoProfileAssemblyFunc assembly_end_unload; - - MonoProfileModuleFunc module_start_load; - MonoProfileModuleResult module_end_load; - MonoProfileModuleFunc module_start_unload; - MonoProfileModuleFunc module_end_unload; - - MonoProfileClassFunc class_start_load; - MonoProfileClassResult class_end_load; - MonoProfileClassFunc class_start_unload; - MonoProfileClassFunc class_end_unload; - - MonoProfileMethodFunc jit_start; - MonoProfileMethodResult jit_end; - MonoProfileJitResult jit_end2; - MonoProfileMethodFunc method_free; - MonoProfileMethodFunc method_start_invoke; - MonoProfileMethodFunc method_end_invoke; - MonoProfileMethodResult man_unman_transition; - MonoProfileAllocFunc allocation_cb; - MonoProfileMonitorFunc monitor_event_cb; - MonoProfileStatFunc statistical_cb; - MonoProfileStatCallChainFunc statistical_call_chain_cb; - int statistical_call_chain_depth; - MonoProfilerCallChainStrategy statistical_call_chain_strategy; - MonoProfileMethodFunc method_enter; - MonoProfileMethodFunc method_leave; - - MonoProfileExceptionFunc exception_throw_cb; - MonoProfileMethodFunc exception_method_leave_cb; - MonoProfileExceptionClauseFunc exception_clause_cb; - - MonoProfileIomapFunc iomap_cb; - - MonoProfileThreadFunc thread_start; - MonoProfileThreadFunc thread_end; - MonoProfileThreadNameFunc thread_name; - - MonoProfileCoverageFilterFunc coverage_filter_cb; - - MonoProfileFunc shutdown_callback; - - MonoProfileGCFunc gc_event; - MonoProfileGCResizeFunc gc_heap_resize; - MonoProfileGCMoveFunc gc_moves; - 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; - MonoProfilerCodeChunkDestroy code_chunk_destroy; - MonoProfilerCodeBufferNew code_buffer_new; -}; - -static ProfilerDesc *prof_list = NULL; -#define mono_profiler_coverage_lock() mono_os_mutex_lock (&profiler_coverage_mutex) -#define mono_profiler_coverage_unlock() mono_os_mutex_unlock (&profiler_coverage_mutex) -static mono_mutex_t profiler_coverage_mutex; +MonoProfilerState mono_profiler_state; -/* this is directly accessible to other mono libs. - * It is the ORed value of all the profiler's events. - */ -MonoProfileFlags mono_profiler_events; - -/** - * mono_profiler_install: - * \param prof a \c MonoProfiler structure pointer, or a pointer to a derived structure. - * \param callback the function to invoke at shutdown - * Use \c mono_profiler_install to activate profiling in the Mono runtime. - * Typically developers of new profilers will create a new structure whose - * first field is a \c MonoProfiler and put any extra information that they need - * to access from the various profiling callbacks there. - */ -void -mono_profiler_install (MonoProfiler *prof, MonoProfileFunc callback) -{ - ProfilerDesc *desc = g_new0 (ProfilerDesc, 1); - if (!prof_list) - mono_os_mutex_init_recursive (&profiler_coverage_mutex); - desc->profiler = prof; - desc->shutdown_callback = callback; - desc->next = prof_list; - prof_list = desc; -} - -/** - * mono_profiler_set_events: - * \param events an ORed set of values made up of \c MONO_PROFILER_ flags - * The events described in the \p events argument is a set of flags - * that represent which profiling events must be triggered. For - * example if you have registered a set of methods for tracking - * JIT compilation start and end with \c mono_profiler_install_jit_compile, - * you will want to pass the \c MONO_PROFILE_JIT_COMPILATION flag to - * this routine. - * - * You can call \c mono_profile_set_events more than once and you can - * do this at runtime to modify which methods are invoked. - */ -void -mono_profiler_set_events (MonoProfileFlags events) -{ - ProfilerDesc *prof; - MonoProfileFlags value = (MonoProfileFlags)0; - if (prof_list) - prof_list->events = events; - for (prof = prof_list; prof; prof = prof->next) - value = (MonoProfileFlags)(value | prof->events); - mono_profiler_events = value; -} +typedef void (*MonoProfilerInitializer) (const char *); -/** - * mono_profiler_get_events: - * - * Returns a list of active events that will be intercepted. - */ -MonoProfileFlags -mono_profiler_get_events (void) -{ - return mono_profiler_events; -} +#define OLD_INITIALIZER_NAME "mono_profiler_startup" +#define NEW_INITIALIZER_NAME "mono_profiler_init" -/** - * mono_profiler_install_enter_leave: - * \param enter the routine to be called on each method entry - * \param fleave the routine to be called each time a method returns - * - * Use this routine to install routines that will be called everytime - * a method enters and leaves. The routines will receive as an argument - * the \c MonoMethod representing the method that is entering or leaving. - */ -void -mono_profiler_install_enter_leave (MonoProfileMethodFunc enter, MonoProfileMethodFunc fleave) +static gboolean +load_profiler (MonoDl *module, const char *name, const char *desc) { - if (!prof_list) - return; - prof_list->method_enter = enter; - prof_list->method_leave = fleave; -} + if (!module) + return FALSE; -/** - * mono_profiler_install_jit_compile: - * \param start the routine to be called when the JIT process starts. - * \param end the routine to be called when the JIT process ends. - * - * Use this routine to install routines that will be called when JIT - * compilation of a method starts and completes. - */ -void -mono_profiler_install_jit_compile (MonoProfileMethodFunc start, MonoProfileMethodResult end) -{ - if (!prof_list) - return; - prof_list->jit_start = start; - prof_list->jit_end = end; -} + char *err, *old_name = g_strdup_printf (OLD_INITIALIZER_NAME); + MonoProfilerInitializer func; -void -mono_profiler_install_jit_end (MonoProfileJitResult end) -{ - if (!prof_list) - return; - prof_list->jit_end2 = end; -} + if (!(err = mono_dl_symbol (module, old_name, (gpointer) &func))) { + mono_profiler_printf_err ("Found old-style startup symbol '%s' for the '%s' profiler; it has not been migrated to the new API.", old_name, name); + g_free (old_name); + return FALSE; + } -void -mono_profiler_install_method_free (MonoProfileMethodFunc callback) -{ - if (!prof_list) - return; - prof_list->method_free = callback; -} + g_free (err); + g_free (old_name); -void -mono_profiler_install_method_invoke (MonoProfileMethodFunc start, MonoProfileMethodFunc end) -{ - if (!prof_list) - return; - prof_list->method_start_invoke = start; - prof_list->method_end_invoke = end; -} + char *new_name = g_strdup_printf (NEW_INITIALIZER_NAME "_%s", name); -/** - * mono_profiler_install_thread: - */ -void -mono_profiler_install_thread (MonoProfileThreadFunc start, MonoProfileThreadFunc end) -{ - if (!prof_list) - return; - prof_list->thread_start = start; - prof_list->thread_end = end; -} + if ((err = mono_dl_symbol (module, new_name, (gpointer *) &func))) { + g_free (err); + g_free (new_name); + return FALSE; + } -void -mono_profiler_install_thread_name (MonoProfileThreadNameFunc thread_name_cb) -{ - if (!prof_list) - return; - prof_list->thread_name = thread_name_cb; -} + g_free (new_name); -/** - * mono_profiler_install_transition: - */ -void -mono_profiler_install_transition (MonoProfileMethodResult callback) -{ - if (!prof_list) - return; - prof_list->man_unman_transition = callback; -} + func (desc); -/** - * mono_profiler_install_allocation: - */ -void -mono_profiler_install_allocation (MonoProfileAllocFunc callback) -{ - if (!prof_list) - return; - prof_list->allocation_cb = callback; + return TRUE; } -void -mono_profiler_install_monitor (MonoProfileMonitorFunc callback) +static gboolean +load_profiler_from_executable (const char *name, const char *desc) { - if (!prof_list) - return; - prof_list->monitor_event_cb = callback; -} + char *err; -static MonoProfileSamplingMode sampling_mode = MONO_PROFILER_STAT_MODE_PROCESS; -static int64_t sampling_frequency = 100; // Hz + /* + * 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. + */ + MonoDl *module = mono_dl_open (NULL, MONO_DL_EAGER, &err); -/** - * mono_profiler_set_statistical_mode: - * \param mode the sampling mode used. - * \param 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_hz) -{ - sampling_mode = mode; - sampling_frequency = sampling_frequency_hz; -} + if (!module) { + mono_profiler_printf_err ("Could not open main executable: %s", err); + g_free (err); + return FALSE; + } -/** - * mono_profiler_install_statistical: - */ -void -mono_profiler_install_statistical (MonoProfileStatFunc callback) -{ - if (!prof_list) - return; - prof_list->statistical_cb = callback; + return load_profiler (module, name, desc); } -int64_t -mono_profiler_get_sampling_rate (void) +static gboolean +load_profiler_from_directory (const char *directory, const char *libname, const char *name, const char *desc) { - return sampling_frequency; -} + char* path; + void *iter = NULL; -MonoProfileSamplingMode -mono_profiler_get_sampling_mode (void) -{ - return sampling_mode; -} + while ((path = mono_dl_build_path (directory, libname, &iter))) { + // See the comment in load_embedded_profiler (). + MonoDl *module = mono_dl_open (path, MONO_DL_EAGER, NULL); -void -mono_profiler_install_statistical_call_chain (MonoProfileStatCallChainFunc callback, int call_chain_depth, MonoProfilerCallChainStrategy call_chain_strategy) { - if (!prof_list) - return; - if (call_chain_depth > MONO_PROFILER_MAX_STAT_CALL_CHAIN_DEPTH) { - call_chain_depth = MONO_PROFILER_MAX_STAT_CALL_CHAIN_DEPTH; - } - if ((call_chain_strategy >= MONO_PROFILER_CALL_CHAIN_INVALID) || (call_chain_strategy < MONO_PROFILER_CALL_CHAIN_NONE)) { - call_chain_strategy = MONO_PROFILER_CALL_CHAIN_NONE; - } - prof_list->statistical_call_chain_cb = callback; - prof_list->statistical_call_chain_depth = call_chain_depth; - prof_list->statistical_call_chain_strategy = call_chain_strategy; -} + g_free (path); -int -mono_profiler_stat_get_call_chain_depth (void) { - if (prof_list && prof_list->statistical_call_chain_cb != NULL) { - return prof_list->statistical_call_chain_depth; - } else { - return 0; + if (module) + return load_profiler (module, name, desc); } -} -MonoProfilerCallChainStrategy -mono_profiler_stat_get_call_chain_strategy (void) { - if (prof_list && prof_list->statistical_call_chain_cb != NULL) { - return prof_list->statistical_call_chain_strategy; - } else { - return MONO_PROFILER_CALL_CHAIN_NONE; - } + return FALSE; } -void mono_profiler_install_exception (MonoProfileExceptionFunc throw_callback, MonoProfileMethodFunc exc_method_leave, MonoProfileExceptionClauseFunc clause_callback) +static gboolean +load_profiler_from_installation (const char *libname, const char *name, const char *desc) { - if (!prof_list) - return; - prof_list->exception_throw_cb = throw_callback; - prof_list->exception_method_leave_cb = exc_method_leave; - prof_list->exception_clause_cb = clause_callback; -} + char *err; + MonoDl *module = mono_dl_open_runtime_lib (libname, MONO_DL_EAGER, &err); -/** - * mono_profiler_install_coverage_filter: - */ -void -mono_profiler_install_coverage_filter (MonoProfileCoverageFilterFunc callback) -{ - if (!prof_list) - return; - prof_list->coverage_filter_cb = callback; -} + g_free (err); -/** - * mono_profiler_install_appdomain: - */ -void -mono_profiler_install_appdomain (MonoProfileAppDomainFunc start_load, MonoProfileAppDomainResult end_load, - MonoProfileAppDomainFunc start_unload, MonoProfileAppDomainFunc end_unload) + if (module) + return load_profiler (module, name, desc); -{ - if (!prof_list) - return; - prof_list->domain_start_load = start_load; - prof_list->domain_end_load = end_load; - prof_list->domain_start_unload = start_unload; - prof_list->domain_end_unload = end_unload; + return FALSE; } void -mono_profiler_install_appdomain_name (MonoProfileAppDomainFriendlyNameFunc domain_name_cb) +mono_profiler_load (const char *desc) { - if (!prof_list) - return; + if (!desc || !strcmp ("default", desc)) + desc = "log:report"; - prof_list->domain_name = domain_name_cb; -} + const char *col = strchr (desc, ':'); + char *mname; -void -mono_profiler_install_context (MonoProfileContextFunc load, MonoProfileContextFunc unload) -{ - if (!prof_list) - return; + if (col != NULL) { + mname = (char *) g_memdup (desc, col - desc + 1); + mname [col - desc] = 0; + } else + mname = g_strdup (desc); - prof_list->context_load = load; - prof_list->context_unload = unload; -} + if (!load_profiler_from_executable (mname, desc)) { + char *libname = g_strdup_printf ("mono-profiler-%s", mname); + gboolean res = load_profiler_from_installation (libname, mname, desc); -/** - * mono_profiler_install_assembly: - */ -void -mono_profiler_install_assembly (MonoProfileAssemblyFunc start_load, MonoProfileAssemblyResult end_load, - MonoProfileAssemblyFunc start_unload, MonoProfileAssemblyFunc end_unload) -{ - if (!prof_list) - return; - prof_list->assembly_start_load = start_load; - prof_list->assembly_end_load = end_load; - prof_list->assembly_start_unload = start_unload; - prof_list->assembly_end_unload = end_unload; -} + if (!res && mono_config_get_assemblies_dir ()) + res = load_profiler_from_directory (mono_assembly_getrootdir (), libname, mname, desc); -/** - * mono_profiler_install_module: - */ -void -mono_profiler_install_module (MonoProfileModuleFunc start_load, MonoProfileModuleResult end_load, - MonoProfileModuleFunc start_unload, MonoProfileModuleFunc end_unload) -{ - if (!prof_list) - return; - prof_list->module_start_load = start_load; - prof_list->module_end_load = end_load; - prof_list->module_start_unload = start_unload; - prof_list->module_end_unload = end_unload; -} + if (!res) + res = load_profiler_from_directory (NULL, libname, mname, desc); -/** - * mono_profiler_install_class: - */ -void -mono_profiler_install_class (MonoProfileClassFunc start_load, MonoProfileClassResult end_load, - MonoProfileClassFunc start_unload, MonoProfileClassFunc end_unload) -{ - if (!prof_list) - return; - prof_list->class_start_load = start_load; - prof_list->class_end_load = end_load; - prof_list->class_start_unload = start_unload; - prof_list->class_end_unload = end_unload; -} + if (!res) + mono_profiler_printf_err ("The '%s' profiler wasn't found in the main executable nor could it be loaded from '%s'.", mname, libname); -/** - * mono_profiler_method_enter: - */ -void -mono_profiler_method_enter (MonoMethod *method) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_ENTER_LEAVE) && prof->method_enter) - prof->method_enter (prof->profiler, method); + g_free (libname); } -} -/** - * mono_profiler_method_leave: - */ -void -mono_profiler_method_leave (MonoMethod *method) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_ENTER_LEAVE) && prof->method_leave) - prof->method_leave (prof->profiler, method); - } + g_free (mname); } -/** - * mono_profiler_method_jit: - */ -void -mono_profiler_method_jit (MonoMethod *method) +MonoProfilerHandle +mono_profiler_create (MonoProfiler *prof) { - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_JIT_COMPILATION) && prof->jit_start) - prof->jit_start (prof->profiler, method); - } -} + MonoProfilerHandle handle = g_new0 (struct _MonoProfilerDesc, 1); -/** - * mono_profiler_method_end_jit: - */ -void -mono_profiler_method_end_jit (MonoMethod *method, MonoJitInfo* jinfo, int result) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_JIT_COMPILATION)) { - if (prof->jit_end) - prof->jit_end (prof->profiler, method, result); - if (prof->jit_end2) - prof->jit_end2 (prof->profiler, method, jinfo, result); - } - } -} + handle->prof = prof; + handle->next = mono_profiler_state.profilers; -void -mono_profiler_method_free (MonoMethod *method) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_METHOD_EVENTS) && prof->method_free) - prof->method_free (prof->profiler, method); - } + mono_profiler_state.profilers = handle; + + return handle; } void -mono_profiler_method_start_invoke (MonoMethod *method) +mono_profiler_set_cleanup_callback (MonoProfilerHandle handle, MonoProfilerCleanupCallback cb) { - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_METHOD_EVENTS) && prof->method_start_invoke) - prof->method_start_invoke (prof->profiler, method); - } + InterlockedWritePointer (&handle->cleanup_callback, (gpointer) cb); } void -mono_profiler_method_end_invoke (MonoMethod *method) +mono_profiler_set_coverage_filter_callback (MonoProfilerHandle handle, MonoProfilerCoverageFilterCallback cb) { - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_METHOD_EVENTS) && prof->method_end_invoke) - prof->method_end_invoke (prof->profiler, method); - } + InterlockedWritePointer (&handle->coverage_filter, (gpointer) cb); } -/** - * mono_profiler_code_transition: - */ -void -mono_profiler_code_transition (MonoMethod *method, int result) +mono_bool +mono_profiler_enable_coverage (void) { - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_TRANSITIONS) && prof->man_unman_transition) - prof->man_unman_transition (prof->profiler, method, result); - } -} + if (mono_profiler_state.startup_done) + return FALSE; -/** - * mono_profiler_allocation: - */ -void -mono_profiler_allocation (MonoObject *obj) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_ALLOCATIONS) && prof->allocation_cb) - prof->allocation_cb (prof->profiler, obj, obj->vtable->klass); - } -} + mono_os_mutex_init (&mono_profiler_state.coverage_mutex); + mono_profiler_state.coverage_hash = g_hash_table_new (NULL, NULL); -void -mono_profiler_monitor_event (MonoObject *obj, MonoProfilerMonitorEvent event) { - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_MONITOR_EVENTS) && prof->monitor_event_cb) - prof->monitor_event_cb (prof->profiler, obj, event); - } -} + if (!mono_debug_enabled ()) + mono_debug_init (MONO_DEBUG_FORMAT_MONO); -/** - * mono_profiler_stat_hit: - */ -void -mono_profiler_stat_hit (guchar *ip, void *context) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_STATISTICAL) && prof->statistical_cb) - prof->statistical_cb (prof->profiler, ip, context); - } + return mono_profiler_state.code_coverage = TRUE; } -void -mono_profiler_stat_call_chain (int call_chain_depth, guchar **ips, void *context) +static void +coverage_lock (void) { - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_STATISTICAL) && prof->statistical_call_chain_cb) - prof->statistical_call_chain_cb (prof->profiler, call_chain_depth, ips, context); - } + mono_os_mutex_lock (&mono_profiler_state.coverage_mutex); } -void -mono_profiler_exception_thrown (MonoObject *exception) +static void +coverage_unlock (void) { - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_EXCEPTIONS) && prof->exception_throw_cb) - prof->exception_throw_cb (prof->profiler, exception); - } + mono_os_mutex_unlock (&mono_profiler_state.coverage_mutex); } -void -mono_profiler_exception_method_leave (MonoMethod *method) +mono_bool +mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method, MonoProfilerCoverageCallback cb) { - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_EXCEPTIONS) && prof->exception_method_leave_cb) - prof->exception_method_leave_cb (prof->profiler, method); - } -} + if (!mono_profiler_state.code_coverage) + return FALSE; -void -mono_profiler_exception_clause_handler (MonoMethod *method, int clause_type, int clause_num) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_EXCEPTIONS) && prof->exception_clause_cb) - prof->exception_clause_cb (prof->profiler, method, clause_type, clause_num); - } -} + coverage_lock (); -/** - * mono_profiler_thread_start: - */ -void -mono_profiler_thread_start (gsize tid) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_THREADS) && prof->thread_start) - prof->thread_start (prof->profiler, tid); - } -} + MonoProfilerCoverageInfo *info = g_hash_table_lookup (mono_profiler_state.coverage_hash, method); -/** - * mono_profiler_thread_end: - */ -void -mono_profiler_thread_end (gsize tid) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_THREADS) && prof->thread_end) - prof->thread_end (prof->profiler, tid); - } -} + coverage_unlock (); -void -mono_profiler_thread_name (gsize tid, const char *name) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_THREADS) && prof->thread_name) - prof->thread_name (prof->profiler, tid, name); - } -} + MonoError error; + MonoMethodHeader *header = mono_method_get_header_checked (method, &error); + mono_error_assert_ok (&error); -/** - * mono_profiler_assembly_event: - */ -void -mono_profiler_assembly_event (MonoAssembly *assembly, int code) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if (!(prof->events & MONO_PROFILE_ASSEMBLY_EVENTS)) - continue; - - switch (code) { - case MONO_PROFILE_START_LOAD: - if (prof->assembly_start_load) - prof->assembly_start_load (prof->profiler, assembly); - break; - case MONO_PROFILE_START_UNLOAD: - if (prof->assembly_start_unload) - prof->assembly_start_unload (prof->profiler, assembly); - break; - case MONO_PROFILE_END_UNLOAD: - if (prof->assembly_end_unload) - prof->assembly_end_unload (prof->profiler, assembly); - break; - default: - g_assert_not_reached (); - } - } -} + guint32 size; -/** - * mono_profiler_assembly_loaded: - */ -void -mono_profiler_assembly_loaded (MonoAssembly *assembly, int result) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_ASSEMBLY_EVENTS) && prof->assembly_end_load) - prof->assembly_end_load (prof->profiler, assembly, result); - } -} + const unsigned char *start = mono_method_header_get_code (header, &size, NULL); + const unsigned char *end = start + size; + MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method); -void mono_profiler_iomap (char *report, const char *pathname, const char *new_pathname) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_IOMAP_EVENTS) && prof->iomap_cb) - prof->iomap_cb (prof->profiler, report, pathname, new_pathname); - } -} + if (!info) { + char *source_file; + int i, n_il_offsets; + int *source_files; + GPtrArray *source_file_list; + MonoSymSeqPoint *sym_seq_points; -/** - * mono_profiler_module_event: - */ -void -mono_profiler_module_event (MonoImage *module, int code) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if (!(prof->events & MONO_PROFILE_MODULE_EVENTS)) - continue; - - switch (code) { - case MONO_PROFILE_START_LOAD: - if (prof->module_start_load) - prof->module_start_load (prof->profiler, module); - break; - case MONO_PROFILE_START_UNLOAD: - if (prof->module_start_unload) - prof->module_start_unload (prof->profiler, module); - break; - case MONO_PROFILE_END_UNLOAD: - if (prof->module_end_unload) - prof->module_end_unload (prof->profiler, module); - break; - default: - g_assert_not_reached (); - } - } -} + /* Return 0 counts for all locations */ -/** - * mono_profiler_module_loaded: - */ -void -mono_profiler_module_loaded (MonoImage *module, int result) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_MODULE_EVENTS) && prof->module_end_load) - prof->module_end_load (prof->profiler, module, result); - } -} + 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 = ""; -/** - * mono_profiler_class_event: - */ -void -mono_profiler_class_event (MonoClass *klass, int code) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if (!(prof->events & MONO_PROFILE_CLASS_EVENTS)) - continue; - - switch (code) { - case MONO_PROFILE_START_LOAD: - if (prof->class_start_load) - prof->class_start_load (prof->profiler, klass); - break; - case MONO_PROFILE_START_UNLOAD: - if (prof->class_start_unload) - prof->class_start_unload (prof->profiler, klass); - break; - case MONO_PROFILE_END_UNLOAD: - if (prof->class_end_unload) - prof->class_end_unload (prof->profiler, klass); - break; - default: - g_assert_not_reached (); + 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); } - } -} -/** - * mono_profiler_class_loaded: - */ -void -mono_profiler_class_loaded (MonoClass *klass, int result) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_CLASS_EVENTS) && prof->class_end_load) - prof->class_end_load (prof->profiler, klass, result); + g_free (source_files); + g_free (sym_seq_points); + g_ptr_array_free (source_file_list, TRUE); + + mono_metadata_free_mh (header); + return TRUE; } -} -/** - * mono_profiler_appdomain_event: - */ -void -mono_profiler_appdomain_event (MonoDomain *domain, int code) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if (!(prof->events & MONO_PROFILE_APPDOMAIN_EVENTS)) - continue; - - switch (code) { - case MONO_PROFILE_START_LOAD: - if (prof->domain_start_load) - prof->domain_start_load (prof->profiler, domain); - break; - case MONO_PROFILE_START_UNLOAD: - if (prof->domain_start_unload) - prof->domain_start_unload (prof->profiler, domain); - break; - case MONO_PROFILE_END_UNLOAD: - if (prof->domain_end_unload) - prof->domain_end_unload (prof->profiler, domain); - break; - default: - g_assert_not_reached (); + for (guint32 i = 0; i < info->entries; i++) { + guchar *cil_code = info->data [i].cil_code; + + if (cil_code && cil_code >= start && cil_code < end) { + guint32 offset = cil_code - start; + + MonoProfilerCoverageData data = { + .method = method, + .il_offset = offset, + .counter = info->data [i].count, + .line = 1, + .column = 1, + }; + + if (minfo) { + MonoDebugSourceLocation *loc = mono_debug_method_lookup_location (minfo, offset); + + if (loc) { + data.file_name = g_strdup (loc->source_file); + data.line = loc->row; + data.column = loc->column; + + mono_debug_free_source_location (loc); + } + } + + cb (handle->prof, &data); + + g_free ((char *) data.file_name); } } -} -/** - * mono_profiler_appdomain_loaded: - */ -void -mono_profiler_appdomain_loaded (MonoDomain *domain, int result) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_APPDOMAIN_EVENTS) && prof->domain_end_load) - prof->domain_end_load (prof->profiler, domain, result); - } -} + mono_metadata_free_mh (header); -void -mono_profiler_appdomain_name (MonoDomain *domain, const char *name) -{ - for (ProfilerDesc *prof = prof_list; prof; prof = prof->next) - if ((prof->events & MONO_PROFILE_APPDOMAIN_EVENTS) && prof->domain_name) - prof->domain_name (prof->profiler, domain, name); + return TRUE; } -void -mono_profiler_context_loaded (MonoAppContext *context) +MonoProfilerCoverageInfo * +mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries) { - for (ProfilerDesc *prof = prof_list; prof; prof = prof->next) - if ((prof->events & MONO_PROFILE_CONTEXT_EVENTS) && prof->context_load) - prof->context_load (prof->profiler, context); -} + if (!mono_profiler_state.code_coverage) + return FALSE; -void -mono_profiler_context_unloaded (MonoAppContext *context) -{ - for (ProfilerDesc *prof = prof_list; prof; prof = prof->next) - if ((prof->events & MONO_PROFILE_CONTEXT_EVENTS) && prof->context_unload) - prof->context_unload (prof->profiler, context); -} + if (method->wrapper_type) + return FALSE; -/** - * mono_profiler_shutdown: - */ -void -mono_profiler_shutdown (void) -{ - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if (prof->shutdown_callback) - prof->shutdown_callback (prof->profiler); + gboolean cover = FALSE; + + for (MonoProfilerHandle handle = mono_profiler_state.profilers; handle; handle = handle->next) { + MonoProfilerCoverageFilterCallback cb = handle->coverage_filter; + + if (cb) + cover |= cb (handle->prof, method); } - mono_profiler_set_events ((MonoProfileFlags)0); + if (!cover) + return NULL; + + coverage_lock (); + + MonoProfilerCoverageInfo *info = g_malloc0 (sizeof (MonoProfilerCoverageInfo) + SIZEOF_VOID_P * 2 * entries); + + info->entries = entries; + + g_hash_table_insert (mono_profiler_state.coverage_hash, method, info); + + coverage_unlock (); + + return info; } -/** - * mono_profiler_gc_heap_resize: - */ -void -mono_profiler_gc_heap_resize (gint64 new_size) +mono_bool +mono_profiler_enable_sampling (MonoProfilerHandle handle) { - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_GC) && prof->gc_heap_resize) - prof->gc_heap_resize (prof->profiler, new_size); - } + if (mono_profiler_state.startup_done) + return FALSE; + + if (mono_profiler_state.sampling_owner) + return TRUE; + + mono_profiler_state.sampling_owner = handle; + mono_profiler_state.sample_mode = MONO_PROFILER_SAMPLE_MODE_NONE; + mono_profiler_state.sample_freq = 100; + mono_os_sem_init (&mono_profiler_state.sampling_semaphore, 0); + + return TRUE; } -/** - * mono_profiler_gc_event: - */ -void -mono_profiler_gc_event (MonoGCEvent event, int generation) +mono_bool +mono_profiler_set_sample_mode (MonoProfilerHandle handle, MonoProfilerSampleMode mode, uint32_t freq) { - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_GC) && prof->gc_event) - prof->gc_event (prof->profiler, event, generation); - } + if (handle != mono_profiler_state.sampling_owner) + return FALSE; + + mono_profiler_state.sample_mode = mode; + mono_profiler_state.sample_freq = freq; + + mono_profiler_sampling_thread_post (); + + return TRUE; } -void -mono_profiler_gc_moves (void **objects, int num) +mono_bool +mono_profiler_get_sample_mode (MonoProfilerHandle handle, MonoProfilerSampleMode *mode, uint32_t *freq) { - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_GC_MOVES) && prof->gc_moves) - prof->gc_moves (prof->profiler, objects, num); - } + if (mode) + *mode = mono_profiler_state.sample_mode; + + if (freq) + *freq = mono_profiler_state.sample_freq; + + return handle == mono_profiler_state.sampling_owner; } -void -mono_profiler_gc_handle (int op, int type, uintptr_t handle, MonoObject *obj) +gboolean +mono_profiler_sampling_enabled (void) { - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_GC_ROOTS) && prof->gc_handle) - prof->gc_handle (prof->profiler, op, type, handle, obj); - } + return !!mono_profiler_state.sampling_owner; } void -mono_profiler_gc_roots (int num, void **objects, int *root_types, uintptr_t *extra_info) +mono_profiler_sampling_thread_post (void) { - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if ((prof->events & MONO_PROFILE_GC_ROOTS) && prof->gc_roots) - prof->gc_roots (prof->profiler, num, objects, root_types, extra_info); - } + mono_os_sem_post (&mono_profiler_state.sampling_semaphore); } -/** - * mono_profiler_install_gc: - */ void -mono_profiler_install_gc (MonoProfileGCFunc callback, MonoProfileGCResizeFunc heap_resize_callback) +mono_profiler_sampling_thread_wait (void) { - if (!prof_list) - return; - prof_list->gc_event = callback; - prof_list->gc_heap_resize = heap_resize_callback; + mono_os_sem_wait (&mono_profiler_state.sampling_semaphore, MONO_SEM_FLAGS_NONE); } -/** - * mono_profiler_install_gc_moves: - * \param callback callback function - * - * Install the \p callback function that the GC will call when moving objects. - * The callback receives an array of pointers and the number of elements - * in the array. Every even element in the array is the original object location - * and the following odd element is the new location of the object in memory. - * So the number of elements argument will always be a multiple of 2. - * Since this callback happens during the GC, it is a restricted environment: - * no locks can be taken and the object pointers can be inspected only once - * the GC is finished (of course the original location pointers will not - * point to valid objects anymore). - */ -void -mono_profiler_install_gc_moves (MonoProfileGCMoveFunc callback) +mono_bool +mono_profiler_enable_allocations (void) { - if (!prof_list) - return; - prof_list->gc_moves = callback; + if (mono_profiler_state.startup_done) + return FALSE; + + return mono_profiler_state.allocations = TRUE; } -/** - * mono_profiler_install_gc_roots: - * \param handle_callback callback function - * \param roots_callback callback function - * - * Install the \p handle_callback function that the GC will call when GC - * handles are created or destroyed. - * The callback receives an operation, which is either \c MONO_PROFILER_GC_HANDLE_CREATED - * or \c MONO_PROFILER_GC_HANDLE_DESTROYED, the handle type, the handle value and the - * object pointer, if present. - * Install the \p roots_callback function that the GC will call when tracing - * the roots for a collection. - * The callback receives the number of elements and three arrays: an array - * of objects, an array of root types and flags and an array of extra info. - * The size of each array is given by the first argument. - */ void -mono_profiler_install_gc_roots (MonoProfileGCHandleFunc handle_callback, MonoProfileGCRootFunc roots_callback) +mono_profiler_set_call_instrumentation_filter_callback (MonoProfilerHandle handle, MonoProfilerCallInstrumentationFilterCallback cb) { - if (!prof_list) - return; - prof_list->gc_handle = handle_callback; - prof_list->gc_roots = roots_callback; + InterlockedWritePointer (&handle->call_instrumentation_filter, (gpointer) cb); } -void -mono_profiler_gc_finalize_begin (void) +mono_bool +mono_profiler_enable_call_context_introspection (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); + if (mono_profiler_state.startup_done) + return FALSE; + + mono_profiler_state.context_enable (); + + return mono_profiler_state.call_contexts = TRUE; } -void -mono_profiler_gc_finalize_object_begin (MonoObject *obj) +void * +mono_profiler_call_context_get_this (MonoProfilerCallContext *context) { - 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); + if (!mono_profiler_state.call_contexts) + return NULL; + + return mono_profiler_state.context_get_this (context); } -void -mono_profiler_gc_finalize_object_end (MonoObject *obj) +void * +mono_profiler_call_context_get_argument (MonoProfilerCallContext *context, uint32_t position) { - 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); + if (!mono_profiler_state.call_contexts) + return NULL; + + return mono_profiler_state.context_get_argument (context, position); } -void -mono_profiler_gc_finalize_end (void) +void * +mono_profiler_call_context_get_local (MonoProfilerCallContext *context, uint32_t position) { - 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); + if (!mono_profiler_state.call_contexts) + return NULL; + + return mono_profiler_state.context_get_local (context, position); } -void -mono_profiler_install_gc_finalize (MonoProfileGCFinalizeFunc begin, MonoProfileGCFinalizeObjectFunc begin_obj, MonoProfileGCFinalizeObjectFunc end_obj, MonoProfileGCFinalizeFunc end) +void * +mono_profiler_call_context_get_result (MonoProfilerCallContext *context) { - if (!prof_list) - return; + if (!mono_profiler_state.call_contexts) + return NULL; - prof_list->gc_finalize_begin = begin; - prof_list->gc_finalize_object_begin = begin_obj; - prof_list->gc_finalize_object_end = end_obj; - prof_list->gc_finalize_end = end; + return mono_profiler_state.context_get_result (context); } void -mono_profiler_install_runtime_initialized (MonoProfileFunc runtime_initialized_callback) +mono_profiler_call_context_free_buffer (void *buffer) { - if (!prof_list) - return; - prof_list->runtime_initialized_event = runtime_initialized_callback; + mono_profiler_state.context_free_buffer (buffer); } -void -mono_profiler_runtime_initialized (void) { - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if (prof->runtime_initialized_event) - prof->runtime_initialized_event (prof->profiler); - } -} +MonoProfilerCallInstrumentationFlags +mono_profiler_get_call_instrumentation_flags (MonoMethod *method) +{ + MonoProfilerCallInstrumentationFlags flags = MONO_PROFILER_CALL_INSTRUMENTATION_NONE; -void -mono_profiler_install_code_chunk_new (MonoProfilerCodeChunkNew callback) { - if (!prof_list) - return; - prof_list->code_chunk_new = callback; -} -void -mono_profiler_code_chunk_new (gpointer chunk, int size) { - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if (prof->code_chunk_new) - prof->code_chunk_new (prof->profiler, chunk, size); - } -} + for (MonoProfilerHandle handle = mono_profiler_state.profilers; handle; handle = handle->next) { + MonoProfilerCallInstrumentationFilterCallback cb = handle->call_instrumentation_filter; -void -mono_profiler_install_code_chunk_destroy (MonoProfilerCodeChunkDestroy callback) { - if (!prof_list) - return; - prof_list->code_chunk_destroy = callback; -} -void -mono_profiler_code_chunk_destroy (gpointer chunk) { - ProfilerDesc *prof; - for (prof = prof_list; prof; prof = prof->next) { - if (prof->code_chunk_destroy) - prof->code_chunk_destroy (prof->profiler, chunk); + if (cb) + flags |= cb (handle->prof, method); } -} -void -mono_profiler_install_code_buffer_new (MonoProfilerCodeBufferNew callback) { - if (!prof_list) - return; - prof_list->code_buffer_new = callback; + return flags; } void -mono_profiler_install_iomap (MonoProfileIomapFunc callback) +mono_profiler_started (void) { - if (!prof_list) - return; - prof_list->iomap_cb = callback; + mono_profiler_state.startup_done = TRUE; } void -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, (void*)data); +mono_profiler_cleanup (void) +{ + for (MonoProfilerHandle handle = mono_profiler_state.profilers; handle; handle = handle->next) { +#define _MONO_PROFILER_EVENT(name) \ + mono_profiler_set_ ## name ## _callback (handle, NULL); \ + g_assert (!handle->name ## _cb); +#define MONO_PROFILER_EVENT_0(name, type) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \ + _MONO_PROFILER_EVENT(name) +#define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ + _MONO_PROFILER_EVENT(name) +#include +#undef MONO_PROFILER_EVENT_0 +#undef MONO_PROFILER_EVENT_1 +#undef MONO_PROFILER_EVENT_2 +#undef MONO_PROFILER_EVENT_3 +#undef MONO_PROFILER_EVENT_4 +#undef _MONO_PROFILER_EVENT } -} -static GHashTable *coverage_hash = NULL; +#define _MONO_PROFILER_EVENT(name, type) \ + g_assert (!mono_profiler_state.name ## _count); +#define MONO_PROFILER_EVENT_0(name, type) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ + _MONO_PROFILER_EVENT(name, type) +#include +#undef MONO_PROFILER_EVENT_0 +#undef MONO_PROFILER_EVENT_1 +#undef MONO_PROFILER_EVENT_2 +#undef MONO_PROFILER_EVENT_3 +#undef MONO_PROFILER_EVENT_4 +#undef _MONO_PROFILER_EVENT -MonoProfileCoverageInfo* -mono_profiler_coverage_alloc (MonoMethod *method, int entries) -{ - MonoProfileCoverageInfo *res; - int instrument = FALSE; - ProfilerDesc *prof; - - for (prof = prof_list; prof; prof = prof->next) { - /* note that we call the filter on all the profilers even if just - * a single one would be enough to instrument a method - */ - if (prof->coverage_filter_cb) - if (prof->coverage_filter_cb (prof->profiler, method)) - instrument = TRUE; + 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 (!instrument) - return NULL; - mono_profiler_coverage_lock (); - if (!coverage_hash) - coverage_hash = g_hash_table_new (NULL, NULL); + if (mono_profiler_state.code_coverage) { + mono_os_mutex_destroy (&mono_profiler_state.coverage_mutex); - res = (MonoProfileCoverageInfo *)g_malloc0 (sizeof (MonoProfileCoverageInfo) + sizeof (void*) * 2 * entries); + GHashTableIter iter; - res->entries = entries; + g_hash_table_iter_init (&iter, mono_profiler_state.coverage_hash); - g_hash_table_insert (coverage_hash, method, res); - mono_profiler_coverage_unlock (); + MonoProfilerCoverageInfo *info; - return res; + 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); } -/* safe only when the method antive code has been unloaded */ -void -mono_profiler_coverage_free (MonoMethod *method) +static void +update_callback (volatile gpointer *location, gpointer new_, volatile gint32 *counter) { - MonoProfileCoverageInfo* info; + gpointer old; - mono_profiler_coverage_lock (); - if (!coverage_hash) { - mono_profiler_coverage_unlock (); - return; - } + do { + old = InterlockedReadPointer (location); + } while (InterlockedCompareExchangePointer (location, new_, old) != old); - info = (MonoProfileCoverageInfo *)g_hash_table_lookup (coverage_hash, method); - if (info) { - g_free (info); - g_hash_table_remove (coverage_hash, method); - } - mono_profiler_coverage_unlock (); -} + /* + * At this point, we could have installed a NULL callback while the counter + * is still non-zero, i.e. setting the callback and modifying the counter + * is not a single atomic operation. This is fine as we make sure callbacks + * are non-NULL before invoking them (see the code below that generates the + * raise functions), and besides, updating callbacks at runtime is an + * inherently racy operation. + */ -/** - * mono_profiler_coverage_get: - * \param prof The profiler handle, installed with mono_profiler_install - * \param method the method to gather information from. - * \param func A routine that will be called back with the results - * - * If the \c MONO_PROFILER_INS_COVERAGE flag was active during JIT compilation - * it is possible to obtain coverage information about a give method. + if (old) + InterlockedDecrement (counter); + + if (new_) + InterlockedIncrement (counter); +} + +#define _MONO_PROFILER_EVENT(name, type) \ + void \ + mono_profiler_set_ ## name ## _callback (MonoProfilerHandle handle, MonoProfiler ## type ## Callback cb) \ + { \ + update_callback (&handle->name ## _cb, (gpointer) cb, &mono_profiler_state.name ## _count); \ + } +#define MONO_PROFILER_EVENT_0(name, type) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \ + _MONO_PROFILER_EVENT(name, type) +#define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ + _MONO_PROFILER_EVENT(name, type) +#include +#undef MONO_PROFILER_EVENT_0 +#undef MONO_PROFILER_EVENT_1 +#undef MONO_PROFILER_EVENT_2 +#undef MONO_PROFILER_EVENT_3 +#undef MONO_PROFILER_EVENT_4 +#undef _MONO_PROFILER_EVENT + +#define _MONO_PROFILER_EVENT(name, type, params, args) \ + void \ + mono_profiler_raise_ ## name params \ + { \ + for (MonoProfilerHandle h = mono_profiler_state.profilers; h; h = h->next) { \ + MonoProfiler ## type ## Callback cb = h->name ## _cb; \ + if (cb) \ + cb args; \ + } \ + } +#define MONO_PROFILER_EVENT_0(name, type) \ + _MONO_PROFILER_EVENT(name, type, (void), (h->prof)) +#define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \ + _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name), (h->prof, arg1_name)) +#define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \ + _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name, arg2_type arg2_name), (h->prof, arg1_name, arg2_name)) +#define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \ + _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name), (h->prof, arg1_name, arg2_name, arg3_name)) +#define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \ + _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name, arg2_type arg2_name, arg3_type arg3_name, arg4_type arg4_name), (h->prof, arg1_name, arg2_name, arg3_name, arg4_name)) +#include +#undef MONO_PROFILER_EVENT_0 +#undef MONO_PROFILER_EVENT_1 +#undef MONO_PROFILER_EVENT_2 +#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. * - * The function \p func will be invoked repeatedly with instances of the - * \c MonoProfileCoverageEntry structure. + * TODO: Remove this some day if we're OK with breaking compatibility. */ -void -mono_profiler_coverage_get (MonoProfiler *prof, MonoMethod *method, MonoProfileCoverageFunc func) -{ - MonoError error; - MonoProfileCoverageInfo* info = NULL; - int i, offset; - guint32 code_size; - const unsigned char *start, *end, *cil_code; - MonoMethodHeader *header; - MonoProfileCoverageEntry entry; - MonoDebugMethodInfo *debug_minfo; - - mono_profiler_coverage_lock (); - if (coverage_hash) - info = (MonoProfileCoverageInfo *)g_hash_table_lookup (coverage_hash, method); - mono_profiler_coverage_unlock (); - - if (!info) - return; - - header = mono_method_get_header_checked (method, &error); - mono_error_assert_ok (&error); - start = mono_method_header_get_code (header, &code_size, NULL); - debug_minfo = mono_debug_lookup_method (method); - end = start + code_size; - for (i = 0; i < info->entries; ++i) { - cil_code = info->data [i].cil_code; - if (cil_code && cil_code >= start && cil_code < end) { - char *fname = NULL; - offset = cil_code - start; - entry.iloffset = offset; - entry.method = method; - entry.counter = info->data [i].count; - entry.line = entry.col = 1; - entry.filename = NULL; - if (debug_minfo) { - MonoDebugSourceLocation *location; - - location = mono_debug_method_lookup_location (debug_minfo, offset); - if (location) { - entry.line = location->row; - entry.col = location->column; - entry.filename = fname = g_strdup (location->source_file); - mono_debug_free_source_location (location); - } - } +typedef void *MonoLegacyProfiler; - func (prof, &entry); - g_free (fname); - } - } - mono_metadata_free_mh (header); -} +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; +}; -typedef void (*ProfilerInitializer) (const char*); -#define INITIALIZER_NAME "mono_profiler_startup" +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 gboolean -load_profiler (MonoDl *pmodule, const char *desc, const char *symbol) +static void +shutdown_cb (MonoProfiler *prof) { - char *err; - ProfilerInitializer func; + prof->shutdown_callback (prof->profiler); +} - if (!pmodule) - return FALSE; +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 ((err = mono_dl_symbol (pmodule, symbol, (gpointer *) &func))) { - g_free (err); - return FALSE; - } else { - func (desc); - } - return TRUE; + if (callback) + mono_profiler_set_runtime_shutdown_end_callback (current->handle, shutdown_cb); } -static gboolean -load_embedded_profiler (const char *desc, const char *name) +static void +thread_start_cb (MonoProfiler *prof, uintptr_t tid) { - char *err = NULL; - char *symbol; - MonoDl *pmodule = NULL; - gboolean result; + prof->thread_start (prof->profiler, tid); +} - /* - * 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); - return FALSE; - } +static void +thread_stop_cb (MonoProfiler *prof, uintptr_t tid) +{ + prof->thread_end (prof->profiler, tid); +} - symbol = g_strdup_printf (INITIALIZER_NAME "_%s", name); - result = load_profiler (pmodule, desc, symbol); - g_free (symbol); +void +mono_profiler_install_thread (MonoLegacyProfileThreadFunc start, MonoLegacyProfileThreadFunc end) +{ + current->thread_start = start; + current->thread_end = end; - return result; + 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); } -// 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) +static void +gc_event_cb (MonoProfiler *prof, MonoProfilerGCEvent event, uint32_t generation) { - MonoDl *pmodule = NULL; - char* path; - char *err; - void *iter; + prof->gc_event (prof->profiler, event, generation); +} - mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "Attempting to load profiler %s from %s (desc %s)", libname, directory, desc); +static void +gc_resize_cb (MonoProfiler *prof, uintptr_t size) +{ + prof->gc_heap_resize (prof->profiler, size); +} - iter = NULL; - err = NULL; - while ((path = mono_dl_build_path (directory, libname, &iter))) { - 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) - return load_profiler (pmodule, desc, INITIALIZER_NAME); - } - - return FALSE; +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 gboolean -load_profiler_from_mono_installation (const char *libname, const char *desc) +static void +jit_done_cb (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo) { - char *err = NULL; - 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); - return FALSE; + prof->jit_end2 (prof->profiler, method, jinfo, 0); } -/** - * mono_profiler_load: - * \param desc arguments to configure the profiler - * - * Invoke this method to initialize the profiler. This will drive the - * loading of the internal ("default") or any external profilers. - * - * This routine is invoked by Mono's driver, but must be called manually - * if you embed Mono into your application. - */ -void -mono_profiler_load (const char *desc) +static void +jit_failed_cb (MonoProfiler *prof, MonoMethod *method) { - char *cdesc = NULL; - mono_gc_base_init (); + prof->jit_end2 (prof->profiler, method, NULL, 1); +} - if (!desc || (strcmp ("default", desc) == 0)) { - desc = "log:report"; - } - /* 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); - } - { - const char* col = strchr (desc, ':'); - char* libname; - char *mname; - gboolean res = FALSE; - - if (col != NULL) { - mname = (char *)g_memdup (desc, col - desc + 1); - mname [col - desc] = 0; - } else { - mname = g_strdup (desc); - } - if (!load_embedded_profiler (desc, mname)) { - libname = g_strdup_printf ("mono-profiler-%s", mname); - 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) - 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 (mname); +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); } - g_free (cdesc); } +void +mono_profiler_set_events (int flags) +{ + /* Do nothing. */ +}