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 *name
, const char *desc
)
29 char *err
, *old_name
= g_strdup_printf (OLD_INITIALIZER_NAME
);
30 MonoProfilerInitializer func
;
32 if (!(err
= mono_dl_symbol (module
, old_name
, (gpointer
) &func
))) {
33 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
);
41 char *new_name
= g_strdup_printf (NEW_INITIALIZER_NAME
"_%s", name
);
43 if ((err
= mono_dl_symbol (module
, new_name
, (gpointer
*) &func
))) {
57 load_profiler_from_executable (const char *name
, const char *desc
)
62 * Some profilers (such as ours) may need to call back into the runtime
63 * from their sampling callback (which is called in async-signal context).
64 * They need to be able to know that all references back to the runtime
65 * have been resolved; otherwise, calling runtime functions may result in
66 * invoking the dynamic linker which is not async-signal-safe. Passing
67 * MONO_DL_EAGER will ask the dynamic linker to resolve everything upfront.
69 MonoDl
*module
= mono_dl_open (NULL
, MONO_DL_EAGER
, &err
);
72 mono_profiler_printf_err ("Could not open main executable: %s", err
);
77 return load_profiler (module
, name
, desc
);
81 load_profiler_from_directory (const char *directory
, const char *libname
, const char *name
, const char *desc
)
86 while ((path
= mono_dl_build_path (directory
, libname
, &iter
))) {
87 // See the comment in load_embedded_profiler ().
88 MonoDl
*module
= mono_dl_open (path
, MONO_DL_EAGER
, NULL
);
93 return load_profiler (module
, name
, desc
);
100 load_profiler_from_installation (const char *libname
, const char *name
, const char *desc
)
103 MonoDl
*module
= mono_dl_open_runtime_lib (libname
, MONO_DL_EAGER
, &err
);
108 return load_profiler (module
, name
, desc
);
114 mono_profiler_load (const char *desc
)
116 if (!desc
|| !strcmp ("default", desc
))
119 const char *col
= strchr (desc
, ':');
123 mname
= (char *) g_memdup (desc
, col
- desc
+ 1);
124 mname
[col
- desc
] = 0;
126 mname
= g_strdup (desc
);
128 if (!load_profiler_from_executable (mname
, desc
)) {
129 char *libname
= g_strdup_printf ("mono-profiler-%s", mname
);
130 gboolean res
= load_profiler_from_installation (libname
, mname
, desc
);
132 if (!res
&& mono_config_get_assemblies_dir ())
133 res
= load_profiler_from_directory (mono_assembly_getrootdir (), libname
, mname
, desc
);
136 res
= load_profiler_from_directory (NULL
, libname
, mname
, desc
);
139 mono_profiler_printf_err ("The '%s' profiler wasn't found in the main executable nor could it be loaded from '%s'.", mname
, libname
);
148 mono_profiler_create (MonoProfiler
*prof
)
150 MonoProfilerHandle handle
= g_new0 (struct _MonoProfilerDesc
, 1);
153 handle
->next
= mono_profiler_state
.profilers
;
155 mono_profiler_state
.profilers
= handle
;
161 mono_profiler_set_coverage_filter_callback (MonoProfilerHandle handle
, MonoProfilerCoverageFilterCallback cb
)
163 InterlockedWritePointer (&handle
->coverage_filter
, (gpointer
) cb
);
167 initialize_coverage (void)
169 mono_os_mutex_init (&mono_profiler_state
.coverage_mutex
);
170 mono_profiler_state
.coverage_hash
= g_hash_table_new (NULL
, NULL
);
174 lazy_initialize_coverage (void)
176 mono_lazy_initialize (&mono_profiler_state
.coverage_status
, initialize_coverage
);
182 mono_os_mutex_lock (&mono_profiler_state
.coverage_mutex
);
186 coverage_unlock (void)
188 mono_os_mutex_unlock (&mono_profiler_state
.coverage_mutex
);
192 mono_profiler_get_coverage_data (MonoProfilerHandle handle
, MonoMethod
*method
, MonoProfilerCoverageCallback cb
)
194 lazy_initialize_coverage ();
198 MonoProfilerCoverageInfo
*info
= g_hash_table_lookup (mono_profiler_state
.coverage_hash
, method
);
206 MonoMethodHeader
*header
= mono_method_get_header_checked (method
, &error
);
207 mono_error_assert_ok (&error
);
211 const unsigned char *start
= mono_method_header_get_code (header
, &size
, NULL
);
212 const unsigned char *end
= start
- size
;
213 MonoDebugMethodInfo
*minfo
= mono_debug_lookup_method (method
);
215 for (guint32 i
= 0; i
< info
->entries
; i
++) {
216 guchar
*cil_code
= info
->data
[i
].cil_code
;
218 if (cil_code
&& cil_code
>= start
&& cil_code
< end
) {
219 guint32 offset
= cil_code
- start
;
221 MonoProfilerCoverageData data
= {
224 .counter
= info
->data
[i
].count
,
230 MonoDebugSourceLocation
*loc
= mono_debug_method_lookup_location (minfo
, offset
);
233 data
.file_name
= g_strdup (loc
->source_file
);
234 data
.line
= loc
->row
;
235 data
.column
= loc
->column
;
237 mono_debug_free_source_location (loc
);
241 cb (handle
->prof
, &data
);
243 g_free ((char *) data
.file_name
);
247 mono_metadata_free_mh (header
);
250 MonoProfilerCoverageInfo
*
251 mono_profiler_coverage_alloc (MonoMethod
*method
, guint32 entries
)
253 lazy_initialize_coverage ();
255 gboolean cover
= FALSE
;
257 for (MonoProfilerHandle handle
= mono_profiler_state
.profilers
; handle
; handle
= handle
->next
) {
258 MonoProfilerCoverageFilterCallback cb
= handle
->coverage_filter
;
261 cover
|= cb (handle
->prof
, method
);
269 MonoProfilerCoverageInfo
*info
= g_malloc0 (sizeof (MonoProfilerCoverageInfo
) + SIZEOF_VOID_P
* 2 * entries
);
271 info
->entries
= entries
;
273 g_hash_table_insert (mono_profiler_state
.coverage_hash
, method
, info
);
281 mono_profiler_coverage_free (MonoMethod
*method
)
283 lazy_initialize_coverage ();
287 MonoProfilerCoverageInfo
*info
= g_hash_table_lookup (mono_profiler_state
.coverage_hash
, method
);
290 g_hash_table_remove (mono_profiler_state
.coverage_hash
, method
);
298 mono_profiler_enable_sampling (MonoProfilerHandle handle
)
300 if (mono_profiler_state
.startup_done
)
303 if (mono_profiler_state
.sampling_owner
)
306 mono_profiler_state
.sampling_owner
= handle
;
307 mono_profiler_state
.sample_mode
= MONO_PROFILER_SAMPLE_MODE_NONE
;
308 mono_profiler_state
.sample_freq
= 100;
309 mono_os_sem_init (&mono_profiler_state
.sampling_semaphore
, 0);
315 mono_profiler_set_sample_mode (MonoProfilerHandle handle
, MonoProfilerSampleMode mode
, uint32_t freq
)
317 if (handle
!= mono_profiler_state
.sampling_owner
)
320 mono_profiler_state
.sample_mode
= mode
;
321 mono_profiler_state
.sample_freq
= freq
;
323 mono_profiler_sampling_thread_post ();
329 mono_profiler_get_sample_mode (MonoProfilerHandle handle
, MonoProfilerSampleMode
*mode
, uint32_t *freq
)
332 *mode
= mono_profiler_state
.sample_mode
;
335 *freq
= mono_profiler_state
.sample_freq
;
337 return handle
== mono_profiler_state
.sampling_owner
;
341 mono_profiler_sampling_enabled (void)
343 return !!mono_profiler_state
.sampling_owner
;
347 mono_profiler_sampling_thread_post (void)
349 mono_os_sem_post (&mono_profiler_state
.sampling_semaphore
);
353 mono_profiler_sampling_thread_wait (void)
355 mono_os_sem_wait (&mono_profiler_state
.sampling_semaphore
, MONO_SEM_FLAGS_NONE
);
359 mono_profiler_enable_allocations (void)
361 if (mono_profiler_state
.startup_done
)
364 return mono_profiler_state
.allocations
= TRUE
;
368 mono_profiler_set_call_instrumentation_filter_callback (MonoProfilerHandle handle
, MonoProfilerCallInstrumentationFilterCallback cb
)
370 InterlockedWritePointer (&handle
->call_instrumentation_filter
, (gpointer
) cb
);
374 mono_profiler_enable_call_context_introspection (void)
376 if (mono_profiler_state
.startup_done
)
379 mono_profiler_state
.context_enable ();
381 return mono_profiler_state
.call_contexts
= TRUE
;
385 mono_profiler_call_context_get_this (MonoProfilerCallContext
*context
)
387 if (!mono_profiler_state
.call_contexts
)
390 return mono_profiler_state
.context_get_this (context
);
394 mono_profiler_call_context_get_argument (MonoProfilerCallContext
*context
, uint32_t position
)
396 if (!mono_profiler_state
.call_contexts
)
399 return mono_profiler_state
.context_get_argument (context
, position
);
403 mono_profiler_call_context_get_local (MonoProfilerCallContext
*context
, uint32_t position
)
405 if (!mono_profiler_state
.call_contexts
)
408 return mono_profiler_state
.context_get_local (context
, position
);
412 mono_profiler_call_context_get_result (MonoProfilerCallContext
*context
)
414 if (!mono_profiler_state
.call_contexts
)
417 return mono_profiler_state
.context_get_result (context
);
421 mono_profiler_call_context_free_buffer (void *buffer
)
423 mono_profiler_state
.context_free_buffer (buffer
);
426 MonoProfilerCallInstrumentationFlags
427 mono_profiler_get_call_instrumentation_flags (MonoMethod
*method
)
429 MonoProfilerCallInstrumentationFlags flags
= MONO_PROFILER_CALL_INSTRUMENTATION_NONE
;
431 for (MonoProfilerHandle handle
= mono_profiler_state
.profilers
; handle
; handle
= handle
->next
) {
432 MonoProfilerCallInstrumentationFilterCallback cb
= handle
->call_instrumentation_filter
;
435 flags
|= cb (handle
->prof
, method
);
442 mono_profiler_started (void)
444 mono_profiler_state
.startup_done
= TRUE
;
448 mono_profiler_cleanup (void)
450 for (MonoProfilerHandle handle
= mono_profiler_state
.profilers
; handle
; handle
= handle
->next
) {
451 #define _MONO_PROFILER_EVENT(name) \
452 mono_profiler_set_ ## name ## _callback (handle, NULL); \
453 g_assert (!handle->name ## _cb);
454 #define MONO_PROFILER_EVENT_0(name, type) \
455 _MONO_PROFILER_EVENT(name)
456 #define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \
457 _MONO_PROFILER_EVENT(name)
458 #define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \
459 _MONO_PROFILER_EVENT(name)
460 #define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \
461 _MONO_PROFILER_EVENT(name)
462 #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \
463 _MONO_PROFILER_EVENT(name)
464 #include <mono/metadata/profiler-events.h>
465 #undef MONO_PROFILER_EVENT_0
466 #undef MONO_PROFILER_EVENT_1
467 #undef MONO_PROFILER_EVENT_2
468 #undef MONO_PROFILER_EVENT_3
469 #undef MONO_PROFILER_EVENT_4
470 #undef _MONO_PROFILER_EVENT
473 #define _MONO_PROFILER_EVENT(name, type) \
474 g_assert (!mono_profiler_state.name ## _count);
475 #define MONO_PROFILER_EVENT_0(name, type) \
476 _MONO_PROFILER_EVENT(name, type)
477 #define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \
478 _MONO_PROFILER_EVENT(name, type)
479 #define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \
480 _MONO_PROFILER_EVENT(name, type)
481 #define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \
482 _MONO_PROFILER_EVENT(name, type)
483 #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \
484 _MONO_PROFILER_EVENT(name, type)
485 #include <mono/metadata/profiler-events.h>
486 #undef MONO_PROFILER_EVENT_0
487 #undef MONO_PROFILER_EVENT_1
488 #undef MONO_PROFILER_EVENT_2
489 #undef MONO_PROFILER_EVENT_3
490 #undef MONO_PROFILER_EVENT_4
491 #undef _MONO_PROFILER_EVENT
495 update_callback (volatile gpointer
*location
, gpointer new_
, volatile gint32
*counter
)
500 old
= InterlockedReadPointer (location
);
501 } while (InterlockedCompareExchangePointer (location
, new_
, old
) != old
);
504 * At this point, we could have installed a NULL callback while the counter
505 * is still non-zero, i.e. setting the callback and modifying the counter
506 * is not a single atomic operation. This is fine as we make sure callbacks
507 * are non-NULL before invoking them (see the code below that generates the
508 * raise functions), and besides, updating callbacks at runtime is an
509 * inherently racy operation.
513 InterlockedDecrement (counter
);
516 InterlockedIncrement (counter
);
519 #define _MONO_PROFILER_EVENT(name, type) \
521 mono_profiler_set_ ## name ## _callback (MonoProfilerHandle handle, MonoProfiler ## type ## Callback cb) \
523 update_callback (&handle->name ## _cb, (gpointer) cb, &mono_profiler_state.name ## _count); \
525 #define MONO_PROFILER_EVENT_0(name, type) \
526 _MONO_PROFILER_EVENT(name, type)
527 #define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \
528 _MONO_PROFILER_EVENT(name, type)
529 #define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \
530 _MONO_PROFILER_EVENT(name, type)
531 #define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \
532 _MONO_PROFILER_EVENT(name, type)
533 #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \
534 _MONO_PROFILER_EVENT(name, type)
535 #include <mono/metadata/profiler-events.h>
536 #undef MONO_PROFILER_EVENT_0
537 #undef MONO_PROFILER_EVENT_1
538 #undef MONO_PROFILER_EVENT_2
539 #undef MONO_PROFILER_EVENT_3
540 #undef MONO_PROFILER_EVENT_4
541 #undef _MONO_PROFILER_EVENT
543 #define _MONO_PROFILER_EVENT(name, type, params, args) \
545 mono_profiler_raise_ ## name params \
547 for (MonoProfilerHandle h = mono_profiler_state.profilers; h; h = h->next) { \
548 MonoProfiler ## type ## Callback cb = h->name ## _cb; \
553 #define MONO_PROFILER_EVENT_0(name, type) \
554 _MONO_PROFILER_EVENT(name, type, (void), (h->prof))
555 #define MONO_PROFILER_EVENT_1(name, type, arg1_type, arg1_name) \
556 _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name), (h->prof, arg1_name))
557 #define MONO_PROFILER_EVENT_2(name, type, arg1_type, arg1_name, arg2_type, arg2_name) \
558 _MONO_PROFILER_EVENT(name, type, (arg1_type arg1_name, arg2_type arg2_name), (h->prof, arg1_name, arg2_name))
559 #define MONO_PROFILER_EVENT_3(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name) \
560 _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))
561 #define MONO_PROFILER_EVENT_4(name, type, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name) \
562 _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))
563 #include <mono/metadata/profiler-events.h>
564 #undef MONO_PROFILER_EVENT_0
565 #undef MONO_PROFILER_EVENT_1
566 #undef MONO_PROFILER_EVENT_2
567 #undef MONO_PROFILER_EVENT_3
568 #undef MONO_PROFILER_EVENT_4
569 #undef _MONO_PROFILER_EVENT