[runtime] Coverage profiler fixes (#5698)
[mono.git] / mono / metadata / profiler.c
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  */
6
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>
16
17 MonoProfilerState mono_profiler_state;
18
19 typedef void (*MonoProfilerInitializer) (const char *);
20
21 #define OLD_INITIALIZER_NAME "mono_profiler_startup"
22 #define NEW_INITIALIZER_NAME "mono_profiler_init"
23
24 static gboolean
25 load_profiler (MonoDl *module, const char *name, const char *desc)
26 {
27         if (!module)
28                 return FALSE;
29
30         char *err, *old_name = g_strdup_printf (OLD_INITIALIZER_NAME);
31         MonoProfilerInitializer func;
32
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);
35                 g_free (old_name);
36                 return FALSE;
37         }
38
39         g_free (err);
40         g_free (old_name);
41
42         char *new_name = g_strdup_printf (NEW_INITIALIZER_NAME "_%s", name);
43
44         if ((err = mono_dl_symbol (module, new_name, (gpointer *) &func))) {
45                 g_free (err);
46                 g_free (new_name);
47                 return FALSE;
48         }
49
50         g_free (new_name);
51
52         func (desc);
53
54         return TRUE;
55 }
56
57 static gboolean
58 load_profiler_from_executable (const char *name, const char *desc)
59 {
60         char *err;
61
62         /*
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.
69          */
70         MonoDl *module = mono_dl_open (NULL, MONO_DL_EAGER, &err);
71
72         if (!module) {
73                 mono_profiler_printf_err ("Could not open main executable: %s", err);
74                 g_free (err);
75                 return FALSE;
76         }
77
78         return load_profiler (module, name, desc);
79 }
80
81 static gboolean
82 load_profiler_from_directory (const char *directory, const char *libname, const char *name, const char *desc)
83 {
84         char* path;
85         void *iter = NULL;
86
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);
90
91                 g_free (path);
92
93                 if (module)
94                         return load_profiler (module, name, desc);
95         }
96
97         return FALSE;
98 }
99
100 static gboolean
101 load_profiler_from_installation (const char *libname, const char *name, const char *desc)
102 {
103         char *err;
104         MonoDl *module = mono_dl_open_runtime_lib (libname, MONO_DL_EAGER, &err);
105
106         g_free (err);
107
108         if (module)
109                 return load_profiler (module, name, desc);
110
111         return FALSE;
112 }
113
114 void
115 mono_profiler_load (const char *desc)
116 {
117         if (!desc || !strcmp ("default", desc))
118                 desc = "log:report";
119
120         const char *col = strchr (desc, ':');
121         char *mname;
122
123         if (col != NULL) {
124                 mname = (char *) g_memdup (desc, col - desc + 1);
125                 mname [col - desc] = 0;
126         } else
127                 mname = g_strdup (desc);
128
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);
132
133                 if (!res && mono_config_get_assemblies_dir ())
134                         res = load_profiler_from_directory (mono_assembly_getrootdir (), libname, mname, desc);
135
136                 if (!res)
137                         res = load_profiler_from_directory (NULL, libname, mname, desc);
138
139                 if (!res)
140                         mono_profiler_printf_err ("The '%s' profiler wasn't found in the main executable nor could it be loaded from '%s'.", mname, libname);
141
142                 g_free (libname);
143         }
144
145         g_free (mname);
146 }
147
148 MonoProfilerHandle
149 mono_profiler_create (MonoProfiler *prof)
150 {
151         MonoProfilerHandle handle = g_new0 (struct _MonoProfilerDesc, 1);
152
153         handle->prof = prof;
154         handle->next = mono_profiler_state.profilers;
155
156         mono_profiler_state.profilers = handle;
157
158         return handle;
159 }
160
161 void
162 mono_profiler_set_cleanup_callback (MonoProfilerHandle handle, MonoProfilerCleanupCallback cb)
163 {
164         InterlockedWritePointer (&handle->cleanup_callback, (gpointer) cb);
165 }
166
167 void
168 mono_profiler_set_coverage_filter_callback (MonoProfilerHandle handle, MonoProfilerCoverageFilterCallback cb)
169 {
170         InterlockedWritePointer (&handle->coverage_filter, (gpointer) cb);
171 }
172
173 mono_bool
174 mono_profiler_enable_coverage (void)
175 {
176         if (mono_profiler_state.startup_done)
177                 return FALSE;
178
179         mono_os_mutex_init (&mono_profiler_state.coverage_mutex);
180         mono_profiler_state.coverage_hash = g_hash_table_new (NULL, NULL);
181
182         if (!mono_debug_enabled ())
183                 mono_debug_init (MONO_DEBUG_FORMAT_MONO);
184
185         return mono_profiler_state.code_coverage = TRUE;
186 }
187
188 static void
189 coverage_lock (void)
190 {
191         mono_os_mutex_lock (&mono_profiler_state.coverage_mutex);
192 }
193
194 static void
195 coverage_unlock (void)
196 {
197         mono_os_mutex_unlock (&mono_profiler_state.coverage_mutex);
198 }
199
200 mono_bool
201 mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method, MonoProfilerCoverageCallback cb)
202 {
203         if (!mono_profiler_state.code_coverage)
204                 return FALSE;
205
206         coverage_lock ();
207
208         MonoProfilerCoverageInfo *info = g_hash_table_lookup (mono_profiler_state.coverage_hash, method);
209
210         coverage_unlock ();
211
212         MonoError error;
213         MonoMethodHeader *header = mono_method_get_header_checked (method, &error);
214         mono_error_assert_ok (&error);
215
216         guint32 size;
217
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);
221
222         if (!info) {
223                 char *source_file;
224                 int i, n_il_offsets;
225                 int *source_files;
226                 GPtrArray *source_file_list;
227                 MonoSymSeqPoint *sym_seq_points;
228
229                 /* Return 0 counts for all locations */
230
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 = "";
235
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;
239                         }
240
241                         MonoProfilerCoverageData data = {
242                                 .method = method,
243                                 .il_offset = sp->il_offset,
244                                 .counter = 0,
245                                 .file_name = srcfile,
246                                 .line = sp->line,
247                                 .column = 0,
248                         };
249
250                         cb (handle->prof, &data);
251                 }
252
253                 g_free (source_files);
254                 g_free (sym_seq_points);
255                 g_ptr_array_free (source_file_list, TRUE);
256
257                 mono_metadata_free_mh (header);
258                 return TRUE;
259         }
260
261         for (guint32 i = 0; i < info->entries; i++) {
262                 guchar *cil_code = info->data [i].cil_code;
263
264                 if (cil_code && cil_code >= start && cil_code < end) {
265                         guint32 offset = cil_code - start;
266
267                         MonoProfilerCoverageData data = {
268                                 .method = method,
269                                 .il_offset = offset,
270                                 .counter = info->data [i].count,
271                                 .line = 1,
272                                 .column = 1,
273                         };
274
275                         if (minfo) {
276                                 MonoDebugSourceLocation *loc = mono_debug_method_lookup_location (minfo, offset);
277
278                                 if (loc) {
279                                         data.file_name = g_strdup (loc->source_file);
280                                         data.line = loc->row;
281                                         data.column = loc->column;
282
283                                         mono_debug_free_source_location (loc);
284                                 }
285                         }
286
287                         cb (handle->prof, &data);
288
289                         g_free ((char *) data.file_name);
290                 }
291         }
292
293         mono_metadata_free_mh (header);
294
295         return TRUE;
296 }
297
298 MonoProfilerCoverageInfo *
299 mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries)
300 {
301         if (!mono_profiler_state.code_coverage)
302                 return FALSE;
303
304         if (method->wrapper_type)
305                 return FALSE;
306
307         gboolean cover = FALSE;
308
309         for (MonoProfilerHandle handle = mono_profiler_state.profilers; handle; handle = handle->next) {
310                 MonoProfilerCoverageFilterCallback cb = handle->coverage_filter;
311
312                 if (cb)
313                         cover |= cb (handle->prof, method);
314         }
315
316         if (!cover)
317                 return NULL;
318
319         coverage_lock ();
320
321         MonoProfilerCoverageInfo *info = g_malloc0 (sizeof (MonoProfilerCoverageInfo) + SIZEOF_VOID_P * 2 * entries);
322
323         info->entries = entries;
324
325         g_hash_table_insert (mono_profiler_state.coverage_hash, method, info);
326
327         coverage_unlock ();
328
329         return info;
330 }
331
332 mono_bool
333 mono_profiler_enable_sampling (MonoProfilerHandle handle)
334 {
335         if (mono_profiler_state.startup_done)
336                 return FALSE;
337
338         if (mono_profiler_state.sampling_owner)
339                 return TRUE;
340
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);
345
346         return TRUE;
347 }
348
349 mono_bool
350 mono_profiler_set_sample_mode (MonoProfilerHandle handle, MonoProfilerSampleMode mode, uint32_t freq)
351 {
352         if (handle != mono_profiler_state.sampling_owner)
353                 return FALSE;
354
355         mono_profiler_state.sample_mode = mode;
356         mono_profiler_state.sample_freq = freq;
357
358         mono_profiler_sampling_thread_post ();
359
360         return TRUE;
361 }
362
363 mono_bool
364 mono_profiler_get_sample_mode (MonoProfilerHandle handle, MonoProfilerSampleMode *mode, uint32_t *freq)
365 {
366         if (mode)
367                 *mode = mono_profiler_state.sample_mode;
368
369         if (freq)
370                 *freq = mono_profiler_state.sample_freq;
371
372         return handle == mono_profiler_state.sampling_owner;
373 }
374
375 gboolean
376 mono_profiler_sampling_enabled (void)
377 {
378         return !!mono_profiler_state.sampling_owner;
379 }
380
381 void
382 mono_profiler_sampling_thread_post (void)
383 {
384         mono_os_sem_post (&mono_profiler_state.sampling_semaphore);
385 }
386
387 void
388 mono_profiler_sampling_thread_wait (void)
389 {
390         mono_os_sem_wait (&mono_profiler_state.sampling_semaphore, MONO_SEM_FLAGS_NONE);
391 }
392
393 mono_bool
394 mono_profiler_enable_allocations (void)
395 {
396         if (mono_profiler_state.startup_done)
397                 return FALSE;
398
399         return mono_profiler_state.allocations = TRUE;
400 }
401
402 void
403 mono_profiler_set_call_instrumentation_filter_callback (MonoProfilerHandle handle, MonoProfilerCallInstrumentationFilterCallback cb)
404 {
405         InterlockedWritePointer (&handle->call_instrumentation_filter, (gpointer) cb);
406 }
407
408 mono_bool
409 mono_profiler_enable_call_context_introspection (void)
410 {
411         if (mono_profiler_state.startup_done)
412                 return FALSE;
413
414         mono_profiler_state.context_enable ();
415
416         return mono_profiler_state.call_contexts = TRUE;
417 }
418
419 void *
420 mono_profiler_call_context_get_this (MonoProfilerCallContext *context)
421 {
422         if (!mono_profiler_state.call_contexts)
423                 return NULL;
424
425         return mono_profiler_state.context_get_this (context);
426 }
427
428 void *
429 mono_profiler_call_context_get_argument (MonoProfilerCallContext *context, uint32_t position)
430 {
431         if (!mono_profiler_state.call_contexts)
432                 return NULL;
433
434         return mono_profiler_state.context_get_argument (context, position);
435 }
436
437 void *
438 mono_profiler_call_context_get_local (MonoProfilerCallContext *context, uint32_t position)
439 {
440         if (!mono_profiler_state.call_contexts)
441                 return NULL;
442
443         return mono_profiler_state.context_get_local (context, position);
444 }
445
446 void *
447 mono_profiler_call_context_get_result (MonoProfilerCallContext *context)
448 {
449         if (!mono_profiler_state.call_contexts)
450                 return NULL;
451
452         return mono_profiler_state.context_get_result (context);
453 }
454
455 void
456 mono_profiler_call_context_free_buffer (void *buffer)
457 {
458         mono_profiler_state.context_free_buffer (buffer);
459 }
460
461 MonoProfilerCallInstrumentationFlags
462 mono_profiler_get_call_instrumentation_flags (MonoMethod *method)
463 {
464         MonoProfilerCallInstrumentationFlags flags = MONO_PROFILER_CALL_INSTRUMENTATION_NONE;
465
466         for (MonoProfilerHandle handle = mono_profiler_state.profilers; handle; handle = handle->next) {
467                 MonoProfilerCallInstrumentationFilterCallback cb = handle->call_instrumentation_filter;
468
469                 if (cb)
470                         flags |= cb (handle->prof, method);
471         }
472
473         return flags;
474 }
475
476 void
477 mono_profiler_started (void)
478 {
479         mono_profiler_state.startup_done = TRUE;
480 }
481
482 void
483 mono_profiler_cleanup (void)
484 {
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
506         }
507
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
527
528         MonoProfilerHandle head = mono_profiler_state.profilers;
529
530         while (head) {
531                 MonoProfilerCleanupCallback cb = head->cleanup_callback;
532
533                 if (cb)
534                         cb (head->prof);
535
536                 MonoProfilerHandle cur = head;
537                 head = head->next;
538
539                 g_free (cur);
540         }
541
542         if (mono_profiler_state.code_coverage) {
543                 mono_os_mutex_destroy (&mono_profiler_state.coverage_mutex);
544
545                 GHashTableIter iter;
546
547                 g_hash_table_iter_init (&iter, mono_profiler_state.coverage_hash);
548
549                 MonoProfilerCoverageInfo *info;
550
551                 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info))
552                         g_free (info);
553
554                 g_hash_table_destroy (mono_profiler_state.coverage_hash);
555         }
556
557         if (mono_profiler_state.sampling_owner)
558                 mono_os_sem_destroy (&mono_profiler_state.sampling_semaphore);
559 }
560
561 static void
562 update_callback (volatile gpointer *location, gpointer new_, volatile gint32 *counter)
563 {
564         gpointer old;
565
566         do {
567                 old = InterlockedReadPointer (location);
568         } while (InterlockedCompareExchangePointer (location, new_, old) != old);
569
570         /*
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.
577          */
578
579         if (old)
580                 InterlockedDecrement (counter);
581
582         if (new_)
583                 InterlockedIncrement (counter);
584 }
585
586 #define _MONO_PROFILER_EVENT(name, type) \
587         void \
588         mono_profiler_set_ ## name ## _callback (MonoProfilerHandle handle, MonoProfiler ## type ## Callback cb) \
589         { \
590                 update_callback (&handle->name ## _cb, (gpointer) cb, &mono_profiler_state.name ## _count); \
591         }
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
609
610 #define _MONO_PROFILER_EVENT(name, type, params, args) \
611         void \
612         mono_profiler_raise_ ## name params \
613         { \
614                 for (MonoProfilerHandle h = mono_profiler_state.profilers; h; h = h->next) { \
615                         MonoProfiler ## type ## Callback cb = h->name ## _cb; \
616                         if (cb) \
617                                 cb args; \
618                 } \
619         }
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
637
638 /*
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.
642  *
643  * TODO: Remove this some day if we're OK with breaking compatibility.
644  */
645
646 typedef void *MonoLegacyProfiler;
647
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);
653
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;
662 };
663
664 static MonoProfiler *current;
665
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);
671
672 static void
673 shutdown_cb (MonoProfiler *prof)
674 {
675         prof->shutdown_callback (prof->profiler);
676 }
677
678 void
679 mono_profiler_install (MonoLegacyProfiler *prof, MonoLegacyProfileFunc callback)
680 {
681         current = g_new0 (MonoProfiler, 1);
682         current->handle = mono_profiler_create (current);
683         current->profiler = prof;
684         current->shutdown_callback = callback;
685
686         if (callback)
687                 mono_profiler_set_runtime_shutdown_end_callback (current->handle, shutdown_cb);
688 }
689
690 static void
691 thread_start_cb (MonoProfiler *prof, uintptr_t tid)
692 {
693         prof->thread_start (prof->profiler, tid);
694 }
695
696 static void
697 thread_stop_cb (MonoProfiler *prof, uintptr_t tid)
698 {
699         prof->thread_end (prof->profiler, tid);
700 }
701
702 void
703 mono_profiler_install_thread (MonoLegacyProfileThreadFunc start, MonoLegacyProfileThreadFunc end)
704 {
705         current->thread_start = start;
706         current->thread_end = end;
707
708         if (start)
709                 mono_profiler_set_thread_started_callback (current->handle, thread_start_cb);
710
711         if (end)
712                 mono_profiler_set_thread_stopped_callback (current->handle, thread_stop_cb);
713 }
714
715 static void
716 gc_event_cb (MonoProfiler *prof, MonoProfilerGCEvent event, uint32_t generation)
717 {
718         prof->gc_event (prof->profiler, event, generation);
719 }
720
721 static void
722 gc_resize_cb (MonoProfiler *prof, uintptr_t size)
723 {
724         prof->gc_heap_resize (prof->profiler, size);
725 }
726
727 void
728 mono_profiler_install_gc (MonoLegacyProfileGCFunc callback, MonoLegacyProfileGCResizeFunc heap_resize_callback)
729 {
730         current->gc_event = callback;
731         current->gc_heap_resize = heap_resize_callback;
732
733         if (callback)
734                 mono_profiler_set_gc_event_callback (current->handle, gc_event_cb);
735
736         if (heap_resize_callback)
737                 mono_profiler_set_gc_resize_callback (current->handle, gc_resize_cb);
738 }
739
740 static void
741 jit_done_cb (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo)
742 {
743         prof->jit_end2 (prof->profiler, method, jinfo, 0);
744 }
745
746 static void
747 jit_failed_cb (MonoProfiler *prof, MonoMethod *method)
748 {
749         prof->jit_end2 (prof->profiler, method, NULL, 1);
750 }
751
752 void
753 mono_profiler_install_jit_end (MonoLegacyProfileJitResult end)
754 {
755         current->jit_end2 = end;
756
757         if (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);
760         }
761 }
762
763 void
764 mono_profiler_set_events (int flags)
765 {
766         /* Do nothing. */
767 }