2 * Licensed to the .NET Foundation under one or more agreements.
3 * The .NET Foundation licenses this file to you under the MIT license.
4 * See the LICENSE file in the project root for more information.
7 #include <mono/metadata/assembly.h>
8 #include <mono/metadata/gc-internals.h>
9 #include <mono/metadata/mono-config-dirs.h>
10 #include <mono/metadata/mono-debug.h>
11 #include <mono/metadata/profiler-private.h>
12 #include <mono/metadata/debug-internals.h>
13 #include <mono/utils/mono-dl.h>
14 #include <mono/utils/mono-error-internals.h>
15 #include <mono/utils/mono-logger-internals.h>
17 MonoProfilerState mono_profiler_state;
19 typedef void (*MonoProfilerInitializer) (const char *);
21 #define OLD_INITIALIZER_NAME "mono_profiler_startup"
22 #define NEW_INITIALIZER_NAME "mono_profiler_init"
25 load_profiler (MonoDl *module, const char *name, const char *desc)
30 char *err, *old_name = g_strdup_printf (OLD_INITIALIZER_NAME);
31 MonoProfilerInitializer func;
33 if (!(err = mono_dl_symbol (module, old_name, (gpointer) &func))) {
34 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);
42 char *new_name = g_strdup_printf (NEW_INITIALIZER_NAME "_%s", name);
44 if ((err = mono_dl_symbol (module, new_name, (gpointer *) &func))) {
58 load_profiler_from_executable (const char *name, const char *desc)
63 * Some profilers (such as ours) may need to call back into the runtime
64 * from their sampling callback (which is called in async-signal context).
65 * They need to be able to know that all references back to the runtime
66 * have been resolved; otherwise, calling runtime functions may result in
67 * invoking the dynamic linker which is not async-signal-safe. Passing
68 * MONO_DL_EAGER will ask the dynamic linker to resolve everything upfront.
70 MonoDl *module = mono_dl_open (NULL, MONO_DL_EAGER, &err);
73 mono_profiler_printf_err ("Could not open main executable: %s", err);
78 return load_profiler (module, name, desc);
82 load_profiler_from_directory (const char *directory, const char *libname, const char *name, const char *desc)
87 while ((path = mono_dl_build_path (directory, libname, &iter))) {
88 // See the comment in load_embedded_profiler ().
89 MonoDl *module = mono_dl_open (path, MONO_DL_EAGER, NULL);
94 return load_profiler (module, name, desc);
101 load_profiler_from_installation (const char *libname, const char *name, const char *desc)
104 MonoDl *module = mono_dl_open_runtime_lib (libname, MONO_DL_EAGER, &err);
109 return load_profiler (module, name, desc);
115 mono_profiler_load (const char *desc)
117 if (!desc || !strcmp ("default", desc))
120 const char *col = strchr (desc, ':');
124 mname = (char *) g_memdup (desc, col - desc + 1);
125 mname [col - desc] = 0;
127 mname = g_strdup (desc);
129 if (!load_profiler_from_executable (mname, desc)) {
130 char *libname = g_strdup_printf ("mono-profiler-%s", mname);
131 gboolean res = load_profiler_from_installation (libname, mname, desc);
133 if (!res && mono_config_get_assemblies_dir ())
134 res = load_profiler_from_directory (mono_assembly_getrootdir (), libname, mname, desc);
137 res = load_profiler_from_directory (NULL, libname, mname, desc);
140 mono_profiler_printf_err ("The '%s' profiler wasn't found in the main executable nor could it be loaded from '%s'.", mname, libname);
149 mono_profiler_create (MonoProfiler *prof)
151 MonoProfilerHandle handle = g_new0 (struct _MonoProfilerDesc, 1);
154 handle->next = mono_profiler_state.profilers;
156 mono_profiler_state.profilers = handle;
162 mono_profiler_set_cleanup_callback (MonoProfilerHandle handle, MonoProfilerCleanupCallback cb)
164 InterlockedWritePointer (&handle->cleanup_callback, (gpointer) cb);
168 mono_profiler_set_coverage_filter_callback (MonoProfilerHandle handle, MonoProfilerCoverageFilterCallback cb)
170 InterlockedWritePointer (&handle->coverage_filter, (gpointer) cb);
174 mono_profiler_enable_coverage (void)
176 if (mono_profiler_state.startup_done)
179 mono_os_mutex_init (&mono_profiler_state.coverage_mutex);
180 mono_profiler_state.coverage_hash = g_hash_table_new (NULL, NULL);
182 if (!mono_debug_enabled ())
183 mono_debug_init (MONO_DEBUG_FORMAT_MONO);
185 return mono_profiler_state.code_coverage = TRUE;
191 mono_os_mutex_lock (&mono_profiler_state.coverage_mutex);
195 coverage_unlock (void)
197 mono_os_mutex_unlock (&mono_profiler_state.coverage_mutex);
201 mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method, MonoProfilerCoverageCallback cb)
203 if (!mono_profiler_state.code_coverage)
208 MonoProfilerCoverageInfo *info = g_hash_table_lookup (mono_profiler_state.coverage_hash, method);
213 MonoMethodHeader *header = mono_method_get_header_checked (method, &error);
214 mono_error_assert_ok (&error);
218 const unsigned char *start = mono_method_header_get_code (header, &size, NULL);
219 const unsigned char *end = start + size;
220 MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method);
226 GPtrArray *source_file_list;
227 MonoSymSeqPoint *sym_seq_points;
229 /* Return 0 counts for all locations */
231 mono_debug_get_seq_points (minfo, &source_file, &source_file_list, &source_files, &sym_seq_points, &n_il_offsets);
232 for (i = 0; i < n_il_offsets; ++i) {
233 MonoSymSeqPoint *sp = &sym_seq_points [i];
234 const char *srcfile = "";
236 if (source_files [i] != -1) {
237 MonoDebugSourceInfo *sinfo = (MonoDebugSourceInfo *)g_ptr_array_index (source_file_list, source_files [i]);
238 srcfile = sinfo->source_file;
241 MonoProfilerCoverageData data = {
243 .il_offset = sp->il_offset,
245 .file_name = srcfile,
250 cb (handle->prof, &data);
253 g_free (source_files);
254 g_free (sym_seq_points);
255 g_ptr_array_free (source_file_list, TRUE);
257 mono_metadata_free_mh (header);
261 for (guint32 i = 0; i < info->entries; i++) {
262 guchar *cil_code = info->data [i].cil_code;
264 if (cil_code && cil_code >= start && cil_code < end) {
265 guint32 offset = cil_code - start;
267 MonoProfilerCoverageData data = {
270 .counter = info->data [i].count,
276 MonoDebugSourceLocation *loc = mono_debug_method_lookup_location (minfo, offset);
279 data.file_name = g_strdup (loc->source_file);
280 data.line = loc->row;
281 data.column = loc->column;
283 mono_debug_free_source_location (loc);
287 cb (handle->prof, &data);
289 g_free ((char *) data.file_name);
293 mono_metadata_free_mh (header);
298 MonoProfilerCoverageInfo *
299 mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries)
301 if (!mono_profiler_state.code_coverage)
304 if (method->wrapper_type)
307 gboolean cover = FALSE;
309 for (MonoProfilerHandle handle = mono_profiler_state.profilers; handle; handle = handle->next) {
310 MonoProfilerCoverageFilterCallback cb = handle->coverage_filter;
313 cover |= cb (handle->prof, method);
321 MonoProfilerCoverageInfo *info = g_malloc0 (sizeof (MonoProfilerCoverageInfo) + SIZEOF_VOID_P * 2 * entries);
323 info->entries = entries;
325 g_hash_table_insert (mono_profiler_state.coverage_hash, method, info);
333 mono_profiler_enable_sampling (MonoProfilerHandle handle)
335 if (mono_profiler_state.startup_done)
338 if (mono_profiler_state.sampling_owner)
341 mono_profiler_state.sampling_owner = handle;
342 mono_profiler_state.sample_mode = MONO_PROFILER_SAMPLE_MODE_NONE;
343 mono_profiler_state.sample_freq = 100;
344 mono_os_sem_init (&mono_profiler_state.sampling_semaphore, 0);
350 mono_profiler_set_sample_mode (MonoProfilerHandle handle, MonoProfilerSampleMode mode, uint32_t freq)
352 if (handle != mono_profiler_state.sampling_owner)
355 mono_profiler_state.sample_mode = mode;
356 mono_profiler_state.sample_freq = freq;
358 mono_profiler_sampling_thread_post ();
364 mono_profiler_get_sample_mode (MonoProfilerHandle handle, MonoProfilerSampleMode *mode, uint32_t *freq)
367 *mode = mono_profiler_state.sample_mode;
370 *freq = mono_profiler_state.sample_freq;
372 return handle == mono_profiler_state.sampling_owner;
376 mono_profiler_sampling_enabled (void)
378 return !!mono_profiler_state.sampling_owner;
382 mono_profiler_sampling_thread_post (void)
384 mono_os_sem_post (&mono_profiler_state.sampling_semaphore);
388 mono_profiler_sampling_thread_wait (void)
390 mono_os_sem_wait (&mono_profiler_state.sampling_semaphore, MONO_SEM_FLAGS_NONE);
394 mono_profiler_enable_allocations (void)
396 if (mono_profiler_state.startup_done)
399 return mono_profiler_state.allocations = TRUE;
403 mono_profiler_set_call_instrumentation_filter_callback (MonoProfilerHandle handle, MonoProfilerCallInstrumentationFilterCallback cb)
405 InterlockedWritePointer (&handle->call_instrumentation_filter, (gpointer) cb);
409 mono_profiler_enable_call_context_introspection (void)
411 if (mono_profiler_state.startup_done)
414 mono_profiler_state.context_enable ();
416 return mono_profiler_state.call_contexts = TRUE;
420 mono_profiler_call_context_get_this (MonoProfilerCallContext *context)
422 if (!mono_profiler_state.call_contexts)
425 return mono_profiler_state.context_get_this (context);
429 mono_profiler_call_context_get_argument (MonoProfilerCallContext *context, uint32_t position)
431 if (!mono_profiler_state.call_contexts)
434 return mono_profiler_state.context_get_argument (context, position);
438 mono_profiler_call_context_get_local (MonoProfilerCallContext *context, uint32_t position)
440 if (!mono_profiler_state.call_contexts)
443 return mono_profiler_state.context_get_local (context, position);
447 mono_profiler_call_context_get_result (MonoProfilerCallContext *context)
449 if (!mono_profiler_state.call_contexts)
452 return mono_profiler_state.context_get_result (context);
456 mono_profiler_call_context_free_buffer (void *buffer)
458 mono_profiler_state.context_free_buffer (buffer);
461 MonoProfilerCallInstrumentationFlags
462 mono_profiler_get_call_instrumentation_flags (MonoMethod *method)
464 MonoProfilerCallInstrumentationFlags flags = MONO_PROFILER_CALL_INSTRUMENTATION_NONE;
466 for (MonoProfilerHandle handle = mono_profiler_state.profilers; handle; handle = handle->next) {
467 MonoProfilerCallInstrumentationFilterCallback cb = handle->call_instrumentation_filter;
470 flags |= cb (handle->prof, method);
477 mono_profiler_started (void)
479 mono_profiler_state.startup_done = TRUE;
483 mono_profiler_cleanup (void)
485 for (MonoProfilerHandle handle = mono_profiler_state.profilers; handle; handle = handle->next) {
486 #define _MONO_PROFILER_EVENT(name) \
487 mono_profiler_set_ ## name ## _callback (handle, NULL); \
488 g_assert (!handle->name ## _cb);
489 #define MONO_PROFILER_EVENT_0(name, type) \
490 _MONO_PROFILER_EVENT(name)
491 #define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \
492 _MONO_PROFILER_EVENT(name)
493 #define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \
494 _MONO_PROFILER_EVENT(name)
495 #define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \
496 _MONO_PROFILER_EVENT(name)
497 #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \
498 _MONO_PROFILER_EVENT(name)
499 #include <mono/metadata/profiler-events.h>
500 #undef MONO_PROFILER_EVENT_0
501 #undef MONO_PROFILER_EVENT_1
502 #undef MONO_PROFILER_EVENT_2
503 #undef MONO_PROFILER_EVENT_3
504 #undef MONO_PROFILER_EVENT_4
505 #undef _MONO_PROFILER_EVENT
508 #define _MONO_PROFILER_EVENT(name, type) \
509 g_assert (!mono_profiler_state.name ## _count);
510 #define MONO_PROFILER_EVENT_0(name, type) \
511 _MONO_PROFILER_EVENT(name, type)
512 #define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \
513 _MONO_PROFILER_EVENT(name, type)
514 #define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \
515 _MONO_PROFILER_EVENT(name, type)
516 #define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \
517 _MONO_PROFILER_EVENT(name, type)
518 #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \
519 _MONO_PROFILER_EVENT(name, type)
520 #include <mono/metadata/profiler-events.h>
521 #undef MONO_PROFILER_EVENT_0
522 #undef MONO_PROFILER_EVENT_1
523 #undef MONO_PROFILER_EVENT_2
524 #undef MONO_PROFILER_EVENT_3
525 #undef MONO_PROFILER_EVENT_4
526 #undef _MONO_PROFILER_EVENT
528 MonoProfilerHandle head = mono_profiler_state.profilers;
531 MonoProfilerCleanupCallback cb = head->cleanup_callback;
536 MonoProfilerHandle cur = head;
542 if (mono_profiler_state.code_coverage) {
543 mono_os_mutex_destroy (&mono_profiler_state.coverage_mutex);
547 g_hash_table_iter_init (&iter, mono_profiler_state.coverage_hash);
549 MonoProfilerCoverageInfo *info;
551 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info))
554 g_hash_table_destroy (mono_profiler_state.coverage_hash);
557 if (mono_profiler_state.sampling_owner)
558 mono_os_sem_destroy (&mono_profiler_state.sampling_semaphore);
562 update_callback (volatile gpointer *location, gpointer new_, volatile gint32 *counter)
567 old = InterlockedReadPointer (location);
568 } while (InterlockedCompareExchangePointer (location, new_, old) != old);
571 * At this point, we could have installed a NULL callback while the counter
572 * is still non-zero, i.e. setting the callback and modifying the counter
573 * is not a single atomic operation. This is fine as we make sure callbacks
574 * are non-NULL before invoking them (see the code below that generates the
575 * raise functions), and besides, updating callbacks at runtime is an
576 * inherently racy operation.
580 InterlockedDecrement (counter);
583 InterlockedIncrement (counter);
586 #define _MONO_PROFILER_EVENT(name, type) \
588 mono_profiler_set_ ## name ## _callback (MonoProfilerHandle handle, MonoProfiler ## type ## Callback cb) \
590 update_callback (&handle->name ## _cb, (gpointer) cb, &mono_profiler_state.name ## _count); \
592 #define MONO_PROFILER_EVENT_0(name, type) \
593 _MONO_PROFILER_EVENT(name, type)
594 #define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \
595 _MONO_PROFILER_EVENT(name, type)
596 #define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \
597 _MONO_PROFILER_EVENT(name, type)
598 #define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \
599 _MONO_PROFILER_EVENT(name, type)
600 #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \
601 _MONO_PROFILER_EVENT(name, type)
602 #include <mono/metadata/profiler-events.h>
603 #undef MONO_PROFILER_EVENT_0
604 #undef MONO_PROFILER_EVENT_1
605 #undef MONO_PROFILER_EVENT_2
606 #undef MONO_PROFILER_EVENT_3
607 #undef MONO_PROFILER_EVENT_4
608 #undef _MONO_PROFILER_EVENT
610 #define _MONO_PROFILER_EVENT(name, type, params, args) \
612 mono_profiler_raise_ ## name params \
614 for (MonoProfilerHandle h = mono_profiler_state.profilers; h; h = h->next) { \
615 MonoProfiler ## type ## Callback cb = h->name ## _cb; \
620 #define MONO_PROFILER_EVENT_0(name, type) \
621 _MONO_PROFILER_EVENT(name, type, (void), (h->prof))
622 #define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \
623 _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name), (h->prof, arg1_name))
624 #define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \
625 _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name, arg2_type arg2_name), (h->prof, arg1_name, arg2_name))
626 #define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \
627 _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))
628 #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \
629 _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))
630 #include <mono/metadata/profiler-events.h>
631 #undef MONO_PROFILER_EVENT_0
632 #undef MONO_PROFILER_EVENT_1
633 #undef MONO_PROFILER_EVENT_2
634 #undef MONO_PROFILER_EVENT_3
635 #undef MONO_PROFILER_EVENT_4
636 #undef _MONO_PROFILER_EVENT
639 * The following code is here to maintain compatibility with a few profiler API
640 * functions used by Xamarin.{Android,iOS,Mac} so that they keep working
641 * regardless of which system Mono version is used.
643 * TODO: Remove this some day if we're OK with breaking compatibility.
646 typedef void *MonoLegacyProfiler;
648 typedef void (*MonoLegacyProfileFunc) (MonoLegacyProfiler *prof);
649 typedef void (*MonoLegacyProfileThreadFunc) (MonoLegacyProfiler *prof, uintptr_t tid);
650 typedef void (*MonoLegacyProfileGCFunc) (MonoLegacyProfiler *prof, MonoProfilerGCEvent event, int generation);
651 typedef void (*MonoLegacyProfileGCResizeFunc) (MonoLegacyProfiler *prof, int64_t new_size);
652 typedef void (*MonoLegacyProfileJitResult) (MonoLegacyProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result);
654 struct _MonoProfiler {
655 MonoProfilerHandle handle;
656 MonoLegacyProfiler *profiler;
657 MonoLegacyProfileFunc shutdown_callback;
658 MonoLegacyProfileThreadFunc thread_start, thread_end;
659 MonoLegacyProfileGCFunc gc_event;
660 MonoLegacyProfileGCResizeFunc gc_heap_resize;
661 MonoLegacyProfileJitResult jit_end2;
664 static MonoProfiler *current;
666 MONO_API void mono_profiler_install (MonoLegacyProfiler *prof, MonoLegacyProfileFunc callback);
667 MONO_API void mono_profiler_install_thread (MonoLegacyProfileThreadFunc start, MonoLegacyProfileThreadFunc end);
668 MONO_API void mono_profiler_install_gc (MonoLegacyProfileGCFunc callback, MonoLegacyProfileGCResizeFunc heap_resize_callback);
669 MONO_API void mono_profiler_install_jit_end (MonoLegacyProfileJitResult end);
670 MONO_API void mono_profiler_set_events (int flags);
673 shutdown_cb (MonoProfiler *prof)
675 prof->shutdown_callback (prof->profiler);
679 mono_profiler_install (MonoLegacyProfiler *prof, MonoLegacyProfileFunc callback)
681 current = g_new0 (MonoProfiler, 1);
682 current->handle = mono_profiler_create (current);
683 current->profiler = prof;
684 current->shutdown_callback = callback;
687 mono_profiler_set_runtime_shutdown_end_callback (current->handle, shutdown_cb);
691 thread_start_cb (MonoProfiler *prof, uintptr_t tid)
693 prof->thread_start (prof->profiler, tid);
697 thread_stop_cb (MonoProfiler *prof, uintptr_t tid)
699 prof->thread_end (prof->profiler, tid);
703 mono_profiler_install_thread (MonoLegacyProfileThreadFunc start, MonoLegacyProfileThreadFunc end)
705 current->thread_start = start;
706 current->thread_end = end;
709 mono_profiler_set_thread_started_callback (current->handle, thread_start_cb);
712 mono_profiler_set_thread_stopped_callback (current->handle, thread_stop_cb);
716 gc_event_cb (MonoProfiler *prof, MonoProfilerGCEvent event, uint32_t generation)
718 prof->gc_event (prof->profiler, event, generation);
722 gc_resize_cb (MonoProfiler *prof, uintptr_t size)
724 prof->gc_heap_resize (prof->profiler, size);
728 mono_profiler_install_gc (MonoLegacyProfileGCFunc callback, MonoLegacyProfileGCResizeFunc heap_resize_callback)
730 current->gc_event = callback;
731 current->gc_heap_resize = heap_resize_callback;
734 mono_profiler_set_gc_event_callback (current->handle, gc_event_cb);
736 if (heap_resize_callback)
737 mono_profiler_set_gc_resize_callback (current->handle, gc_resize_cb);
741 jit_done_cb (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo)
743 prof->jit_end2 (prof->profiler, method, jinfo, 0);
747 jit_failed_cb (MonoProfiler *prof, MonoMethod *method)
749 prof->jit_end2 (prof->profiler, method, NULL, 1);
753 mono_profiler_install_jit_end (MonoLegacyProfileJitResult end)
755 current->jit_end2 = end;
758 mono_profiler_set_jit_done_callback (current->handle, jit_done_cb);
759 mono_profiler_set_jit_failed_callback (current->handle, jit_failed_cb);
764 mono_profiler_set_events (int flags)