X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fprofiler.c;h=7e050c1ed3700622ba4b219a3df8cd3b47f32a1c;hb=dcc01356ae5eb56e34d67cc439ea16ee2171e792;hp=d9bda6f7292079a556dde092e97cb71635fdcb61;hpb=5bbfa8860b090e465a3aa45edeb9c94481ef1a22;p=mono.git diff --git a/mono/metadata/profiler.c b/mono/metadata/profiler.c index d9bda6f7292..7e050c1ed37 100644 --- a/mono/metadata/profiler.c +++ b/mono/metadata/profiler.c @@ -4,8 +4,8 @@ * Author: * Paolo Molaro (lupus@ximian.com) * - * (C) 2001-2003 Ximian, Inc. - * (C) 2003-2006 Novell, Inc. + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) */ #include "config.h" @@ -18,59 +18,93 @@ #include "mono/metadata/domain-internals.h" #include "mono/metadata/gc-internal.h" #include "mono/io-layer/io-layer.h" +#include "mono/utils/mono-dl.h" #include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TIME_H #include -#include +#endif #ifdef HAVE_BACKTRACE_SYMBOLS #include #endif -static MonoProfiler * current_profiler = NULL; - -static MonoProfileAppDomainFunc domain_start_load; -static MonoProfileAppDomainResult domain_end_load; -static MonoProfileAppDomainFunc domain_start_unload; -static MonoProfileAppDomainFunc domain_end_unload; - -static MonoProfileAssemblyFunc assembly_start_load; -static MonoProfileAssemblyResult assembly_end_load; -static MonoProfileAssemblyFunc assembly_start_unload; -static MonoProfileAssemblyFunc assembly_end_unload; - -static MonoProfileModuleFunc module_start_load; -static MonoProfileModuleResult module_end_load; -static MonoProfileModuleFunc module_start_unload; -static MonoProfileModuleFunc module_end_unload; - -static MonoProfileClassFunc class_start_load; -static MonoProfileClassResult class_end_load; -static MonoProfileClassFunc class_start_unload; -static MonoProfileClassFunc class_end_unload; - -static MonoProfileMethodFunc jit_start; -static MonoProfileMethodResult jit_end; -static MonoProfileJitResult jit_end2; -static MonoProfileMethodResult man_unman_transition; -static MonoProfileAllocFunc allocation_cb; -static MonoProfileStatFunc statistical_cb; -static MonoProfileMethodFunc method_enter; -static MonoProfileMethodFunc method_leave; - -static MonoProfileThreadFunc thread_start; -static MonoProfileThreadFunc thread_end; - -static MonoProfileCoverageFilterFunc coverage_filter_cb; - -static MonoProfileFunc shutdown_callback; +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; + + 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; + + MonoProfileCoverageFilterFunc coverage_filter_cb; + + MonoProfileFunc shutdown_callback; + + MonoProfileGCFunc gc_event; + MonoProfileGCResizeFunc gc_heap_resize; + MonoProfileGCMoveFunc gc_moves; + + MonoProfileFunc runtime_initialized_event; + + MonoProfilerCodeChunkNew code_chunk_new; + MonoProfilerCodeChunkDestroy code_chunk_destroy; + MonoProfilerCodeBufferNew code_buffer_new; +}; -static MonoProfileGCFunc gc_event; -static MonoProfileGCResizeFunc gc_heap_resize; +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; -/* this is directly accessible to other mono libs. */ +/* this is directly accessible to other mono libs. + * It is the ORed value of all the profiler's events. + */ MonoProfileFlags mono_profiler_events; /** @@ -87,11 +121,13 @@ MonoProfileFlags mono_profiler_events; void mono_profiler_install (MonoProfiler *prof, MonoProfileFunc callback) { - if (current_profiler) - g_error ("profiler already setup"); - current_profiler = prof; - shutdown_callback = callback; - InitializeCriticalSection (&profiler_coverage_mutex); + ProfilerDesc *desc = g_new0 (ProfilerDesc, 1); + if (!prof_list) + InitializeCriticalSection (&profiler_coverage_mutex); + desc->profiler = prof; + desc->shutdown_callback = callback; + desc->next = prof_list; + prof_list = desc; } /** @@ -111,7 +147,13 @@ mono_profiler_install (MonoProfiler *prof, MonoProfileFunc callback) void mono_profiler_set_events (MonoProfileFlags events) { - mono_profiler_events = events; + ProfilerDesc *prof; + MonoProfileFlags value = 0; + if (prof_list) + prof_list->events = events; + for (prof = prof_list; prof; prof = prof->next) + value |= prof->events; + mono_profiler_events = value; } /** @@ -137,8 +179,10 @@ mono_profiler_get_events (void) void mono_profiler_install_enter_leave (MonoProfileMethodFunc enter, MonoProfileMethodFunc fleave) { - method_enter = enter; - method_leave = fleave; + if (!prof_list) + return; + prof_list->method_enter = enter; + prof_list->method_leave = fleave; } /** @@ -152,45 +196,126 @@ mono_profiler_install_enter_leave (MonoProfileMethodFunc enter, MonoProfileMetho void mono_profiler_install_jit_compile (MonoProfileMethodFunc start, MonoProfileMethodResult end) { - jit_start = start; - jit_end = end; + if (!prof_list) + return; + prof_list->jit_start = start; + prof_list->jit_end = end; } void mono_profiler_install_jit_end (MonoProfileJitResult end) { - jit_end2 = end; + if (!prof_list) + return; + prof_list->jit_end2 = end; +} + +void +mono_profiler_install_method_free (MonoProfileMethodFunc callback) +{ + if (!prof_list) + return; + prof_list->method_free = callback; +} + +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; } void mono_profiler_install_thread (MonoProfileThreadFunc start, MonoProfileThreadFunc end) { - thread_start = start; - thread_end = end; + if (!prof_list) + return; + prof_list->thread_start = start; + prof_list->thread_end = end; } void mono_profiler_install_transition (MonoProfileMethodResult callback) { - man_unman_transition = callback; + if (!prof_list) + return; + prof_list->man_unman_transition = callback; } void mono_profiler_install_allocation (MonoProfileAllocFunc callback) { - allocation_cb = callback; + if (!prof_list) + return; + prof_list->allocation_cb = callback; +} + +void +mono_profiler_install_monitor (MonoProfileMonitorFunc callback) +{ + if (!prof_list) + return; + prof_list->monitor_event_cb = callback; } void mono_profiler_install_statistical (MonoProfileStatFunc callback) { - statistical_cb = callback; + if (!prof_list) + return; + prof_list->statistical_cb = callback; +} + +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; +} + +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; + } +} + +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; + } +} + +void mono_profiler_install_exception (MonoProfileExceptionFunc throw_callback, MonoProfileMethodFunc exc_method_leave, MonoProfileExceptionClauseFunc clause_callback) +{ + 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; } void mono_profiler_install_coverage_filter (MonoProfileCoverageFilterFunc callback) { - coverage_filter_cb = callback; + if (!prof_list) + return; + prof_list->coverage_filter_cb = callback; } void @@ -198,260 +323,521 @@ mono_profiler_install_appdomain (MonoProfileAppDomainFunc start_load, MonoProf MonoProfileAppDomainFunc start_unload, MonoProfileAppDomainFunc end_unload) { - domain_start_load = start_load; - domain_end_load = end_load; - domain_start_unload = start_unload; - domain_end_unload = end_unload; + 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; } void mono_profiler_install_assembly (MonoProfileAssemblyFunc start_load, MonoProfileAssemblyResult end_load, MonoProfileAssemblyFunc start_unload, MonoProfileAssemblyFunc end_unload) { - assembly_start_load = start_load; - assembly_end_load = end_load; - assembly_start_unload = start_unload; - assembly_end_unload = 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; } void mono_profiler_install_module (MonoProfileModuleFunc start_load, MonoProfileModuleResult end_load, MonoProfileModuleFunc start_unload, MonoProfileModuleFunc end_unload) { - module_start_load = start_load; - module_end_load = end_load; - module_start_unload = start_unload; - module_end_unload = 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; } void mono_profiler_install_class (MonoProfileClassFunc start_load, MonoProfileClassResult end_load, MonoProfileClassFunc start_unload, MonoProfileClassFunc end_unload) { - class_start_load = start_load; - class_end_load = end_load; - class_start_unload = start_unload; - class_end_unload = 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; } void mono_profiler_method_enter (MonoMethod *method) { - if ((mono_profiler_events & MONO_PROFILE_ENTER_LEAVE) && method_enter) - method_enter (current_profiler, 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); + } } void mono_profiler_method_leave (MonoMethod *method) { - if ((mono_profiler_events & MONO_PROFILE_ENTER_LEAVE) && method_leave) - method_leave (current_profiler, 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); + } } void mono_profiler_method_jit (MonoMethod *method) { - if ((mono_profiler_events & MONO_PROFILE_JIT_COMPILATION) && jit_start) - jit_start (current_profiler, method); + 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); + } } void mono_profiler_method_end_jit (MonoMethod *method, MonoJitInfo* jinfo, int result) { - if ((mono_profiler_events & MONO_PROFILE_JIT_COMPILATION)) { - if (jit_end) - jit_end (current_profiler, method, result); - if (jit_end2) - jit_end2 (current_profiler, method, jinfo, 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); + } + } +} + +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); + } +} + +void +mono_profiler_method_start_invoke (MonoMethod *method) +{ + 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); + } +} + +void +mono_profiler_method_end_invoke (MonoMethod *method) +{ + 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); } } void mono_profiler_code_transition (MonoMethod *method, int result) { - if ((mono_profiler_events & MONO_PROFILE_TRANSITIONS) && man_unman_transition) - man_unman_transition (current_profiler, method, result); + 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); + } } void mono_profiler_allocation (MonoObject *obj, MonoClass *klass) { - if ((mono_profiler_events & MONO_PROFILE_ALLOCATIONS) && allocation_cb) - allocation_cb (current_profiler, obj, klass); + 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, klass); + } +} + +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); + } } void mono_profiler_stat_hit (guchar *ip, void *context) { - if ((mono_profiler_events & MONO_PROFILE_STATISTICAL) && statistical_cb) - statistical_cb (current_profiler, ip, 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); + } +} + +void +mono_profiler_stat_call_chain (int call_chain_depth, guchar **ips, void *context) +{ + 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); + } +} + +void +mono_profiler_exception_thrown (MonoObject *exception) +{ + 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); + } +} + +void +mono_profiler_exception_method_leave (MonoMethod *method) +{ + 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); + } +} + +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); + } } void mono_profiler_thread_start (gsize tid) { - if ((mono_profiler_events & MONO_PROFILE_THREADS) && thread_start) - thread_start (current_profiler, 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); + } } void mono_profiler_thread_end (gsize tid) { - if ((mono_profiler_events & MONO_PROFILE_THREADS) && thread_end) - thread_end (current_profiler, 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); + } } void mono_profiler_assembly_event (MonoAssembly *assembly, int code) { - if (!(mono_profiler_events & MONO_PROFILE_ASSEMBLY_EVENTS)) - return; - - switch (code) { - case MONO_PROFILE_START_LOAD: - if (assembly_start_load) - assembly_start_load (current_profiler, assembly); - break; - case MONO_PROFILE_START_UNLOAD: - if (assembly_start_unload) - assembly_start_unload (current_profiler, assembly); - break; - case MONO_PROFILE_END_UNLOAD: - if (assembly_end_unload) - assembly_end_unload (current_profiler, assembly); - break; - default: - g_assert_not_reached (); + 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 (); + } } } void mono_profiler_assembly_loaded (MonoAssembly *assembly, int result) { - if ((mono_profiler_events & MONO_PROFILE_ASSEMBLY_EVENTS) && assembly_end_load) - assembly_end_load (current_profiler, assembly, 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); + } +} + +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); + } } void mono_profiler_module_event (MonoImage *module, int code) { - if (!(mono_profiler_events & MONO_PROFILE_MODULE_EVENTS)) - return; - - switch (code) { - case MONO_PROFILE_START_LOAD: - if (module_start_load) - module_start_load (current_profiler, module); - break; - case MONO_PROFILE_START_UNLOAD: - if (module_start_unload) - module_start_unload (current_profiler, module); - break; - case MONO_PROFILE_END_UNLOAD: - if (module_end_unload) - module_end_unload (current_profiler, module); - break; - default: - g_assert_not_reached (); + 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 (); + } } } void mono_profiler_module_loaded (MonoImage *module, int result) { - if ((mono_profiler_events & MONO_PROFILE_MODULE_EVENTS) && module_end_load) - module_end_load (current_profiler, module, 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); + } } void mono_profiler_class_event (MonoClass *klass, int code) { - if (!(mono_profiler_events & MONO_PROFILE_CLASS_EVENTS)) - return; - - switch (code) { - case MONO_PROFILE_START_LOAD: - if (class_start_load) - class_start_load (current_profiler, klass); - break; - case MONO_PROFILE_START_UNLOAD: - if (class_start_unload) - class_start_unload (current_profiler, klass); - break; - case MONO_PROFILE_END_UNLOAD: - if (class_end_unload) - class_end_unload (current_profiler, klass); - break; - default: - g_assert_not_reached (); + 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 (); + } } } void mono_profiler_class_loaded (MonoClass *klass, int result) { - if ((mono_profiler_events & MONO_PROFILE_CLASS_EVENTS) && class_end_load) - class_end_load (current_profiler, klass, 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); + } } void mono_profiler_appdomain_event (MonoDomain *domain, int code) { - if (!(mono_profiler_events & MONO_PROFILE_APPDOMAIN_EVENTS)) - return; - - switch (code) { - case MONO_PROFILE_START_LOAD: - if (domain_start_load) - domain_start_load (current_profiler, domain); - break; - case MONO_PROFILE_START_UNLOAD: - if (domain_start_unload) - domain_start_unload (current_profiler, domain); - break; - case MONO_PROFILE_END_UNLOAD: - if (domain_end_unload) - domain_end_unload (current_profiler, domain); - break; - default: - g_assert_not_reached (); + 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 (); + } } } void mono_profiler_appdomain_loaded (MonoDomain *domain, int result) { - if ((mono_profiler_events & MONO_PROFILE_APPDOMAIN_EVENTS) && domain_end_load) - domain_end_load (current_profiler, domain, 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); + } } void mono_profiler_shutdown (void) { - if (current_profiler && shutdown_callback) - shutdown_callback (current_profiler); + ProfilerDesc *prof; + for (prof = prof_list; prof; prof = prof->next) { + if (prof->shutdown_callback) + prof->shutdown_callback (prof->profiler); + } } void mono_profiler_gc_heap_resize (gint64 new_size) { - if ((mono_profiler_events & MONO_PROFILE_GC) && gc_heap_resize) - gc_heap_resize (current_profiler, new_size); + 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); + } } void mono_profiler_gc_event (MonoGCEvent event, int generation) { - if ((mono_profiler_events & MONO_PROFILE_GC) && gc_event) - gc_event (current_profiler, event, generation); + 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); + } +} + +void +mono_profiler_gc_moves (void **objects, int num) +{ + 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); + } } void mono_profiler_install_gc (MonoProfileGCFunc callback, MonoProfileGCResizeFunc heap_resize_callback) { mono_gc_enable_events (); - gc_event = callback; - gc_heap_resize = heap_resize_callback; + if (!prof_list) + return; + prof_list->gc_event = callback; + prof_list->gc_heap_resize = heap_resize_callback; +} + +/** + * mono_profiler_install_gc_moves: + * @callback: callback function + * + * Install the @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) +{ + if (!prof_list) + return; + prof_list->gc_moves = callback; +} + +void +mono_profiler_install_runtime_initialized (MonoProfileFunc runtime_initialized_callback) +{ + if (!prof_list) + return; + prof_list->runtime_initialized_event = runtime_initialized_callback; +} + +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); + } +} + +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); + } +} + +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); + } +} + +void +mono_profiler_install_code_buffer_new (MonoProfilerCodeBufferNew callback) { + if (!prof_list) + return; + prof_list->code_buffer_new = callback; +} + +void +mono_profiler_install_iomap (MonoProfileIomapFunc callback) +{ + if (!prof_list) + return; + prof_list->iomap_cb = callback; +} + +void +mono_profiler_code_buffer_new (gpointer buffer, int size, MonoProfilerCodeBufferType type, void *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); + } } static GHashTable *coverage_hash = NULL; @@ -460,10 +846,19 @@ MonoProfileCoverageInfo* mono_profiler_coverage_alloc (MonoMethod *method, int entries) { MonoProfileCoverageInfo *res; - - if (coverage_filter_cb) - if (! (*coverage_filter_cb) (current_profiler, method)) - return NULL; + 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; + } + if (!instrument) + return NULL; mono_profiler_coverage_lock (); if (!coverage_hash) @@ -537,6 +932,7 @@ mono_profiler_coverage_get (MonoProfiler *prof, MonoMethod *method, MonoProfileC 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; @@ -550,15 +946,16 @@ mono_profiler_coverage_get (MonoProfiler *prof, MonoMethod *method, MonoProfileC if (location) { entry.line = location->row; entry.col = location->column; - entry.filename = g_strdup (location->source_file); + entry.filename = fname = g_strdup (location->source_file); mono_debug_free_source_location (location); } } func (prof, &entry); - g_free (entry.filename); + g_free (fname); } } + mono_metadata_free_mh (header); } #ifndef DISABLE_PROFILER @@ -695,6 +1092,7 @@ typedef struct _LastCallerInfo LastCallerInfo; struct _MonoProfiler { GHashTable *methods; MonoMemPool *mempool; + GSList *domains; /* info about JIT time */ MONO_TIMER_TYPE jit_timer; double jit_time; @@ -811,6 +1209,14 @@ method_get_name (MonoMethod* method) static void output_callers (MethodProfile *p); +/* This isn't defined on older glib versions and on some platforms */ +#ifndef G_GUINT64_FORMAT +#define G_GUINT64_FORMAT "ul" +#endif +#ifndef G_GINT64_FORMAT +#define G_GINT64_FORMAT "lld" +#endif + static void output_profile (GList *funcs) { @@ -829,7 +1235,7 @@ output_profile (GList *funcs) m = method_get_name (p->method); fprintf (poutput, "########################\n"); fprintf (poutput, "% 8.3f ", (double) (p->total * 1000)); - fprintf (poutput, "%7llu ", (unsigned long long)p->count); + fprintf (poutput, "%7" G_GUINT64_FORMAT " ", (guint64)p->count); fprintf (poutput, "% 8.3f ", (double) (p->total * 1000)/(double)p->count); fprintf (poutput, " %s\n", m); @@ -837,7 +1243,7 @@ output_profile (GList *funcs) /* callers */ output_callers (p); } - fprintf (poutput, "Total number of calls: %lld\n", (long long)total_calls); + fprintf (poutput, "Total number of calls: %" G_GINT64_FORMAT "\n", (gint64)total_calls); } typedef struct { @@ -927,10 +1333,12 @@ output_callers (MethodProfile *p) { } } -/* This isn't defined on older glib versions and on some platforms */ -#ifndef G_GUINT64_FORMAT -#define G_GUINT64_FORMAT "ul" -#endif +static int moved_objects = 0; +static void +simple_gc_move (MonoProfiler *prof, void **objects, int num) +{ + moved_objects += num / 2; +} static void output_newobj_profile (GList *proflist) @@ -979,6 +1387,7 @@ output_newobj_profile (GList *proflist) output_callers (mp); } fprintf (poutput, "Total memory allocated: %" G_GUINT64_FORMAT " KB\n", total / 1024); + fprintf (poutput, "Objects copied: %d\n", moved_objects); } static void @@ -1123,7 +1532,7 @@ simple_allocation (MonoProfiler *prof, MonoObject *obj, MonoClass *klass) MonoMethod *caller = prof->callers->method; /* Otherwise all allocations are attributed to icall_wrapper_mono_object_new */ - if (caller->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) + if (caller->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE && prof->callers->next) caller = prof->callers->next->method; if (!(profile_info = g_hash_table_lookup (prof->methods, caller))) @@ -1228,6 +1637,17 @@ try_addr2line (const char* binary, gpointer ip) const char *addr_argv[] = {"addr2line", "-f", "-e", binary, NULL}; int child_pid; int ch_in, ch_out; +#ifdef __linux__ + char monobin [1024]; + /* non-linux platforms will need different code here */ + if (strcmp (binary, "mono") == 0) { + int count = readlink ("/proc/self/exe", monobin, sizeof (monobin)); + if (count >= 0 && count < sizeof (monobin)) { + monobin [count] = 0; + addr_argv [3] = monobin; + } + } +#endif if (!g_spawn_async_with_pipes (NULL, (char**)addr_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &child_pid, &ch_in, &ch_out, NULL, NULL)) { return g_strdup (binary); @@ -1257,7 +1677,7 @@ try_addr2line (const char* binary, gpointer ip) } static void -stat_prof_report (void) +stat_prof_report (MonoProfiler *prof) { MonoJitInfo *ji; int count = prof_counts; @@ -1265,12 +1685,19 @@ stat_prof_report (void) char *mn; gpointer ip; GList *tmp, *sorted = NULL; + GSList *l; int pcount = ++ prof_counts; prof_counts = MAX_PROF_SAMPLES; for (i = 0; i < count; ++i) { ip = prof_addresses [i]; ji = mono_jit_info_table_find (mono_domain_get (), ip); + + if (!ji) { + for (l = prof->domains; l && !ji; l = l->next) + ji = mono_jit_info_table_find (l->data, ip); + } + if (ji) { mn = mono_method_full_name (ji->method, TRUE); } else { @@ -1321,16 +1748,23 @@ stat_prof_report (void) g_list_free (sorted); } +static void +simple_appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result) +{ + prof->domains = g_slist_prepend (prof->domains, domain); +} + static void simple_appdomain_unload (MonoProfiler *prof, MonoDomain *domain) { /* FIXME: we should actually record partial data for each domain, - * since the ip->ji->method mappings are going away at domain unload time. + * but at this point it's must easier using the new logging profiler. */ - if (domain == mono_get_root_domain ()) - stat_prof_report (); + mono_profiler_shutdown (); } +static gint32 simple_shutdown_done = FALSE; + static void simple_shutdown (MonoProfiler *prof) { @@ -1338,7 +1772,24 @@ simple_shutdown (MonoProfiler *prof) MonoProfiler *tprof; GSList *tmp; char *str; + gint32 see_shutdown_done; + +#ifndef HOST_WIN32 + mono_thread_attach(mono_get_root_domain()); +#endif + + // Make sure we execute simple_shutdown only once + see_shutdown_done = InterlockedExchange(& simple_shutdown_done, TRUE); + if (see_shutdown_done) + return; + + if (mono_profiler_events & MONO_PROFILE_STATISTICAL) { + stat_prof_report (prof); + } + // Stop all incoming events + mono_profiler_set_events (0); + for (tmp = prof->per_thread; tmp; tmp = tmp->next) { tprof = tmp->data; merge_thread_data (prof, tprof); @@ -1388,12 +1839,14 @@ mono_profiler_install_simple (const char *desc) for (ptr = args; ptr && *ptr; ptr++) { const char *arg = *ptr; + // Alwais listen to appdomaon events to shutdown at the first unload + flags |= MONO_PROFILE_APPDOMAIN_EVENTS; if (!strcmp (arg, "time")) - flags |= MONO_PROFILE_ENTER_LEAVE; + flags |= MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_EXCEPTIONS; else if (!strcmp (arg, "alloc")) flags |= MONO_PROFILE_ALLOCATIONS; else if (!strcmp (arg, "stat")) - flags |= MONO_PROFILE_STATISTICAL | MONO_PROFILE_APPDOMAIN_EVENTS; + flags |= MONO_PROFILE_STATISTICAL; else if (!strcmp (arg, "jit")) flags |= MONO_PROFILE_JIT_COMPILATION; else if (strncmp (arg, "file=", 5) == 0) { @@ -1408,6 +1861,12 @@ mono_profiler_install_simple (const char *desc) } } } + if (flags & MONO_PROFILE_ALLOCATIONS) + flags |= MONO_PROFILE_GC_MOVES; + if (flags & MONO_PROFILE_ALLOCATIONS) + flags |= MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_EXCEPTIONS; + if (!flags) + flags = MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_JIT_COMPILATION | MONO_PROFILE_EXCEPTIONS; prof = create_profiler (); ALLOC_PROFILER (); @@ -1419,10 +1878,12 @@ mono_profiler_install_simple (const char *desc) mono_profiler_install (prof, simple_shutdown); mono_profiler_install_enter_leave (simple_method_enter, simple_method_leave); + mono_profiler_install_exception (NULL, simple_method_leave, NULL); mono_profiler_install_jit_compile (simple_method_jit, simple_method_end_jit); mono_profiler_install_allocation (simple_allocation); - mono_profiler_install_appdomain (NULL, NULL, simple_appdomain_unload, NULL); + mono_profiler_install_appdomain (NULL, simple_appdomain_load, simple_appdomain_unload, NULL); mono_profiler_install_statistical (simple_stat_hit); + mono_profiler_install_gc_moves (simple_gc_move); mono_profiler_set_events (flags); } @@ -1444,6 +1905,8 @@ typedef void (*ProfilerInitializer) (const char*); void mono_profiler_load (const char *desc) { + mono_gc_base_init (); + #ifndef DISABLE_PROFILER if (!desc || (strcmp ("default", desc) == 0) || (strncmp (desc, "default:", 8) == 0)) { mono_profiler_install_simple (desc); @@ -1455,31 +1918,42 @@ mono_profiler_load (const char *desc) } #endif { - GModule *pmodule; + MonoDl *pmodule = NULL; const char* col = strchr (desc, ':'); char* libname; char* path; char *mname; + char *err; + void *iter; if (col != NULL) { - mname = g_memdup (desc, col - desc); + mname = g_memdup (desc, col - desc + 1); mname [col - desc] = 0; } else { mname = g_strdup (desc); } libname = g_strdup_printf ("mono-profiler-%s", mname); - path = g_module_build_path (NULL, libname); - pmodule = g_module_open (path, G_MODULE_BIND_LAZY); - if (pmodule) { - ProfilerInitializer func; - if (!g_module_symbol (pmodule, INITIALIZER_NAME, (gpointer *)&func)) { - g_warning ("Cannot find initializer function %s in profiler module: %s", INITIALIZER_NAME, libname); - } else { - func (desc); + 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; } - } else { - g_warning ("Error loading profiler module '%s': %s", libname, g_module_error ()); + g_free (path); + } + if (!pmodule) { + g_warning ("Error loading profiler module '%s': %s", libname, err); + g_free (err); } - g_free (libname); g_free (mname); g_free (path);