[profiler] Implement call context introspection for enter/leave events.
[mono-project.git] / mono / metadata / profiler.c
blob36b864a60e1284849cb371d681efda1aa37b4421
1 /*
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.
5 */
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"
23 static gboolean
24 load_profiler (MonoDl *module, const char *name, const char *desc)
26 if (!module)
27 return FALSE;
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);
34 g_free (old_name);
35 return FALSE;
38 g_free (err);
39 g_free (old_name);
41 char *new_name = g_strdup_printf (NEW_INITIALIZER_NAME "_%s", name);
43 if ((err = mono_dl_symbol (module, new_name, (gpointer *) &func))) {
44 g_free (err);
45 g_free (new_name);
46 return FALSE;
49 g_free (new_name);
51 func (desc);
53 return TRUE;
56 static gboolean
57 load_profiler_from_executable (const char *name, const char *desc)
59 char *err;
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);
71 if (!module) {
72 mono_profiler_printf_err ("Could not open main executable: %s", err);
73 g_free (err);
74 return FALSE;
77 return load_profiler (module, name, desc);
80 static gboolean
81 load_profiler_from_directory (const char *directory, const char *libname, const char *name, const char *desc)
83 char* path;
84 void *iter = NULL;
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);
90 g_free (path);
92 if (module)
93 return load_profiler (module, name, desc);
96 return FALSE;
99 static gboolean
100 load_profiler_from_installation (const char *libname, const char *name, const char *desc)
102 char *err;
103 MonoDl *module = mono_dl_open_runtime_lib (libname, MONO_DL_EAGER, &err);
105 g_free (err);
107 if (module)
108 return load_profiler (module, name, desc);
110 return FALSE;
113 void
114 mono_profiler_load (const char *desc)
116 if (!desc || !strcmp ("default", desc))
117 desc = "log:report";
119 const char *col = strchr (desc, ':');
120 char *mname;
122 if (col != NULL) {
123 mname = (char *) g_memdup (desc, col - desc + 1);
124 mname [col - desc] = 0;
125 } else
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);
135 if (!res)
136 res = load_profiler_from_directory (NULL, libname, mname, desc);
138 if (!res)
139 mono_profiler_printf_err ("The '%s' profiler wasn't found in the main executable nor could it be loaded from '%s'.", mname, libname);
141 g_free (libname);
144 g_free (mname);
147 MonoProfilerHandle
148 mono_profiler_create (MonoProfiler *prof)
150 MonoProfilerHandle handle = g_new0 (struct _MonoProfilerDesc, 1);
152 handle->prof = prof;
153 handle->next = mono_profiler_state.profilers;
155 mono_profiler_state.profilers = handle;
157 return handle;
160 void
161 mono_profiler_set_coverage_filter_callback (MonoProfilerHandle handle, MonoProfilerCoverageFilterCallback cb)
163 InterlockedWritePointer (&handle->coverage_filter, (gpointer) cb);
166 static void
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);
173 static void
174 lazy_initialize_coverage (void)
176 mono_lazy_initialize (&mono_profiler_state.coverage_status, initialize_coverage);
179 static void
180 coverage_lock (void)
182 mono_os_mutex_lock (&mono_profiler_state.coverage_mutex);
185 static void
186 coverage_unlock (void)
188 mono_os_mutex_unlock (&mono_profiler_state.coverage_mutex);
191 void
192 mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method, MonoProfilerCoverageCallback cb)
194 lazy_initialize_coverage ();
196 coverage_lock ();
198 MonoProfilerCoverageInfo *info = g_hash_table_lookup (mono_profiler_state.coverage_hash, method);
200 coverage_unlock ();
202 if (!info)
203 return;
205 MonoError error;
206 MonoMethodHeader *header = mono_method_get_header_checked (method, &error);
207 mono_error_assert_ok (&error);
209 guint32 size;
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 = {
222 .method = method,
223 .il_offset = offset,
224 .counter = info->data [i].count,
225 .line = 1,
226 .column = 1,
229 if (minfo) {
230 MonoDebugSourceLocation *loc = mono_debug_method_lookup_location (minfo, offset);
232 if (loc) {
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;
260 if (cb)
261 cover |= cb (handle->prof, method);
264 if (!cover)
265 return NULL;
267 coverage_lock ();
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);
275 coverage_unlock ();
277 return info;
280 void
281 mono_profiler_coverage_free (MonoMethod *method)
283 lazy_initialize_coverage ();
285 coverage_lock ();
287 MonoProfilerCoverageInfo *info = g_hash_table_lookup (mono_profiler_state.coverage_hash, method);
289 if (info) {
290 g_hash_table_remove (mono_profiler_state.coverage_hash, method);
291 g_free (info);
294 coverage_unlock ();
297 mono_bool
298 mono_profiler_enable_sampling (MonoProfilerHandle handle)
300 if (mono_profiler_state.startup_done)
301 return FALSE;
303 if (mono_profiler_state.sampling_owner)
304 return TRUE;
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);
311 return TRUE;
314 mono_bool
315 mono_profiler_set_sample_mode (MonoProfilerHandle handle, MonoProfilerSampleMode mode, uint32_t freq)
317 if (handle != mono_profiler_state.sampling_owner)
318 return FALSE;
320 mono_profiler_state.sample_mode = mode;
321 mono_profiler_state.sample_freq = freq;
323 mono_profiler_sampling_thread_post ();
325 return TRUE;
328 mono_bool
329 mono_profiler_get_sample_mode (MonoProfilerHandle handle, MonoProfilerSampleMode *mode, uint32_t *freq)
331 if (mode)
332 *mode = mono_profiler_state.sample_mode;
334 if (freq)
335 *freq = mono_profiler_state.sample_freq;
337 return handle == mono_profiler_state.sampling_owner;
340 gboolean
341 mono_profiler_sampling_enabled (void)
343 return !!mono_profiler_state.sampling_owner;
346 void
347 mono_profiler_sampling_thread_post (void)
349 mono_os_sem_post (&mono_profiler_state.sampling_semaphore);
352 void
353 mono_profiler_sampling_thread_wait (void)
355 mono_os_sem_wait (&mono_profiler_state.sampling_semaphore, MONO_SEM_FLAGS_NONE);
358 mono_bool
359 mono_profiler_enable_allocations (void)
361 if (mono_profiler_state.startup_done)
362 return FALSE;
364 return mono_profiler_state.allocations = TRUE;
367 void
368 mono_profiler_set_call_instrumentation_filter_callback (MonoProfilerHandle handle, MonoProfilerCallInstrumentationFilterCallback cb)
370 InterlockedWritePointer (&handle->call_instrumentation_filter, (gpointer) cb);
373 mono_bool
374 mono_profiler_enable_call_context_introspection (void)
376 if (mono_profiler_state.startup_done)
377 return FALSE;
379 mono_profiler_state.context_enable ();
381 return mono_profiler_state.call_contexts = TRUE;
384 void *
385 mono_profiler_call_context_get_this (MonoProfilerCallContext *context)
387 if (!mono_profiler_state.call_contexts)
388 return NULL;
390 return mono_profiler_state.context_get_this (context);
393 void *
394 mono_profiler_call_context_get_argument (MonoProfilerCallContext *context, uint32_t position)
396 if (!mono_profiler_state.call_contexts)
397 return NULL;
399 return mono_profiler_state.context_get_argument (context, position);
402 void *
403 mono_profiler_call_context_get_local (MonoProfilerCallContext *context, uint32_t position)
405 if (!mono_profiler_state.call_contexts)
406 return NULL;
408 return mono_profiler_state.context_get_local (context, position);
411 void *
412 mono_profiler_call_context_get_result (MonoProfilerCallContext *context)
414 if (!mono_profiler_state.call_contexts)
415 return NULL;
417 return mono_profiler_state.context_get_result (context);
420 void
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;
434 if (cb)
435 flags |= cb (handle->prof, method);
438 return flags;
441 void
442 mono_profiler_started (void)
444 mono_profiler_state.startup_done = TRUE;
447 void
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
494 static void
495 update_callback (volatile gpointer *location, gpointer new_, volatile gint32 *counter)
497 gpointer old;
499 do {
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.
512 if (old)
513 InterlockedDecrement (counter);
515 if (new_)
516 InterlockedIncrement (counter);
519 #define _MONO_PROFILER_EVENT(name, type) \
520 void \
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) \
544 void \
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; \
549 if (cb) \
550 cb args; \
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