2 * coverage.c: mono coverage profiler
5 * Paolo Molaro (lupus@ximian.com)
6 * Alex Rønne Petersen (alexrp@xamarin.com)
7 * Ludovic Henry (ludovic@xamarin.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13 * The Coverage XML output schema
23 * <coverage> - The root element of the documentation. It can contain any number of
24 * <assembly>, <class> or <method> elements.
26 * - version: The version number for the file format - (eg: "0.3")
27 * <assembly> - Contains data about assemblies. Has no child elements
29 * - name: The name of the assembly - (eg: "System.Xml")
30 * - guid: The GUID of the assembly
31 * - filename: The filename of the assembly
32 * - method-count: The number of methods in the assembly
33 * - full: The number of fully covered methods
34 * - partial: The number of partially covered methods
35 * <class> - Contains data about classes. Has no child elements
37 * - name: The name of the class
38 * - method-count: The number of methods in the class
39 * - full: The number of fully covered methods
40 * - partial: The number of partially covered methods
41 * <method> - Contains data about methods. Can contain any number of <statement> elements
43 * - assembly: The name of the parent assembly
44 * - class: The name of the parent class
45 * - name: The name of the method, with all it's parameters
46 * - filename: The name of the source file containing this method
48 * <statement> - Contains data about IL statements. Has no child elements
50 * - offset: The offset of the statement in the IL code after the previous
52 * - counter: 1 if the line was covered, 0 if it was not
53 * - line: The line number in the parent method's file
54 * - column: The column on the line
69 #ifdef HAVE_SYS_MMAN_H
72 #if defined (HAVE_SYS_ZLIB)
76 #include <mono/metadata/assembly.h>
77 #include <mono/metadata/debug-helpers.h>
78 #include <mono/metadata/profiler.h>
79 #include <mono/metadata/tabledefs.h>
80 #include <mono/metadata/metadata-internals.h>
82 #include <mono/utils/atomic.h>
83 #include <mono/utils/hazard-pointer.h>
84 #include <mono/utils/lock-free-queue.h>
85 #include <mono/utils/mono-conc-hashtable.h>
86 #include <mono/utils/mono-os-mutex.h>
87 #include <mono/utils/mono-logger-internals.h>
88 #include <mono/utils/mono-counters.h>
90 // Statistics for profiler events.
91 static gint32 coverage_methods_ctr,
92 coverage_statements_ctr,
94 coverage_assemblies_ctr;
96 struct _MonoProfiler {
97 MonoProfilerHandle handle;
107 MonoConcurrentHashTable *filtered_classes;
108 MonoConcurrentHashTable *suppressed_assemblies;
110 MonoConcurrentHashTable *methods;
111 MonoConcurrentHashTable *assemblies;
112 MonoConcurrentHashTable *classes;
114 MonoConcurrentHashTable *image_to_methods;
116 guint32 previous_offset;
120 //Where to compress the output file
123 //Name of the generated xml file
124 const char *output_filename;
126 //Filter files used by the code coverage mode
127 GPtrArray *cov_filter_files;
130 static ProfilerConfig coverage_config;
131 static struct _MonoProfiler coverage_profiler;
133 /* This is a very basic escape function that escapes < > and &
134 Ideally we'd use g_markup_escape_string but that function isn't
135 available in Mono's eglib. This was written without looking at the
136 source of that function in glib. */
138 escape_string_for_xml (const char *string)
140 GString *string_builder = g_string_new (NULL);
141 const char *start, *p;
145 while (*p && *p != '&' && *p != '<' && *p != '>')
148 g_string_append_len (string_builder, start, p - start);
155 g_string_append (string_builder, "<");
159 g_string_append (string_builder, ">");
163 g_string_append (string_builder, "&");
174 return g_string_free (string_builder, FALSE);
178 MonoLockFreeQueueNode node;
191 free_coverage_entry (gpointer data, gpointer userdata)
193 CoverageEntry *entry = (CoverageEntry *)data;
194 g_free (entry->filename);
199 obtain_coverage_for_method (MonoProfiler *prof, const MonoProfilerCoverageData *entry)
201 g_assert (prof == &coverage_profiler);
203 int offset = entry->il_offset - coverage_profiler.previous_offset;
204 CoverageEntry *e = g_new (CoverageEntry, 1);
206 coverage_profiler.previous_offset = entry->il_offset;
209 e->counter = entry->counter;
210 e->filename = g_strdup(entry->file_name ? entry->file_name : "");
211 e->line = entry->line;
212 e->column = entry->column;
214 g_ptr_array_add (coverage_profiler.data, e);
218 parse_generic_type_names(char *name)
220 char *new_name, *ret;
221 int within_generic_declaration = 0, generic_members = 1;
223 if (name == NULL || *name == '\0')
224 return g_strdup ("");
226 if (!(ret = new_name = (char *) g_calloc (strlen (name) * 4 + 1, sizeof (char))))
232 within_generic_declaration = 1;
236 within_generic_declaration = 0;
238 if (*(name - 1) != '<') {
240 *new_name++ = '0' + generic_members;
242 memcpy (new_name, "<>", 8);
254 if (!within_generic_declaration)
265 dump_method (gpointer key, gpointer value, gpointer userdata)
267 MonoMethod *method = (MonoMethod *)value;
270 char *class_name, *escaped_image_name, *escaped_class_name, *escaped_method_name, *escaped_method_signature, *escaped_method_filename;
271 const char *image_name, *method_name, *method_signature, *method_filename;
274 coverage_profiler.previous_offset = 0;
275 coverage_profiler.data = g_ptr_array_new ();
277 mono_profiler_get_coverage_data (coverage_profiler.handle, method, obtain_coverage_for_method);
279 klass = mono_method_get_class (method);
280 image = mono_class_get_image (klass);
281 image_name = mono_image_get_name (image);
283 method_signature = mono_signature_get_desc (mono_method_signature (method), TRUE);
284 class_name = parse_generic_type_names (mono_type_get_name (mono_class_get_type (klass)));
285 method_name = mono_method_get_name (method);
287 if (coverage_profiler.data->len != 0) {
288 CoverageEntry *entry = (CoverageEntry *)coverage_profiler.data->pdata[0];
289 method_filename = entry->filename ? entry->filename : "";
291 method_filename = "";
293 image_name = image_name ? image_name : "";
294 method_signature = method_signature ? method_signature : "";
295 method_name = method_name ? method_name : "";
297 escaped_image_name = escape_string_for_xml (image_name);
298 escaped_class_name = escape_string_for_xml (class_name);
299 escaped_method_name = escape_string_for_xml (method_name);
300 escaped_method_signature = escape_string_for_xml (method_signature);
301 escaped_method_filename = escape_string_for_xml (method_filename);
303 fprintf (coverage_profiler.file, "\t<method assembly=\"%s\" class=\"%s\" name=\"%s (%s)\" filename=\"%s\" token=\"%d\">\n",
304 escaped_image_name, escaped_class_name, escaped_method_name, escaped_method_signature, escaped_method_filename, mono_method_get_token (method));
306 g_free (escaped_image_name);
307 g_free (escaped_class_name);
308 g_free (escaped_method_name);
309 g_free (escaped_method_signature);
310 g_free (escaped_method_filename);
312 for (i = 0; i < coverage_profiler.data->len; i++) {
313 CoverageEntry *entry = (CoverageEntry *)coverage_profiler.data->pdata[i];
315 fprintf (coverage_profiler.file, "\t\t<statement offset=\"%d\" counter=\"%d\" line=\"%d\" column=\"%d\"/>\n",
316 entry->offset, entry->counter, entry->line, entry->column);
319 fprintf (coverage_profiler.file, "\t</method>\n");
323 g_ptr_array_foreach (coverage_profiler.data, free_coverage_entry, NULL);
324 g_ptr_array_free (coverage_profiler.data, TRUE);
327 /* This empties the queue */
329 count_queue (MonoLockFreeQueue *queue)
331 MonoLockFreeQueueNode *node;
334 while ((node = mono_lock_free_queue_dequeue (queue))) {
336 mono_thread_hazardous_try_free (node, g_free);
343 dump_classes_for_image (gpointer key, gpointer value, gpointer userdata)
345 MonoClass *klass = (MonoClass *)key;
346 MonoLockFreeQueue *class_methods = (MonoLockFreeQueue *)value;
348 char *class_name, *escaped_class_name;
349 const char *image_name;
350 int number_of_methods, partially_covered;
353 image = mono_class_get_image (klass);
354 image_name = mono_image_get_name (image);
356 if (!image_name || strcmp (image_name, mono_image_get_name (((MonoImage*) userdata))) != 0)
359 class_name = mono_type_get_name (mono_class_get_type (klass));
361 number_of_methods = mono_class_num_methods (klass);
362 fully_covered = count_queue (class_methods);
363 /* We don't handle partial covered yet */
364 partially_covered = 0;
366 escaped_class_name = escape_string_for_xml (class_name);
368 fprintf (coverage_profiler.file, "\t<class name=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n",
369 escaped_class_name, number_of_methods, fully_covered, partially_covered);
371 g_free (escaped_class_name);
378 get_coverage_for_image (MonoImage *image, int *number_of_methods, guint *fully_covered, int *partially_covered)
380 MonoLockFreeQueue *image_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (coverage_profiler.image_to_methods, image);
382 *number_of_methods = mono_image_get_table_rows (image, MONO_TABLE_METHOD);
384 *fully_covered = count_queue (image_methods);
388 // FIXME: We don't handle partially covered yet.
389 *partially_covered = 0;
393 dump_assembly (gpointer key, gpointer value, gpointer userdata)
395 MonoAssembly *assembly = (MonoAssembly *)value;
396 MonoImage *image = mono_assembly_get_image (assembly);
397 const char *image_name, *image_guid, *image_filename;
398 char *escaped_image_name, *escaped_image_filename;
399 int number_of_methods = 0, partially_covered = 0;
400 guint fully_covered = 0;
402 image_name = mono_image_get_name (image);
403 image_guid = mono_image_get_guid (image);
404 image_filename = mono_image_get_filename (image);
406 image_name = image_name ? image_name : "";
407 image_guid = image_guid ? image_guid : "";
408 image_filename = image_filename ? image_filename : "";
410 get_coverage_for_image (image, &number_of_methods, &fully_covered, &partially_covered);
412 escaped_image_name = escape_string_for_xml (image_name);
413 escaped_image_filename = escape_string_for_xml (image_filename);
415 fprintf (coverage_profiler.file, "\t<assembly name=\"%s\" guid=\"%s\" filename=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n",
416 escaped_image_name, image_guid, escaped_image_filename, number_of_methods, fully_covered, partially_covered);
418 g_free (escaped_image_name);
419 g_free (escaped_image_filename);
421 mono_conc_hashtable_foreach (coverage_profiler.classes, dump_classes_for_image, image);
427 fprintf (coverage_profiler.file, "<?xml version=\"1.0\"?>\n");
428 fprintf (coverage_profiler.file, "<coverage version=\"0.3\">\n");
430 mono_os_mutex_lock (&coverage_profiler.mutex);
431 mono_conc_hashtable_foreach (coverage_profiler.assemblies, dump_assembly, NULL);
432 mono_conc_hashtable_foreach (coverage_profiler.methods, dump_method, NULL);
433 mono_os_mutex_unlock (&coverage_profiler.mutex);
435 fprintf (coverage_profiler.file, "</coverage>\n");
438 static MonoLockFreeQueueNode *
439 create_method_node (MonoMethod *method)
441 MethodNode *node = (MethodNode *) g_malloc (sizeof (MethodNode));
442 mono_lock_free_queue_node_init ((MonoLockFreeQueueNode *) node, FALSE);
443 node->method = method;
445 return (MonoLockFreeQueueNode *) node;
449 coverage_filter (MonoProfiler *prof, MonoMethod *method)
454 MonoAssembly *assembly;
455 MonoMethodHeader *header;
456 guint32 iflags, flags, code_size;
457 char *fqn, *classname;
458 gboolean has_positive, found;
459 MonoLockFreeQueue *image_methods, *class_methods;
460 MonoLockFreeQueueNode *node;
462 g_assert (prof == &coverage_profiler);
464 flags = mono_method_get_flags (method, &iflags);
465 if ((iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
466 (flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
469 // Don't need to do anything else if we're already tracking this method
470 if (mono_conc_hashtable_lookup (coverage_profiler.methods, method))
473 klass = mono_method_get_class (method);
474 image = mono_class_get_image (klass);
476 // Don't handle coverage for the core assemblies
477 if (mono_conc_hashtable_lookup (coverage_profiler.suppressed_assemblies, (gpointer) mono_image_get_name (image)) != NULL)
480 if (coverage_profiler.filters) {
481 /* Check already filtered classes first */
482 if (mono_conc_hashtable_lookup (coverage_profiler.filtered_classes, klass))
485 classname = mono_type_get_name (mono_class_get_type (klass));
487 fqn = g_strdup_printf ("[%s]%s", mono_image_get_name (image), classname);
489 // Check positive filters first
490 has_positive = FALSE;
492 for (guint i = 0; i < coverage_profiler.filters->len; ++i) {
493 char *filter = (char *)g_ptr_array_index (coverage_profiler.filters, i);
495 if (filter [0] == '+') {
496 filter = &filter [1];
498 if (strstr (fqn, filter) != NULL)
505 if (has_positive && !found) {
506 mono_os_mutex_lock (&coverage_profiler.mutex);
507 mono_conc_hashtable_insert (coverage_profiler.filtered_classes, klass, klass);
508 mono_os_mutex_unlock (&coverage_profiler.mutex);
515 for (guint i = 0; i < coverage_profiler.filters->len; ++i) {
516 // FIXME: Is substring search sufficient?
517 char *filter = (char *)g_ptr_array_index (coverage_profiler.filters, i);
518 if (filter [0] == '+')
522 filter = &filter [1];
524 if (strstr (fqn, filter) != NULL) {
525 mono_os_mutex_lock (&coverage_profiler.mutex);
526 mono_conc_hashtable_insert (coverage_profiler.filtered_classes, klass, klass);
527 mono_os_mutex_unlock (&coverage_profiler.mutex);
539 header = mono_method_get_header_checked (method, &error);
540 mono_error_cleanup (&error);
542 mono_method_header_get_code (header, &code_size, NULL);
544 assembly = mono_image_get_assembly (image);
546 // Need to keep the assemblies around for as long as they are kept in the hashtable
547 // Nunit, for example, has a habit of unloading them before the coverage statistics are
548 // generated causing a crash. See https://bugzilla.xamarin.com/show_bug.cgi?id=39325
549 mono_assembly_addref (assembly);
551 mono_os_mutex_lock (&coverage_profiler.mutex);
552 mono_conc_hashtable_insert (coverage_profiler.methods, method, method);
553 mono_conc_hashtable_insert (coverage_profiler.assemblies, assembly, assembly);
554 mono_os_mutex_unlock (&coverage_profiler.mutex);
556 image_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (coverage_profiler.image_to_methods, image);
558 if (image_methods == NULL) {
559 image_methods = (MonoLockFreeQueue *) g_malloc (sizeof (MonoLockFreeQueue));
560 mono_lock_free_queue_init (image_methods);
561 mono_os_mutex_lock (&coverage_profiler.mutex);
562 mono_conc_hashtable_insert (coverage_profiler.image_to_methods, image, image_methods);
563 mono_os_mutex_unlock (&coverage_profiler.mutex);
566 node = create_method_node (method);
567 mono_lock_free_queue_enqueue (image_methods, node);
569 class_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (coverage_profiler.classes, klass);
571 if (class_methods == NULL) {
572 class_methods = (MonoLockFreeQueue *) g_malloc (sizeof (MonoLockFreeQueue));
573 mono_lock_free_queue_init (class_methods);
574 mono_os_mutex_lock (&coverage_profiler.mutex);
575 mono_conc_hashtable_insert (coverage_profiler.classes, klass, class_methods);
576 mono_os_mutex_unlock (&coverage_profiler.mutex);
579 node = create_method_node (method);
580 mono_lock_free_queue_enqueue (class_methods, node);
585 #define LINE_BUFFER_SIZE 4096
586 /* Max file limit of 128KB */
587 #define MAX_FILE_SIZE 128 * 1024
589 get_file_content (const gchar *filename)
597 stream = fopen (filename, "r");
601 res = fseek (stream, 0, SEEK_END);
607 filesize = ftell (stream);
613 res = fseek (stream, 0, SEEK_SET);
619 if (filesize > MAX_FILE_SIZE) {
624 buffer = (char *) g_malloc ((filesize + 1) * sizeof (char));
625 while ((bytes_read = fread (buffer + offset, 1, LINE_BUFFER_SIZE, stream)) > 0)
626 offset += bytes_read;
628 /* NULL terminate our buffer */
629 buffer[filesize] = '\0';
636 get_next_line (char *contents, char **next_start)
640 if (p == NULL || *p == '\0') {
645 while (*p != '\n' && *p != '\0')
658 init_suppressed_assemblies (void)
663 coverage_profiler.suppressed_assemblies = mono_conc_hashtable_new (g_str_hash, g_str_equal);
665 /* Don't need to free content as it is referred to by the lines stored in @filters */
666 content = get_file_content (SUPPRESSION_DIR "/mono-profiler-coverage.suppression");
668 content = get_file_content (SUPPRESSION_DIR "/mono-profiler-log.suppression");
672 while ((line = get_next_line (content, &content))) {
673 line = g_strchomp (g_strchug (line));
674 /* No locking needed as we're doing initialization */
675 mono_conc_hashtable_insert (coverage_profiler.suppressed_assemblies, line, line);
680 parse_cov_filter_file (GPtrArray *filters, const char *file)
685 /* Don't need to free content as it is referred to by the lines stored in @filters */
686 content = get_file_content (file);
687 if (content == NULL) {
688 mono_profiler_printf_err ("Could not open coverage filter file '%s'.", file);
692 while ((line = get_next_line (content, &content)))
693 g_ptr_array_add (filters, g_strchug (g_strchomp (line)));
697 unref_coverage_assemblies (gpointer key, gpointer value, gpointer userdata)
699 MonoAssembly *assembly = (MonoAssembly *)value;
700 mono_assembly_close (assembly);
704 log_shutdown (MonoProfiler *prof)
706 g_assert (prof == &coverage_profiler);
710 mono_os_mutex_lock (&coverage_profiler.mutex);
711 mono_conc_hashtable_foreach (coverage_profiler.assemblies, unref_coverage_assemblies, NULL);
712 mono_os_mutex_unlock (&coverage_profiler.mutex);
714 mono_conc_hashtable_destroy (coverage_profiler.methods);
715 mono_conc_hashtable_destroy (coverage_profiler.assemblies);
716 mono_conc_hashtable_destroy (coverage_profiler.classes);
717 mono_conc_hashtable_destroy (coverage_profiler.filtered_classes);
719 mono_conc_hashtable_destroy (coverage_profiler.image_to_methods);
720 mono_conc_hashtable_destroy (coverage_profiler.suppressed_assemblies);
721 mono_os_mutex_destroy (&coverage_profiler.mutex);
723 if (*coverage_config.output_filename == '|') {
724 pclose (coverage_profiler.file);
725 } else if (*coverage_config.output_filename == '#') {
728 fclose (coverage_profiler.file);
731 g_free (coverage_profiler.args);
735 runtime_initialized (MonoProfiler *profiler)
737 mono_counters_register ("Event: Coverage methods", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &coverage_methods_ctr);
738 mono_counters_register ("Event: Coverage statements", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &coverage_statements_ctr);
739 mono_counters_register ("Event: Coverage classes", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &coverage_classes_ctr);
740 mono_counters_register ("Event: Coverage assemblies", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &coverage_assemblies_ctr);
743 static void usage (void);
746 match_option (const char *arg, const char *opt_name, const char **rval)
749 const char *end = strchr (arg, '=');
753 return !strcmp (arg, opt_name);
755 if (strncmp (arg, opt_name, strlen (opt_name)) || (end - arg) > strlen (opt_name) + 1)
760 //FIXME how should we handle passing a value to an arg that doesn't expect it?
761 return !strcmp (arg, opt_name);
766 parse_arg (const char *arg)
770 if (match_option (arg, "help", NULL)) {
772 // } else if (match_option (arg, "zip", NULL)) {
773 // coverage_config.use_zip = TRUE;
774 } else if (match_option (arg, "output", &val)) {
775 coverage_config.output_filename = g_strdup (val);
776 // } else if (match_option (arg, "covfilter", &val)) {
777 // g_error ("not supported");
778 } else if (match_option (arg, "covfilter-file", &val)) {
779 if (coverage_config.cov_filter_files == NULL)
780 coverage_config.cov_filter_files = g_ptr_array_new ();
781 g_ptr_array_add (coverage_config.cov_filter_files, g_strdup (val));
783 mono_profiler_printf_err ("Could not parse argument: %s", arg);
788 parse_args (const char *desc)
791 gboolean in_quotes = FALSE;
792 char quote_char = '\0';
793 char *buffer = malloc (strlen (desc));
796 for (p = desc; *p; p++){
800 if (buffer_pos != 0){
801 buffer [buffer_pos] = 0;
806 buffer [buffer_pos++] = *p;
812 buffer [buffer_pos++] = p[1];
819 if (quote_char == *p)
822 buffer [buffer_pos++] = *p;
829 buffer [buffer_pos++] = *p;
834 if (buffer_pos != 0) {
835 buffer [buffer_pos] = 0;
845 mono_profiler_printf ("Mono coverage profiler");
846 mono_profiler_printf ("Usage: mono --profile=coverage[:OPTION1[,OPTION2...]] program.exe\n");
847 mono_profiler_printf ("Options:");
848 mono_profiler_printf ("\thelp show this usage info");
850 // mono_profiler_printf ("\tcovfilter=ASSEMBLY add ASSEMBLY to the code coverage filters");
851 // mono_profiler_printf ("\t prefix a + to include the assembly or a - to exclude it");
852 // mono_profiler_printf ("\t e.g. covfilter=-mscorlib");
853 mono_profiler_printf ("\tcovfilter-file=FILE use FILE to generate the list of assemblies to be filtered");
854 mono_profiler_printf ("\toutput=FILENAME write the data to file FILENAME (the file is always overwritten)");
855 mono_profiler_printf ("\toutput=+FILENAME write the data to file FILENAME.pid (the file is always overwritten)");
856 mono_profiler_printf ("\toutput=|PROGRAM write the data to the stdin of PROGRAM");
857 mono_profiler_printf ("\toutput=|PROGRAM write the data to the stdin of PROGRAM");
858 // mono_profiler_printf ("\tzip compress the output data");
862 mono_profiler_init_coverage (const char *desc);
865 mono_profiler_init_coverage (const char *desc)
867 GPtrArray *filters = NULL;
869 parse_args (desc [strlen("coverage")] == ':' ? desc + strlen ("coverage") + 1 : "");
871 if (coverage_config.cov_filter_files) {
872 filters = g_ptr_array_new ();
874 for (i = 0; i < coverage_config.cov_filter_files->len; ++i) {
875 const char *name = coverage_config.cov_filter_files->pdata [i];
876 parse_cov_filter_file (filters, name);
880 coverage_profiler.args = g_strdup (desc);
882 //If coverage_config.output_filename begin with +, append the pid at the end
883 if (!coverage_config.output_filename)
884 coverage_config.output_filename = "coverage.xml";
885 else if (*coverage_config.output_filename == '+')
886 coverage_config.output_filename = g_strdup_printf ("%s.%d", coverage_config.output_filename + 1, getpid ());
888 if (*coverage_config.output_filename == '|')
889 coverage_profiler.file = popen (coverage_config.output_filename + 1, "w");
890 else if (*coverage_config.output_filename == '#')
891 coverage_profiler.file = fdopen (strtol (coverage_config.output_filename + 1, NULL, 10), "a");
893 coverage_profiler.file = fopen (coverage_config.output_filename, "w");
895 if (!coverage_profiler.file) {
896 mono_profiler_printf_err ("Could not create coverage profiler output file '%s'.", coverage_config.output_filename);
900 mono_os_mutex_init (&coverage_profiler.mutex);
901 coverage_profiler.methods = mono_conc_hashtable_new (NULL, NULL);
902 coverage_profiler.assemblies = mono_conc_hashtable_new (NULL, NULL);
903 coverage_profiler.classes = mono_conc_hashtable_new (NULL, NULL);
904 coverage_profiler.filtered_classes = mono_conc_hashtable_new (NULL, NULL);
905 coverage_profiler.image_to_methods = mono_conc_hashtable_new (NULL, NULL);
906 init_suppressed_assemblies ();
908 coverage_profiler.filters = filters;
910 MonoProfilerHandle handle = coverage_profiler.handle = mono_profiler_create (&coverage_profiler);
913 * Required callbacks. These are either necessary for the profiler itself
914 * to function, or provide metadata that's needed if other events (e.g.
915 * allocations, exceptions) are dynamically enabled/disabled.
918 mono_profiler_set_runtime_shutdown_end_callback (handle, log_shutdown);
919 mono_profiler_set_runtime_initialized_callback (handle, runtime_initialized);
921 mono_profiler_enable_coverage ();
922 mono_profiler_set_coverage_filter_callback (handle, coverage_filter);