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>
15 MonoProfilerState mono_profiler_state;
17 typedef void (*MonoProfilerInitializer) (const char *);
19 #define OLD_INITIALIZER_NAME "mono_profiler_startup"
20 #define NEW_INITIALIZER_NAME "mono_profiler_init"
23 load_profiler (MonoDl *module, const char *desc, const char *suffix)
31 old_name = g_strdup_printf (OLD_INITIALIZER_NAME "_%s", suffix);
33 old_name = g_strdup_printf (OLD_INITIALIZER_NAME);
35 MonoProfilerInitializer func;
39 if (!(err = mono_dl_symbol (module, old_name, (gpointer) &func))) {
40 g_warning ("Found old-style startup symbol %s; profiler has not been migrated to the new API.", old_name);
51 new_name = g_strdup_printf (NEW_INITIALIZER_NAME "_%s", suffix);
53 new_name = g_strdup_printf (NEW_INITIALIZER_NAME);
55 if ((err = mono_dl_symbol (module, new_name, (gpointer *) &func))) {
69 load_profiler_from_executable (const char *desc, const char *name)
74 * Some profilers (such as ours) may need to call back into the runtime
75 * from their sampling callback (which is called in async-signal context).
76 * They need to be able to know that all references back to the runtime
77 * have been resolved; otherwise, calling runtime functions may result in
78 * invoking the dynamic linker which is not async-signal-safe. Passing
79 * MONO_DL_EAGER will ask the dynamic linker to resolve everything upfront.
81 MonoDl *module = mono_dl_open (NULL, MONO_DL_EAGER, &err);
84 g_warning ("Could not open main executable (%s).", err);
89 return load_profiler (module, desc, name);
93 load_profiler_from_directory (const char *directory, const char *libname, const char *desc)
98 while ((path = mono_dl_build_path (directory, libname, &iter))) {
99 // See the comment in load_embedded_profiler ().
100 MonoDl *module = mono_dl_open (path, MONO_DL_EAGER, NULL);
105 return load_profiler (module, desc, NULL);
112 load_profiler_from_installation (const char *libname, const char *desc)
115 MonoDl *module = mono_dl_open_runtime_lib (libname, MONO_DL_EAGER, &err);
120 return load_profiler (module, desc, NULL);
126 mono_profiler_load (const char *desc)
128 mono_gc_base_init ();
130 if (!desc || !strcmp ("default", desc))
133 const char *col = strchr (desc, ':');
137 mname = (char *) g_memdup (desc, col - desc + 1);
138 mname [col - desc] = 0;
140 mname = g_strdup (desc);
142 if (!load_profiler_from_executable (desc, mname)) {
143 char *libname = g_strdup_printf ("mono-profiler-%s", mname);
144 gboolean res = load_profiler_from_installation (libname, desc);
146 if (!res && mono_config_get_assemblies_dir ())
147 res = load_profiler_from_directory (mono_assembly_getrootdir (), libname, desc);
150 res = load_profiler_from_directory (NULL, libname, desc);
153 g_warning ("The '%s' profiler wasn't found in the main executable nor could it be loaded from '%s'.", mname, libname);
162 mono_profiler_install (MonoProfiler *prof)
164 MonoProfilerHandle handle = g_new0 (struct _MonoProfilerDesc, 1);
167 handle->next = mono_profiler_state.profilers;
169 mono_profiler_state.profilers = handle;
175 mono_profiler_set_coverage_filter_callback (MonoProfilerHandle handle, MonoProfilerCoverageFilterCallback cb)
177 InterlockedWritePointer (&handle->coverage_filter, (gpointer) cb);
181 initialize_coverage (void)
183 mono_os_mutex_init (&mono_profiler_state.coverage_mutex);
184 mono_profiler_state.coverage_hash = g_hash_table_new (NULL, NULL);
188 lazy_initialize_coverage (void)
190 mono_lazy_initialize (&mono_profiler_state.coverage_status, initialize_coverage);
196 mono_os_mutex_lock (&mono_profiler_state.coverage_mutex);
200 coverage_unlock (void)
202 mono_os_mutex_unlock (&mono_profiler_state.coverage_mutex);
206 mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method, MonoProfilerCoverageCallback cb)
208 lazy_initialize_coverage ();
212 MonoProfilerCoverageInfo *info = g_hash_table_lookup (mono_profiler_state.coverage_hash, method);
220 MonoMethodHeader *header = mono_method_get_header_checked (method, &error);
221 mono_error_assert_ok (&error);
225 const unsigned char *start = mono_method_header_get_code (header, &size, NULL);
226 const unsigned char *end = start - size;
227 MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method);
229 for (guint32 i = 0; i < info->entries; i++) {
230 guchar *cil_code = info->data [i].cil_code;
232 if (cil_code && cil_code >= start && cil_code < end) {
233 guint32 offset = cil_code - start;
235 MonoProfilerCoverageData data = {
238 .counter = info->data [i].count,
244 MonoDebugSourceLocation *loc = mono_debug_method_lookup_location (minfo, offset);
247 data.file_name = g_strdup (loc->source_file);
248 data.line = loc->row;
249 data.column = loc->column;
251 mono_debug_free_source_location (loc);
255 cb (handle->prof, &data);
257 g_free ((char *) data.file_name);
261 mono_metadata_free_mh (header);
264 MonoProfilerCoverageInfo *
265 mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries)
267 lazy_initialize_coverage ();
269 gboolean cover = FALSE;
271 for (MonoProfilerHandle handle = mono_profiler_state.profilers; handle; handle = handle->next) {
272 MonoProfilerCoverageFilterCallback cb = handle->coverage_filter;
275 cover |= cb (handle->prof, method);
283 MonoProfilerCoverageInfo *info = g_malloc0 (sizeof (MonoProfilerCoverageInfo) + SIZEOF_VOID_P * 2 * entries);
285 info->entries = entries;
287 g_hash_table_insert (mono_profiler_state.coverage_hash, method, info);
295 mono_profiler_coverage_free (MonoMethod *method)
297 lazy_initialize_coverage ();
301 MonoProfilerCoverageInfo *info = g_hash_table_lookup (mono_profiler_state.coverage_hash, method);
304 g_hash_table_remove (mono_profiler_state.coverage_hash, method);
312 mono_profiler_enable_sampling (MonoProfilerHandle handle)
314 if (mono_profiler_state.startup_done)
317 if (mono_profiler_state.sampling_owner)
320 mono_profiler_state.sampling_owner = handle;
321 mono_profiler_state.sample_mode = MONO_PROFILER_SAMPLE_MODE_NONE;
322 mono_profiler_state.sample_freq = 100;
323 mono_os_sem_init (&mono_profiler_state.sampling_semaphore, 0);
329 mono_profiler_set_sample_mode (MonoProfilerHandle handle, MonoProfilerSampleMode mode, uint64_t freq)
331 if (handle != mono_profiler_state.sampling_owner)
334 mono_profiler_state.sample_mode = mode;
335 mono_profiler_state.sample_freq = freq;
337 mono_os_sem_post (&mono_profiler_state.sampling_semaphore);
343 mono_profiler_get_sample_mode (MonoProfilerHandle handle, MonoProfilerSampleMode *mode, uint64_t *freq)
346 *mode = mono_profiler_state.sample_mode;
349 *freq = mono_profiler_state.sample_freq;
351 return handle == mono_profiler_state.sampling_owner;
355 mono_profiler_sampling_enabled (void)
357 return !!mono_profiler_state.sampling_owner;
361 mono_profiler_sampling_thread_sleep (void)
363 mono_os_sem_wait (&mono_profiler_state.sampling_semaphore, MONO_SEM_FLAGS_NONE);
367 mono_profiler_enable_allocations (void)
369 if (mono_profiler_state.startup_done)
372 mono_profiler_state.allocations = TRUE;
378 mono_profiler_set_call_instrumentation_filter_callback (MonoProfilerHandle handle, MonoProfilerCallInstrumentationFilterCallback cb)
380 InterlockedWritePointer (&handle->call_instrumentation_filter, (gpointer) cb);
384 mono_profiler_should_instrument_method (MonoMethod *method, gboolean entry)
386 MonoProfilerCallInstrumentationFlags flags = MONO_PROFILER_CALL_INSTRUMENTATION_NONE;
388 for (MonoProfilerHandle handle = mono_profiler_state.profilers; handle; handle = handle->next) {
389 MonoProfilerCallInstrumentationFilterCallback cb = handle->call_instrumentation_filter;
392 flags |= cb (handle->prof, method);
396 return flags & MONO_PROFILER_CALL_INSTRUMENTATION_PROLOGUE;
398 return flags & MONO_PROFILER_CALL_INSTRUMENTATION_EPILOGUE;
402 mono_profiler_started (void)
404 mono_profiler_state.startup_done = TRUE;
408 mono_profiler_cleanup (void)
410 for (MonoProfilerHandle handle = mono_profiler_state.profilers; handle; handle = handle->next) {
411 #define _MONO_PROFILER_EVENT(name) \
412 mono_profiler_set_ ## name ## _callback (handle, NULL); \
413 g_assert (!handle->name ## _cb);
414 #define MONO_PROFILER_EVENT_0(name, type) \
415 _MONO_PROFILER_EVENT(name)
416 #define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \
417 _MONO_PROFILER_EVENT(name)
418 #define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \
419 _MONO_PROFILER_EVENT(name)
420 #define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \
421 _MONO_PROFILER_EVENT(name)
422 #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \
423 _MONO_PROFILER_EVENT(name)
424 #include <mono/metadata/profiler-events.h>
425 #undef MONO_PROFILER_EVENT_0
426 #undef MONO_PROFILER_EVENT_1
427 #undef MONO_PROFILER_EVENT_2
428 #undef MONO_PROFILER_EVENT_3
429 #undef MONO_PROFILER_EVENT_4
430 #undef _MONO_PROFILER_EVENT
433 #define _MONO_PROFILER_EVENT(name, type) \
434 g_assert (!mono_profiler_state.name ## _count);
435 #define MONO_PROFILER_EVENT_0(name, type) \
436 _MONO_PROFILER_EVENT(name, type)
437 #define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \
438 _MONO_PROFILER_EVENT(name, type)
439 #define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \
440 _MONO_PROFILER_EVENT(name, type)
441 #define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \
442 _MONO_PROFILER_EVENT(name, type)
443 #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \
444 _MONO_PROFILER_EVENT(name, type)
445 #include <mono/metadata/profiler-events.h>
446 #undef MONO_PROFILER_EVENT_0
447 #undef MONO_PROFILER_EVENT_1
448 #undef MONO_PROFILER_EVENT_2
449 #undef MONO_PROFILER_EVENT_3
450 #undef MONO_PROFILER_EVENT_4
451 #undef _MONO_PROFILER_EVENT
455 update_callback (volatile gpointer *location, gpointer new_, volatile gint32 *counter)
460 old = InterlockedReadPointer (location);
461 } while (InterlockedCompareExchangePointer (location, new_, old) != old);
464 * At this point, we could have installed a NULL callback while the counter
465 * is still non-zero, i.e. setting the callback and modifying the counter
466 * is not a single atomic operation. This is fine as we make sure callbacks
467 * are non-NULL before invoking them (see the code below that generates the
468 * raise functions), and besides, updating callbacks at runtime is an
469 * inherently racy operation.
473 InterlockedDecrement (counter);
476 InterlockedIncrement (counter);
479 #define _MONO_PROFILER_EVENT(name, type) \
481 mono_profiler_set_ ## name ## _callback (MonoProfilerHandle handle, MonoProfiler ## type ## Callback cb) \
483 update_callback (&handle->name ## _cb, (gpointer) cb, &mono_profiler_state.name ## _count); \
485 #define MONO_PROFILER_EVENT_0(name, type) \
486 _MONO_PROFILER_EVENT(name, type)
487 #define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \
488 _MONO_PROFILER_EVENT(name, type)
489 #define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \
490 _MONO_PROFILER_EVENT(name, type)
491 #define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \
492 _MONO_PROFILER_EVENT(name, type)
493 #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \
494 _MONO_PROFILER_EVENT(name, type)
495 #include <mono/metadata/profiler-events.h>
496 #undef MONO_PROFILER_EVENT_0
497 #undef MONO_PROFILER_EVENT_1
498 #undef MONO_PROFILER_EVENT_2
499 #undef MONO_PROFILER_EVENT_3
500 #undef MONO_PROFILER_EVENT_4
501 #undef _MONO_PROFILER_EVENT
503 #define _MONO_PROFILER_EVENT(name, type, params, args) \
505 mono_profiler_raise_ ## name params \
507 for (MonoProfilerHandle h = mono_profiler_state.profilers; h; h = h->next) { \
508 MonoProfiler ## type ## Callback cb = h->name ## _cb; \
513 #define MONO_PROFILER_EVENT_0(name, type) \
514 _MONO_PROFILER_EVENT(name, type, (void), (h->prof))
515 #define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \
516 _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name), (h->prof, arg1_name))
517 #define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \
518 _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name, arg2_type arg2_name), (h->prof, arg1_name, arg2_name))
519 #define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \
520 _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))
521 #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \
522 _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))
523 #include <mono/metadata/profiler-events.h>
524 #undef MONO_PROFILER_EVENT_0
525 #undef MONO_PROFILER_EVENT_1
526 #undef MONO_PROFILER_EVENT_2
527 #undef MONO_PROFILER_EVENT_3
528 #undef MONO_PROFILER_EVENT_4
529 #undef _MONO_PROFILER_EVENT