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/utils/mono-dl.h>
13 #include <mono/utils/mono-error-internals.h>
14 #include <mono/utils/mono-logger-internals.h>
16 MonoProfilerState mono_profiler_state;
18 typedef void (*MonoProfilerInitializer) (const char *);
20 #define OLD_INITIALIZER_NAME "mono_profiler_startup"
21 #define NEW_INITIALIZER_NAME "mono_profiler_init"
24 load_profiler (MonoDl *module, const char *desc, const char *suffix)
32 old_name = g_strdup_printf (OLD_INITIALIZER_NAME "_%s", suffix);
34 old_name = g_strdup_printf (OLD_INITIALIZER_NAME);
36 MonoProfilerInitializer func;
40 if (!(err = mono_dl_symbol (module, old_name, (gpointer) &func))) {
41 mono_profiler_printf_err ("Found old-style startup symbol %s for %s; profiler has not been migrated to the new API.", old_name, desc);
52 new_name = g_strdup_printf (NEW_INITIALIZER_NAME "_%s", suffix);
54 new_name = g_strdup_printf (NEW_INITIALIZER_NAME);
56 if ((err = mono_dl_symbol (module, new_name, (gpointer *) &func))) {
70 load_profiler_from_executable (const char *desc, const char *name)
75 * Some profilers (such as ours) may need to call back into the runtime
76 * from their sampling callback (which is called in async-signal context).
77 * They need to be able to know that all references back to the runtime
78 * have been resolved; otherwise, calling runtime functions may result in
79 * invoking the dynamic linker which is not async-signal-safe. Passing
80 * MONO_DL_EAGER will ask the dynamic linker to resolve everything upfront.
82 MonoDl *module = mono_dl_open (NULL, MONO_DL_EAGER, &err);
85 mono_profiler_printf_err ("Could not open main executable: %s", err);
90 return load_profiler (module, desc, name);
94 load_profiler_from_directory (const char *directory, const char *libname, const char *desc)
99 while ((path = mono_dl_build_path (directory, libname, &iter))) {
100 // See the comment in load_embedded_profiler ().
101 MonoDl *module = mono_dl_open (path, MONO_DL_EAGER, NULL);
106 return load_profiler (module, desc, NULL);
113 load_profiler_from_installation (const char *libname, const char *desc)
116 MonoDl *module = mono_dl_open_runtime_lib (libname, MONO_DL_EAGER, &err);
121 return load_profiler (module, desc, NULL);
127 mono_profiler_load (const char *desc)
129 if (!desc || !strcmp ("default", desc))
132 const char *col = strchr (desc, ':');
136 mname = (char *) g_memdup (desc, col - desc + 1);
137 mname [col - desc] = 0;
139 mname = g_strdup (desc);
141 if (!load_profiler_from_executable (desc, mname)) {
142 char *libname = g_strdup_printf ("mono-profiler-%s", mname);
143 gboolean res = load_profiler_from_installation (libname, desc);
145 if (!res && mono_config_get_assemblies_dir ())
146 res = load_profiler_from_directory (mono_assembly_getrootdir (), libname, desc);
149 res = load_profiler_from_directory (NULL, libname, desc);
152 mono_profiler_printf_err ("The '%s' profiler wasn't found in the main executable nor could it be loaded from '%s'.", mname, libname);
161 mono_profiler_install (MonoProfiler *prof)
163 MonoProfilerHandle handle = g_new0 (struct _MonoProfilerDesc, 1);
166 handle->next = mono_profiler_state.profilers;
168 mono_profiler_state.profilers = handle;
174 mono_profiler_set_coverage_filter_callback (MonoProfilerHandle handle, MonoProfilerCoverageFilterCallback cb)
176 InterlockedWritePointer (&handle->coverage_filter, (gpointer) cb);
180 initialize_coverage (void)
182 mono_os_mutex_init (&mono_profiler_state.coverage_mutex);
183 mono_profiler_state.coverage_hash = g_hash_table_new (NULL, NULL);
187 lazy_initialize_coverage (void)
189 mono_lazy_initialize (&mono_profiler_state.coverage_status, initialize_coverage);
195 mono_os_mutex_lock (&mono_profiler_state.coverage_mutex);
199 coverage_unlock (void)
201 mono_os_mutex_unlock (&mono_profiler_state.coverage_mutex);
205 mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method, MonoProfilerCoverageCallback cb)
207 lazy_initialize_coverage ();
211 MonoProfilerCoverageInfo *info = g_hash_table_lookup (mono_profiler_state.coverage_hash, method);
219 MonoMethodHeader *header = mono_method_get_header_checked (method, &error);
220 mono_error_assert_ok (&error);
224 const unsigned char *start = mono_method_header_get_code (header, &size, NULL);
225 const unsigned char *end = start - size;
226 MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method);
228 for (guint32 i = 0; i < info->entries; i++) {
229 guchar *cil_code = info->data [i].cil_code;
231 if (cil_code && cil_code >= start && cil_code < end) {
232 guint32 offset = cil_code - start;
234 MonoProfilerCoverageData data = {
237 .counter = info->data [i].count,
243 MonoDebugSourceLocation *loc = mono_debug_method_lookup_location (minfo, offset);
246 data.file_name = g_strdup (loc->source_file);
247 data.line = loc->row;
248 data.column = loc->column;
250 mono_debug_free_source_location (loc);
254 cb (handle->prof, &data);
256 g_free ((char *) data.file_name);
260 mono_metadata_free_mh (header);
263 MonoProfilerCoverageInfo *
264 mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries)
266 lazy_initialize_coverage ();
268 gboolean cover = FALSE;
270 for (MonoProfilerHandle handle = mono_profiler_state.profilers; handle; handle = handle->next) {
271 MonoProfilerCoverageFilterCallback cb = handle->coverage_filter;
274 cover |= cb (handle->prof, method);
282 MonoProfilerCoverageInfo *info = g_malloc0 (sizeof (MonoProfilerCoverageInfo) + SIZEOF_VOID_P * 2 * entries);
284 info->entries = entries;
286 g_hash_table_insert (mono_profiler_state.coverage_hash, method, info);
294 mono_profiler_coverage_free (MonoMethod *method)
296 lazy_initialize_coverage ();
300 MonoProfilerCoverageInfo *info = g_hash_table_lookup (mono_profiler_state.coverage_hash, method);
303 g_hash_table_remove (mono_profiler_state.coverage_hash, method);
311 mono_profiler_enable_sampling (MonoProfilerHandle handle)
313 if (mono_profiler_state.startup_done)
316 if (mono_profiler_state.sampling_owner)
319 mono_profiler_state.sampling_owner = handle;
320 mono_profiler_state.sample_mode = MONO_PROFILER_SAMPLE_MODE_NONE;
321 mono_profiler_state.sample_freq = 100;
322 mono_os_sem_init (&mono_profiler_state.sampling_semaphore, 0);
328 mono_profiler_set_sample_mode (MonoProfilerHandle handle, MonoProfilerSampleMode mode, uint32_t freq)
330 if (handle != mono_profiler_state.sampling_owner)
333 mono_profiler_state.sample_mode = mode;
334 mono_profiler_state.sample_freq = freq;
336 mono_profiler_sampling_thread_post ();
342 mono_profiler_get_sample_mode (MonoProfilerHandle handle, MonoProfilerSampleMode *mode, uint32_t *freq)
345 *mode = mono_profiler_state.sample_mode;
348 *freq = mono_profiler_state.sample_freq;
350 return handle == mono_profiler_state.sampling_owner;
354 mono_profiler_sampling_enabled (void)
356 return !!mono_profiler_state.sampling_owner;
360 mono_profiler_sampling_thread_post (void)
362 mono_os_sem_post (&mono_profiler_state.sampling_semaphore);
366 mono_profiler_sampling_thread_wait (void)
368 mono_os_sem_wait (&mono_profiler_state.sampling_semaphore, MONO_SEM_FLAGS_NONE);
372 mono_profiler_enable_allocations (void)
374 if (mono_profiler_state.startup_done)
377 mono_profiler_state.allocations = TRUE;
383 mono_profiler_set_call_instrumentation_filter_callback (MonoProfilerHandle handle, MonoProfilerCallInstrumentationFilterCallback cb)
385 InterlockedWritePointer (&handle->call_instrumentation_filter, (gpointer) cb);
389 mono_profiler_should_instrument_method (MonoMethod *method, gboolean entry)
391 MonoProfilerCallInstrumentationFlags flags = MONO_PROFILER_CALL_INSTRUMENTATION_NONE;
393 for (MonoProfilerHandle handle = mono_profiler_state.profilers; handle; handle = handle->next) {
394 MonoProfilerCallInstrumentationFilterCallback cb = handle->call_instrumentation_filter;
397 flags |= cb (handle->prof, method);
401 return flags & MONO_PROFILER_CALL_INSTRUMENTATION_PROLOGUE;
403 return flags & MONO_PROFILER_CALL_INSTRUMENTATION_EPILOGUE;
407 mono_profiler_started (void)
409 mono_profiler_state.startup_done = TRUE;
413 mono_profiler_cleanup (void)
415 for (MonoProfilerHandle handle = mono_profiler_state.profilers; handle; handle = handle->next) {
416 #define _MONO_PROFILER_EVENT(name) \
417 mono_profiler_set_ ## name ## _callback (handle, NULL); \
418 g_assert (!handle->name ## _cb);
419 #define MONO_PROFILER_EVENT_0(name, type) \
420 _MONO_PROFILER_EVENT(name)
421 #define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \
422 _MONO_PROFILER_EVENT(name)
423 #define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \
424 _MONO_PROFILER_EVENT(name)
425 #define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \
426 _MONO_PROFILER_EVENT(name)
427 #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \
428 _MONO_PROFILER_EVENT(name)
429 #include <mono/metadata/profiler-events.h>
430 #undef MONO_PROFILER_EVENT_0
431 #undef MONO_PROFILER_EVENT_1
432 #undef MONO_PROFILER_EVENT_2
433 #undef MONO_PROFILER_EVENT_3
434 #undef MONO_PROFILER_EVENT_4
435 #undef _MONO_PROFILER_EVENT
438 #define _MONO_PROFILER_EVENT(name, type) \
439 g_assert (!mono_profiler_state.name ## _count);
440 #define MONO_PROFILER_EVENT_0(name, type) \
441 _MONO_PROFILER_EVENT(name, type)
442 #define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \
443 _MONO_PROFILER_EVENT(name, type)
444 #define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \
445 _MONO_PROFILER_EVENT(name, type)
446 #define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \
447 _MONO_PROFILER_EVENT(name, type)
448 #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \
449 _MONO_PROFILER_EVENT(name, type)
450 #include <mono/metadata/profiler-events.h>
451 #undef MONO_PROFILER_EVENT_0
452 #undef MONO_PROFILER_EVENT_1
453 #undef MONO_PROFILER_EVENT_2
454 #undef MONO_PROFILER_EVENT_3
455 #undef MONO_PROFILER_EVENT_4
456 #undef _MONO_PROFILER_EVENT
460 update_callback (volatile gpointer *location, gpointer new_, volatile gint32 *counter)
465 old = InterlockedReadPointer (location);
466 } while (InterlockedCompareExchangePointer (location, new_, old) != old);
469 * At this point, we could have installed a NULL callback while the counter
470 * is still non-zero, i.e. setting the callback and modifying the counter
471 * is not a single atomic operation. This is fine as we make sure callbacks
472 * are non-NULL before invoking them (see the code below that generates the
473 * raise functions), and besides, updating callbacks at runtime is an
474 * inherently racy operation.
478 InterlockedDecrement (counter);
481 InterlockedIncrement (counter);
484 #define _MONO_PROFILER_EVENT(name, type) \
486 mono_profiler_set_ ## name ## _callback (MonoProfilerHandle handle, MonoProfiler ## type ## Callback cb) \
488 update_callback (&handle->name ## _cb, (gpointer) cb, &mono_profiler_state.name ## _count); \
490 #define MONO_PROFILER_EVENT_0(name, type) \
491 _MONO_PROFILER_EVENT(name, type)
492 #define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \
493 _MONO_PROFILER_EVENT(name, type)
494 #define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \
495 _MONO_PROFILER_EVENT(name, type)
496 #define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \
497 _MONO_PROFILER_EVENT(name, type)
498 #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \
499 _MONO_PROFILER_EVENT(name, type)
500 #include <mono/metadata/profiler-events.h>
501 #undef MONO_PROFILER_EVENT_0
502 #undef MONO_PROFILER_EVENT_1
503 #undef MONO_PROFILER_EVENT_2
504 #undef MONO_PROFILER_EVENT_3
505 #undef MONO_PROFILER_EVENT_4
506 #undef _MONO_PROFILER_EVENT
508 #define _MONO_PROFILER_EVENT(name, type, params, args) \
510 mono_profiler_raise_ ## name params \
512 for (MonoProfilerHandle h = mono_profiler_state.profilers; h; h = h->next) { \
513 MonoProfiler ## type ## Callback cb = h->name ## _cb; \
518 #define MONO_PROFILER_EVENT_0(name, type) \
519 _MONO_PROFILER_EVENT(name, type, (void), (h->prof))
520 #define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \
521 _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name), (h->prof, arg1_name))
522 #define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \
523 _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name, arg2_type arg2_name), (h->prof, arg1_name, arg2_name))
524 #define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \
525 _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))
526 #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \
527 _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))
528 #include <mono/metadata/profiler-events.h>
529 #undef MONO_PROFILER_EVENT_0
530 #undef MONO_PROFILER_EVENT_1
531 #undef MONO_PROFILER_EVENT_2
532 #undef MONO_PROFILER_EVENT_3
533 #undef MONO_PROFILER_EVENT_4
534 #undef _MONO_PROFILER_EVENT