Merge pull request #5433 from lateralusX/lateralusX/windows-mono-debugger-soft-hang
authorJohan Lorensson <lateralusx.github@gmail.com>
Tue, 26 Sep 2017 06:33:40 +0000 (08:33 +0200)
committerGitHub <noreply@github.com>
Tue, 26 Sep 2017 06:33:40 +0000 (08:33 +0200)
Fix sporadic hang in Mono.Debugger.Soft test suite on Windows.

17 files changed:
Makefile.am
man/mono.1
man/mprof-report.1
mono/arch/ppc/ppc-codegen.h
mono/metadata/class.c
mono/metadata/class.h
mono/metadata/w32handle.c
mono/mini/aot-compiler.c
mono/mini/cpu-ppc.md
mono/mini/cpu-ppc64.md
mono/mini/mini-ppc.c
mono/profiler/Makefile.am
mono/profiler/coverage.c [new file with mode: 0644]
mono/profiler/log-args.c
mono/tests/Makefile.am
mono/tests/bug-59281.cs [new file with mode: 0644]
msvc/scripts/genproj.cs

index c322d249127837da530f6c936ee1f98df0ed6540..12b27f14b08425ea15bae3ce0d3af13287c02368 100644 (file)
@@ -145,4 +145,7 @@ update-llvm-version:
 update-solution-files:
        make update-csproj
        make package-inputs
-       (cd msvc/scripts; make genproj.exe; mono genproj.exe)
+       (cd msvc/scripts; make genproj.exe; mono genproj.exe $(GENPROJ_ARGS))
+
+update-solution-files-with-tests:
+       make "GENPROJ_ARGS=2012 true true" update-solution-files
index 7d2f14c151e5a42709de29d68df5022822d01667..5ac6ea81002aa08bca26649d78f1ea3288f0fe8e 100644 (file)
@@ -908,8 +908,11 @@ your profiler.
 For a sample of how to write your own custom profiler look in the
 Mono source tree for in the samples/profiler.c.
 .SH CODE COVERAGE
-Mono ships with a code coverage module in the \f[I]log\f[] profiler.
-Check the `coverage' option on the mprof-report(1) page for more details.
+Mono ships with a code coverage module in the \f[I]coverage\f[] profiler.
+To enable it, pass \fB--profile=coverage\fR to your mono invocation. It
+will by default output a coverage.xml in the current directory. Use
+\fBmono --profile=coverage:help sample.exe\fR for more information on the
+different options.
 .SH AOT PROFILING
 You can improve startup performance by using the AOT profiler.
 .PP
index 56663251bf8e0066c83fd9f700eb835d724a2ff2..45ea4f77b8825258569dafb600aa9461d41a6b5e 100644 (file)
@@ -213,9 +213,6 @@ The following commands are available:
 .IP \[bu] 2
 \f[I]nocounters\f[]: disables sampling of runtime and performance
 counters, which is normally done every 1 second.
-.IP \[bu] 2
-\f[I]coverage\f[]: collect code coverage data. This implies enabling
-the \f[I]calls\f[] option.
 .RE
 .SS Analyzing the profile data
 .PP
@@ -338,8 +335,6 @@ version
 .IP \[bu] 2
 \f[I]counters\f[]: counters samples
 .IP \[bu] 2
-\f[I]coverage\f[]: code coverage data
-.IP \[bu] 2
 \f[I]stats\f[]: event statistics
 .PP
 It is possible to limit some of the data displayed to a timeframe
@@ -411,13 +406,6 @@ By default mprof-report will print the summary data to the console.
 To print it to a file, instead, use the option:
 .PP
 \f[B]--out=FILENAME\f[]
-.SS Processing code coverage data
-.PP
-If you ran the profiler with the \f[I]coverage\f[] option, you can
-process the collected coverage data into an XML file by running
-mprof-report like this:
-.PP
-\f[B]mprof-report --coverage-out=coverage.xml output.mlpd\f[]
 .SS Dealing with profiler slowness
 .PP
 If the profiler needs to collect lots of data, the execution of the
index 869365b4bccd9b3d93d99dd1184dfbc049253d8f..98fed7525fb2937dea3a7cc334c33c026d596503 100644 (file)
@@ -333,7 +333,7 @@ my and Ximian's copyright to this code. ;)
 #define ppc_andid(c,S,A,ui) ppc_emit32(c, (28 << 26) | ((S) << 21 ) | ((A) << 16) | ((guint16)(ui)))
 #define ppc_andisd(c,S,A,ui) ppc_emit32(c, (29 << 26) | ((S) << 21 ) | ((A) << 16) | ((guint16)(ui)))
 
-#define ppc_bcx(c,BO,BI,BD,AA,LK) ppc_emit32(c, (16 << 26) | (BO << 21 )| (BI << 16) | (BD << 2) | ((AA) << 1) | LK)
+#define ppc_bcx(c,BO,BI,BD,AA,LK) ppc_emit32(c, (16 << 26) | ((BO) << 21 )| ((BI) << 16) | (BD << 2) | ((AA) << 1) | LK)
 #define ppc_bc(c,BO,BI,BD) ppc_bcx(c,BO,BI,BD,0,0) 
 #define ppc_bca(c,BO,BI,BD) ppc_bcx(c,BO,BI,BD,1,0)
 #define ppc_bcl(c,BO,BI,BD) ppc_bcx(c,BO,BI,BD,0,1)
index f00d436281a5d0d1a05075f56870a49d37183875..f3e87fe6428f9296d921a09a1405f21ae87f235f 100644 (file)
@@ -2926,6 +2926,10 @@ collect_implemented_interfaces_aux (MonoClass *klass, GPtrArray **res, GHashTabl
                        *ifaces = g_hash_table_new (NULL, NULL);
                if (g_hash_table_lookup (*ifaces, ic))
                        continue;
+               /* A gparam is not an implemented interface for the purposes of
+                * mono_class_get_implemented_interfaces */
+               if (mono_class_is_gparam (ic))
+                       continue;
                g_ptr_array_add (*res, ic);
                g_hash_table_insert (*ifaces, ic, ic);
                mono_class_init (ic);
@@ -3316,7 +3320,9 @@ setup_interface_offsets (MonoClass *klass, int cur_slot, gboolean overwrite)
                for (i = 0; i < k->interface_count; i++) {
                        ic = k->interfaces [i];
 
-                       mono_class_init (ic);
+                       /* A gparam does not have any interface_id set. */
+                       if (! mono_class_is_gparam (ic))
+                               mono_class_init (ic);
 
                        if (max_iid < ic->interface_id)
                                max_iid = ic->interface_id;
index f79cd9bff99e928cd3b8c9834b69311126c43fcd..565dac59e4f215b0ac4c2dd7a5abf35b10b62195 100644 (file)
@@ -45,6 +45,7 @@ mono_class_from_typeref    (MonoImage *image, uint32_t type_token);
 MONO_API MonoClass *
 mono_class_from_typeref_checked (MonoImage *image, uint32_t type_token, MonoError *error);
 
+MONO_RT_EXTERNAL_ONLY
 MONO_API MonoClass *
 mono_class_from_generic_parameter (MonoGenericParam *param, MonoImage *image, mono_bool is_mvar);
 
index 98d4ace6d62593b62e3b0e1be6ace694f8333ff6..654b215538d800de195db57afeca5ccaaa9176e9 100644 (file)
@@ -1218,8 +1218,13 @@ mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waital
                signalled = (waitall && count == nhandles) || (!waitall && count > 0);
 
                if (signalled) {
-                       for (i = 0; i < nhandles; i++)
-                               own_if_signalled (handles [i], &abandoned [i]);
+                       for (i = 0; i < nhandles; i++) {
+                               if (own_if_signalled (handles [i], &abandoned [i]) && !waitall) {
+                                       /* if we are calling WaitHandle.WaitAny, .NET only owns the first one; it matters for Mutex which
+                                        * throw AbandonedMutexException in case we owned it but didn't release it */
+                                       break;
+                               }
+                       }
                }
 
                mono_w32handle_unlock_handles (handles, nhandles);
index 125110303adc583d0bf57c0457bfd357e021114b..6b91543a56b6bbfd9e7f6a4c32f6bdb29911f289 100644 (file)
@@ -2897,7 +2897,7 @@ encode_klass_ref_inner (MonoAotCompile *acfg, MonoClass *klass, guint8 *buf, gui
                if (par->gshared_constraint) {
                        MonoGSharedGenericParam *gpar = (MonoGSharedGenericParam*)par;
                        encode_type (acfg, par->gshared_constraint, p, &p);
-                       encode_klass_ref (acfg, mono_class_from_generic_parameter (gpar->parent, NULL, klass->byval_arg.type == MONO_TYPE_MVAR), p, &p);
+                       encode_klass_ref (acfg, mono_class_from_generic_parameter_internal (gpar->parent), p, &p);
                } else {
                        encode_value (klass->byval_arg.type, p, &p);
                        encode_value (mono_type_get_generic_param_num (&klass->byval_arg), p, &p);
index 65cdc9b149dd3c28f06edd9b80af305577919846..0baa0fe3c993261b2366ed0c5b15e6c864c556de 100644 (file)
@@ -198,6 +198,9 @@ float_cgt_un: dest:i src1:f src2:f len:20
 float_clt: dest:i src1:f src2:f len:16
 float_clt_un: dest:i src1:f src2:f len:20
 float_conv_to_u: dest:i src1:f len:36
+float_cneq: dest:i src1:f src2:f len:16
+float_cge: dest:i src1:f src2:f len:16
+float_cle: dest:i src1:f src2:f len:16
 call_handler: len:12 clob:c
 endfilter: src1:i len:32
 aot_const: dest:i len:8
@@ -289,6 +292,12 @@ int_cgt_un: dest:i len:12
 int_clt: dest:i len:12
 int_clt_un: dest:i len:12
 
+int_cneq: dest:i len:12
+int_cge: dest:i len:12
+int_cle: dest:i len:12
+int_cge_un: dest:i len:12
+int_cle_un: dest:i len:12
+
 cond_exc_ieq: len:8
 cond_exc_ine_un: len:8
 cond_exc_ilt: len:8
index 31119afdafc47395de492839c77c5a16efd0ab9e..2c28e7dca7cd78c0d539da4e1b08306beee76472 100644 (file)
@@ -201,6 +201,9 @@ float_cgt_un: dest:i src1:f src2:f len:20
 float_clt: dest:i src1:f src2:f len:16
 float_clt_un: dest:i src1:f src2:f len:20
 float_conv_to_u: dest:i src1:f len:36
+float_cneq: dest:i src1:f src2:f len:16
+float_cge: dest:i src1:f src2:f len:16
+float_cle: dest:i src1:f src2:f len:16
 call_handler: len:12 clob:c
 endfilter: src1:i len:20
 aot_const: dest:i len:8
@@ -293,6 +296,12 @@ int_cgt_un: dest:i len:12
 int_clt: dest:i len:12
 int_clt_un: dest:i len:12
 
+int_cneq: dest:i len:12
+int_cge: dest:i len:12
+int_cle: dest:i len:12
+int_cge_un: dest:i len:12
+int_cle_un: dest:i len:12
+
 cond_exc_ieq: len:8
 cond_exc_ine_un: len:8
 cond_exc_ilt: len:8
index 1a947639a241d3692584586ae0a099a69ecc9856..4ba20cc2257e31ff9ad4e154ca7267b2b939d124 100644 (file)
@@ -1005,8 +1005,7 @@ get_call_info (MonoMethodSignature *sig)
        fr = PPC_FIRST_FPARG_REG;
        gr = PPC_FIRST_ARG_REG;
 
-       /* FIXME: handle returning a struct */
-       if (MONO_TYPE_ISSTRUCT (sig->ret)) {
+       if (mini_type_is_vtype (sig->ret)) {
                cinfo->vtype_retaddr = TRUE;
        }
 
@@ -4126,6 +4125,11 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        ppc_mtctr (code, ins->sreg1);
                        ppc_bcctr (code, PPC_BR_ALWAYS, 0);
                        break;
+               case OP_ICNEQ:
+                       ppc_li (code, ins->dreg, 0);
+                       ppc_bc (code, PPC_BR_TRUE, PPC_BR_EQ, 2);
+                       ppc_li (code, ins->dreg, 1);
+                       break;
                case OP_CEQ:
                case OP_ICEQ:
                CASE_PPC64 (OP_LCEQ)
@@ -4143,6 +4147,12 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        ppc_bc (code, PPC_BR_TRUE, PPC_BR_LT, 2);
                        ppc_li (code, ins->dreg, 0);
                        break;
+               case OP_ICGE:
+               case OP_ICGE_UN:
+                       ppc_li (code, ins->dreg, 1);
+                       ppc_bc (code, PPC_BR_FALSE, PPC_BR_LT, 2);
+                       ppc_li (code, ins->dreg, 0);
+                       break;
                case OP_CGT:
                case OP_CGT_UN:
                case OP_ICGT:
@@ -4153,6 +4163,12 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        ppc_bc (code, PPC_BR_TRUE, PPC_BR_GT, 2);
                        ppc_li (code, ins->dreg, 0);
                        break;
+               case OP_ICLE:
+               case OP_ICLE_UN:
+                       ppc_li (code, ins->dreg, 1);
+                       ppc_bc (code, PPC_BR_FALSE, PPC_BR_GT, 2);
+                       ppc_li (code, ins->dreg, 0);
+                       break;
                case OP_COND_EXC_EQ:
                case OP_COND_EXC_NE_UN:
                case OP_COND_EXC_LT:
@@ -4355,15 +4371,17 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        ppc_fcmpu (code, 0, ins->sreg1, ins->sreg2);
                        break;
                case OP_FCEQ:
+               case OP_FCNEQ:
                        ppc_fcmpo (code, 0, ins->sreg1, ins->sreg2);
-                       ppc_li (code, ins->dreg, 0);
-                       ppc_bc (code, PPC_BR_FALSE, PPC_BR_EQ, 2);
                        ppc_li (code, ins->dreg, 1);
+                       ppc_bc (code, ins->opcode == OP_FCEQ ? PPC_BR_TRUE : PPC_BR_FALSE, PPC_BR_EQ, 2);
+                       ppc_li (code, ins->dreg, 0);
                        break;
                case OP_FCLT:
+               case OP_FCGE:
                        ppc_fcmpo (code, 0, ins->sreg1, ins->sreg2);
                        ppc_li (code, ins->dreg, 1);
-                       ppc_bc (code, PPC_BR_TRUE, PPC_BR_LT, 2);
+                       ppc_bc (code, ins->opcode == OP_FCLT ? PPC_BR_TRUE : PPC_BR_FALSE, PPC_BR_LT, 2);
                        ppc_li (code, ins->dreg, 0);
                        break;
                case OP_FCLT_UN:
@@ -4374,9 +4392,10 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        ppc_li (code, ins->dreg, 0);
                        break;
                case OP_FCGT:
+               case OP_FCLE:
                        ppc_fcmpo (code, 0, ins->sreg1, ins->sreg2);
                        ppc_li (code, ins->dreg, 1);
-                       ppc_bc (code, PPC_BR_TRUE, PPC_BR_GT, 2);
+                       ppc_bc (code, ins->opcode == OP_FCGT ? PPC_BR_TRUE : PPC_BR_FALSE, PPC_BR_GT, 2);
                        ppc_li (code, ins->dreg, 0);
                        break;
                case OP_FCGT_UN:
index 88cd9d37a1a2851aa5944c04a5feb265f5651767..212278b57e407b178b84e22766c55e864084e043 100644 (file)
@@ -25,6 +25,8 @@ lib_LTLIBRARIES = \
        libmono-profiler-iomap-static.la \
        libmono-profiler-log.la \
        libmono-profiler-log-static.la \
+       libmono-profiler-coverage.la \
+       libmono-profiler-coverage-static.la \
        $(vtune_libs)
 
 suppressiondir = $(datadir)/mono-$(API_VER)/mono/profiler
@@ -83,6 +85,12 @@ libmono_profiler_log_la_LDFLAGS = $(prof_ldflags)
 libmono_profiler_log_static_la_SOURCES = log.c log-args.c
 libmono_profiler_log_static_la_LDFLAGS = -static
 
+libmono_profiler_coverage_la_SOURCES = coverage.c
+libmono_profiler_coverage_la_LIBADD = $(libmono_dep) $(GLIB_LIBS) $(Z_LIBS)
+libmono_profiler_coverage_la_LDFLAGS = $(prof_ldflags)
+libmono_profiler_coverage_static_la_SOURCES = coverage.c
+libmono_profiler_coverage_static_la_LDFLAGS = -static
+
 if HAVE_VTUNE
 libmono_profiler_vtune_la_SOURCES = vtune.c
 libmono_profiler_vtune_la_CFLAGS = $(VTUNE_CFLAGS)
diff --git a/mono/profiler/coverage.c b/mono/profiler/coverage.c
new file mode 100644 (file)
index 0000000..5a9d5b6
--- /dev/null
@@ -0,0 +1,923 @@
+/*
+ * coverage.c: mono coverage profiler
+ *
+ * Authors:
+ *   Paolo Molaro (lupus@ximian.com)
+ *   Alex Rønne Petersen (alexrp@xamarin.com)
+ *   Ludovic Henry (ludovic@xamarin.com)
+ *
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
+ */
+
+/*
+ * The Coverage XML output schema
+ * <coverage>
+ *   <assembly/>
+ *   <class/>
+ *   <method>
+ *     <statement/>
+ *   </method>
+ * </coverage>
+ *
+ * Elements:
+ *   <coverage> - The root element of the documentation. It can contain any number of
+ *                <assembly>, <class> or <method> elements.
+ *                Attributes:
+ *                   - version: The version number for the file format - (eg: "0.3")
+ *   <assembly> - Contains data about assemblies. Has no child elements
+ *                Attributes:
+ *                   - name: The name of the assembly - (eg: "System.Xml")
+ *                   - guid: The GUID of the assembly
+ *                   - filename: The filename of the assembly
+ *                   - method-count: The number of methods in the assembly
+ *                   - full: The number of fully covered methods
+ *                   - partial: The number of partially covered methods
+ *   <class> - Contains data about classes. Has no child elements
+ *             Attributes:
+ *                - name: The name of the class
+ *                - method-count: The number of methods in the class
+ *                - full: The number of fully covered methods
+ *                - partial: The number of partially covered methods
+ *   <method> - Contains data about methods. Can contain any number of <statement> elements
+ *              Attributes:
+ *                 - assembly: The name of the parent assembly
+ *                 - class: The name of the parent class
+ *                 - name: The name of the method, with all it's parameters
+ *                 - filename: The name of the source file containing this method
+ *                 - token
+ *   <statement> - Contains data about IL statements. Has no child elements
+ *                 Attributes:
+ *                    - offset: The offset of the statement in the IL code after the previous
+ *                              statement's offset
+ *                    - counter: 1 if the line was covered, 0 if it was not
+ *                    - line: The line number in the parent method's file
+ *                    - column: The column on the line
+ */
+
+#include <config.h>
+#include <glib.h>
+
+#include <stdio.h>
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#if defined (HAVE_SYS_ZLIB)
+#include <zlib.h>
+#endif
+
+#include <mono/metadata/assembly.h>
+#include <mono/metadata/debug-helpers.h>
+#include <mono/metadata/profiler.h>
+#include <mono/metadata/tabledefs.h>
+#include <mono/metadata/metadata-internals.h>
+
+#include <mono/utils/atomic.h>
+#include <mono/utils/hazard-pointer.h>
+#include <mono/utils/lock-free-queue.h>
+#include <mono/utils/mono-conc-hashtable.h>
+#include <mono/utils/mono-os-mutex.h>
+#include <mono/utils/mono-logger-internals.h>
+#include <mono/utils/mono-counters.h>
+
+// Statistics for profiler events.
+static gint32 coverage_methods_ctr,
+              coverage_statements_ctr,
+              coverage_classes_ctr,
+              coverage_assemblies_ctr;
+
+struct _MonoProfiler {
+       MonoProfilerHandle handle;
+
+       FILE* file;
+
+       char *args;
+
+       mono_mutex_t mutex;
+       GPtrArray *data;
+
+       GPtrArray *filters;
+       MonoConcurrentHashTable *filtered_classes;
+       MonoConcurrentHashTable *suppressed_assemblies;
+
+       MonoConcurrentHashTable *methods;
+       MonoConcurrentHashTable *assemblies;
+       MonoConcurrentHashTable *classes;
+
+       MonoConcurrentHashTable *image_to_methods;
+
+       guint32 previous_offset;
+};
+
+typedef struct {
+       //Where to compress the output file
+       gboolean use_zip;
+
+       //Name of the generated xml file
+       const char *output_filename;
+
+       //Filter files used by the code coverage mode
+       GPtrArray *cov_filter_files;
+} ProfilerConfig;
+
+static ProfilerConfig coverage_config;
+static struct _MonoProfiler coverage_profiler;
+
+/* This is a very basic escape function that escapes < > and &
+   Ideally we'd use g_markup_escape_string but that function isn't
+        available in Mono's eglib. This was written without looking at the
+        source of that function in glib. */
+static char *
+escape_string_for_xml (const char *string)
+{
+       GString *string_builder = g_string_new (NULL);
+       const char *start, *p;
+
+       start = p = string;
+       while (*p) {
+               while (*p && *p != '&' && *p != '<' && *p != '>')
+                       p++;
+
+               g_string_append_len (string_builder, start, p - start);
+
+               if (*p == '\0')
+                       break;
+
+               switch (*p) {
+               case '<':
+                       g_string_append (string_builder, "&lt;");
+                       break;
+
+               case '>':
+                       g_string_append (string_builder, "&gt;");
+                       break;
+
+               case '&':
+                       g_string_append (string_builder, "&amp;");
+                       break;
+
+               default:
+                       break;
+               }
+
+               p++;
+               start = p;
+       }
+
+       return g_string_free (string_builder, FALSE);
+}
+
+typedef struct {
+       MonoLockFreeQueueNode node;
+       MonoMethod *method;
+} MethodNode;
+
+typedef struct {
+       int offset;
+       int counter;
+       char *filename;
+       int line;
+       int column;
+} CoverageEntry;
+
+static void
+free_coverage_entry (gpointer data, gpointer userdata)
+{
+       CoverageEntry *entry = (CoverageEntry *)data;
+       g_free (entry->filename);
+       g_free (entry);
+}
+
+static void
+obtain_coverage_for_method (MonoProfiler *prof, const MonoProfilerCoverageData *entry)
+{
+       g_assert (prof == &coverage_profiler);
+
+       int offset = entry->il_offset - coverage_profiler.previous_offset;
+       CoverageEntry *e = g_new (CoverageEntry, 1);
+
+       coverage_profiler.previous_offset = entry->il_offset;
+
+       e->offset = offset;
+       e->counter = entry->counter;
+       e->filename = g_strdup(entry->file_name ? entry->file_name : "");
+       e->line = entry->line;
+       e->column = entry->column;
+
+       g_ptr_array_add (coverage_profiler.data, e);
+}
+
+static char *
+parse_generic_type_names(char *name)
+{
+       char *new_name, *ret;
+       int within_generic_declaration = 0, generic_members = 1;
+
+       if (name == NULL || *name == '\0')
+               return g_strdup ("");
+
+       if (!(ret = new_name = (char *) g_calloc (strlen (name) * 4 + 1, sizeof (char))))
+               return NULL;
+
+       do {
+               switch (*name) {
+                       case '<':
+                               within_generic_declaration = 1;
+                               break;
+
+                       case '>':
+                               within_generic_declaration = 0;
+
+                               if (*(name - 1) != '<') {
+                                       *new_name++ = '`';
+                                       *new_name++ = '0' + generic_members;
+                               } else {
+                                       memcpy (new_name, "&lt;&gt;", 8);
+                                       new_name += 8;
+                               }
+
+                               generic_members = 0;
+                               break;
+
+                       case ',':
+                               generic_members++;
+                               break;
+
+                       default:
+                               if (!within_generic_declaration)
+                                       *new_name++ = *name;
+
+                               break;
+               }
+       } while (*name++);
+
+       return ret;
+}
+
+static void
+dump_method (gpointer key, gpointer value, gpointer userdata)
+{
+       MonoMethod *method = (MonoMethod *)value;
+       MonoClass *klass;
+       MonoImage *image;
+       char *class_name, *escaped_image_name, *escaped_class_name, *escaped_method_name, *escaped_method_signature, *escaped_method_filename;
+       const char *image_name, *method_name, *method_signature, *method_filename;
+       guint i;
+
+       coverage_profiler.previous_offset = 0;
+       coverage_profiler.data = g_ptr_array_new ();
+
+       mono_profiler_get_coverage_data (coverage_profiler.handle, method, obtain_coverage_for_method);
+
+       klass = mono_method_get_class (method);
+       image = mono_class_get_image (klass);
+       image_name = mono_image_get_name (image);
+
+       method_signature = mono_signature_get_desc (mono_method_signature (method), TRUE);
+       class_name = parse_generic_type_names (mono_type_get_name (mono_class_get_type (klass)));
+       method_name = mono_method_get_name (method);
+
+       if (coverage_profiler.data->len != 0) {
+               CoverageEntry *entry = (CoverageEntry *)coverage_profiler.data->pdata[0];
+               method_filename = entry->filename ? entry->filename : "";
+       } else
+               method_filename = "";
+
+       image_name = image_name ? image_name : "";
+       method_signature = method_signature ? method_signature : "";
+       method_name = method_name ? method_name : "";
+
+       escaped_image_name = escape_string_for_xml (image_name);
+       escaped_class_name = escape_string_for_xml (class_name);
+       escaped_method_name = escape_string_for_xml (method_name);
+       escaped_method_signature = escape_string_for_xml (method_signature);
+       escaped_method_filename = escape_string_for_xml (method_filename);
+
+       fprintf (coverage_profiler.file, "\t<method assembly=\"%s\" class=\"%s\" name=\"%s (%s)\" filename=\"%s\" token=\"%d\">\n",
+               escaped_image_name, escaped_class_name, escaped_method_name, escaped_method_signature, escaped_method_filename, mono_method_get_token (method));
+
+       g_free (escaped_image_name);
+       g_free (escaped_class_name);
+       g_free (escaped_method_name);
+       g_free (escaped_method_signature);
+       g_free (escaped_method_filename);
+
+       for (i = 0; i < coverage_profiler.data->len; i++) {
+               CoverageEntry *entry = (CoverageEntry *)coverage_profiler.data->pdata[i];
+
+               fprintf (coverage_profiler.file, "\t\t<statement offset=\"%d\" counter=\"%d\" line=\"%d\" column=\"%d\"/>\n",
+                       entry->offset, entry->counter, entry->line, entry->column);
+       }
+
+       fprintf (coverage_profiler.file, "\t</method>\n");
+
+       g_free (class_name);
+
+       g_ptr_array_foreach (coverage_profiler.data, free_coverage_entry, NULL);
+       g_ptr_array_free (coverage_profiler.data, TRUE);
+}
+
+/* This empties the queue */
+static guint
+count_queue (MonoLockFreeQueue *queue)
+{
+       MonoLockFreeQueueNode *node;
+       guint count = 0;
+
+       while ((node = mono_lock_free_queue_dequeue (queue))) {
+               count++;
+               mono_thread_hazardous_try_free (node, g_free);
+       }
+
+       return count;
+}
+
+static void
+dump_classes_for_image (gpointer key, gpointer value, gpointer userdata)
+{
+       MonoClass *klass = (MonoClass *)key;
+       MonoLockFreeQueue *class_methods = (MonoLockFreeQueue *)value;
+       MonoImage *image;
+       char *class_name, *escaped_class_name;
+       const char *image_name;
+       int number_of_methods, partially_covered;
+       guint fully_covered;
+
+       image = mono_class_get_image (klass);
+       image_name = mono_image_get_name (image);
+
+       if (!image_name || strcmp (image_name, mono_image_get_name (((MonoImage*) userdata))) != 0)
+               return;
+
+       class_name = mono_type_get_name (mono_class_get_type (klass));
+
+       number_of_methods = mono_class_num_methods (klass);
+       fully_covered = count_queue (class_methods);
+       /* We don't handle partial covered yet */
+       partially_covered = 0;
+
+       escaped_class_name = escape_string_for_xml (class_name);
+
+       fprintf (coverage_profiler.file, "\t<class name=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n",
+               escaped_class_name, number_of_methods, fully_covered, partially_covered);
+
+       g_free (escaped_class_name);
+
+       g_free (class_name);
+
+}
+
+static void
+get_coverage_for_image (MonoImage *image, int *number_of_methods, guint *fully_covered, int *partially_covered)
+{
+       MonoLockFreeQueue *image_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (coverage_profiler.image_to_methods, image);
+
+       *number_of_methods = mono_image_get_table_rows (image, MONO_TABLE_METHOD);
+       if (image_methods)
+               *fully_covered = count_queue (image_methods);
+       else
+               *fully_covered = 0;
+
+       // FIXME: We don't handle partially covered yet.
+       *partially_covered = 0;
+}
+
+static void
+dump_assembly (gpointer key, gpointer value, gpointer userdata)
+{
+       MonoAssembly *assembly = (MonoAssembly *)value;
+       MonoImage *image = mono_assembly_get_image (assembly);
+       const char *image_name, *image_guid, *image_filename;
+       char *escaped_image_name, *escaped_image_filename;
+       int number_of_methods = 0, partially_covered = 0;
+       guint fully_covered = 0;
+
+       image_name = mono_image_get_name (image);
+       image_guid = mono_image_get_guid (image);
+       image_filename = mono_image_get_filename (image);
+
+       image_name = image_name ? image_name : "";
+       image_guid = image_guid ? image_guid : "";
+       image_filename = image_filename ? image_filename : "";
+
+       get_coverage_for_image (image, &number_of_methods, &fully_covered, &partially_covered);
+
+       escaped_image_name = escape_string_for_xml (image_name);
+       escaped_image_filename = escape_string_for_xml (image_filename);
+
+       fprintf (coverage_profiler.file, "\t<assembly name=\"%s\" guid=\"%s\" filename=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n",
+               escaped_image_name, image_guid, escaped_image_filename, number_of_methods, fully_covered, partially_covered);
+
+       g_free (escaped_image_name);
+       g_free (escaped_image_filename);
+
+       mono_conc_hashtable_foreach (coverage_profiler.classes, dump_classes_for_image, image);
+}
+
+static void
+dump_coverage (void)
+{
+       fprintf (coverage_profiler.file, "<?xml version=\"1.0\"?>\n");
+       fprintf (coverage_profiler.file, "<coverage version=\"0.3\">\n");
+
+       mono_os_mutex_lock (&coverage_profiler.mutex);
+       mono_conc_hashtable_foreach (coverage_profiler.assemblies, dump_assembly, NULL);
+       mono_conc_hashtable_foreach (coverage_profiler.methods, dump_method, NULL);
+       mono_os_mutex_unlock (&coverage_profiler.mutex);
+
+       fprintf (coverage_profiler.file, "</coverage>\n");
+}
+
+static MonoLockFreeQueueNode *
+create_method_node (MonoMethod *method)
+{
+       MethodNode *node = (MethodNode *) g_malloc (sizeof (MethodNode));
+       mono_lock_free_queue_node_init ((MonoLockFreeQueueNode *) node, FALSE);
+       node->method = method;
+
+       return (MonoLockFreeQueueNode *) node;
+}
+
+static gboolean
+coverage_filter (MonoProfiler *prof, MonoMethod *method)
+{
+       MonoError error;
+       MonoClass *klass;
+       MonoImage *image;
+       MonoAssembly *assembly;
+       MonoMethodHeader *header;
+       guint32 iflags, flags, code_size;
+       char *fqn, *classname;
+       gboolean has_positive, found;
+       MonoLockFreeQueue *image_methods, *class_methods;
+       MonoLockFreeQueueNode *node;
+
+       g_assert (prof == &coverage_profiler);
+
+       flags = mono_method_get_flags (method, &iflags);
+       if ((iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
+           (flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
+               return FALSE;
+
+       // Don't need to do anything else if we're already tracking this method
+       if (mono_conc_hashtable_lookup (coverage_profiler.methods, method))
+               return TRUE;
+
+       klass = mono_method_get_class (method);
+       image = mono_class_get_image (klass);
+
+       // Don't handle coverage for the core assemblies
+       if (mono_conc_hashtable_lookup (coverage_profiler.suppressed_assemblies, (gpointer) mono_image_get_name (image)) != NULL)
+               return FALSE;
+
+       if (coverage_profiler.filters) {
+               /* Check already filtered classes first */
+               if (mono_conc_hashtable_lookup (coverage_profiler.filtered_classes, klass))
+                       return FALSE;
+
+               classname = mono_type_get_name (mono_class_get_type (klass));
+
+               fqn = g_strdup_printf ("[%s]%s", mono_image_get_name (image), classname);
+
+               // Check positive filters first
+               has_positive = FALSE;
+               found = FALSE;
+               for (guint i = 0; i < coverage_profiler.filters->len; ++i) {
+                       char *filter = (char *)g_ptr_array_index (coverage_profiler.filters, i);
+
+                       if (filter [0] == '+') {
+                               filter = &filter [1];
+
+                               if (strstr (fqn, filter) != NULL)
+                                       found = TRUE;
+
+                               has_positive = TRUE;
+                       }
+               }
+
+               if (has_positive && !found) {
+                       mono_os_mutex_lock (&coverage_profiler.mutex);
+                       mono_conc_hashtable_insert (coverage_profiler.filtered_classes, klass, klass);
+                       mono_os_mutex_unlock (&coverage_profiler.mutex);
+                       g_free (fqn);
+                       g_free (classname);
+
+                       return FALSE;
+               }
+
+               for (guint i = 0; i < coverage_profiler.filters->len; ++i) {
+                       // FIXME: Is substring search sufficient?
+                       char *filter = (char *)g_ptr_array_index (coverage_profiler.filters, i);
+                       if (filter [0] == '+')
+                               continue;
+
+                       // Skip '-'
+                       filter = &filter [1];
+
+                       if (strstr (fqn, filter) != NULL) {
+                               mono_os_mutex_lock (&coverage_profiler.mutex);
+                               mono_conc_hashtable_insert (coverage_profiler.filtered_classes, klass, klass);
+                               mono_os_mutex_unlock (&coverage_profiler.mutex);
+                               g_free (fqn);
+                               g_free (classname);
+
+                               return FALSE;
+                       }
+               }
+
+               g_free (fqn);
+               g_free (classname);
+       }
+
+       header = mono_method_get_header_checked (method, &error);
+       mono_error_cleanup (&error);
+
+       mono_method_header_get_code (header, &code_size, NULL);
+
+       assembly = mono_image_get_assembly (image);
+
+       // Need to keep the assemblies around for as long as they are kept in the hashtable
+       // Nunit, for example, has a habit of unloading them before the coverage statistics are
+       // generated causing a crash. See https://bugzilla.xamarin.com/show_bug.cgi?id=39325
+       mono_assembly_addref (assembly);
+
+       mono_os_mutex_lock (&coverage_profiler.mutex);
+       mono_conc_hashtable_insert (coverage_profiler.methods, method, method);
+       mono_conc_hashtable_insert (coverage_profiler.assemblies, assembly, assembly);
+       mono_os_mutex_unlock (&coverage_profiler.mutex);
+
+       image_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (coverage_profiler.image_to_methods, image);
+
+       if (image_methods == NULL) {
+               image_methods = (MonoLockFreeQueue *) g_malloc (sizeof (MonoLockFreeQueue));
+               mono_lock_free_queue_init (image_methods);
+               mono_os_mutex_lock (&coverage_profiler.mutex);
+               mono_conc_hashtable_insert (coverage_profiler.image_to_methods, image, image_methods);
+               mono_os_mutex_unlock (&coverage_profiler.mutex);
+       }
+
+       node = create_method_node (method);
+       mono_lock_free_queue_enqueue (image_methods, node);
+
+       class_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (coverage_profiler.classes, klass);
+
+       if (class_methods == NULL) {
+               class_methods = (MonoLockFreeQueue *) g_malloc (sizeof (MonoLockFreeQueue));
+               mono_lock_free_queue_init (class_methods);
+               mono_os_mutex_lock (&coverage_profiler.mutex);
+               mono_conc_hashtable_insert (coverage_profiler.classes, klass, class_methods);
+               mono_os_mutex_unlock (&coverage_profiler.mutex);
+       }
+
+       node = create_method_node (method);
+       mono_lock_free_queue_enqueue (class_methods, node);
+
+       return TRUE;
+}
+
+#define LINE_BUFFER_SIZE 4096
+/* Max file limit of 128KB */
+#define MAX_FILE_SIZE 128 * 1024
+static char *
+get_file_content (const gchar *filename)
+{
+       char *buffer;
+       ssize_t bytes_read;
+       long filesize;
+       int res, offset = 0;
+       FILE *stream;
+
+       stream = fopen (filename, "r");
+       if (stream == NULL)
+               return NULL;
+
+       res = fseek (stream, 0, SEEK_END);
+       if (res < 0) {
+               fclose (stream);
+               return NULL;
+       }
+
+       filesize = ftell (stream);
+       if (filesize < 0) {
+               fclose (stream);
+               return NULL;
+       }
+
+       res = fseek (stream, 0, SEEK_SET);
+       if (res < 0) {
+               fclose (stream);
+               return NULL;
+       }
+
+       if (filesize > MAX_FILE_SIZE) {
+               fclose (stream);
+               return NULL;
+       }
+
+       buffer = (char *) g_malloc ((filesize + 1) * sizeof (char));
+       while ((bytes_read = fread (buffer + offset, 1, LINE_BUFFER_SIZE, stream)) > 0)
+               offset += bytes_read;
+
+       /* NULL terminate our buffer */
+       buffer[filesize] = '\0';
+
+       fclose (stream);
+       return buffer;
+}
+
+static char *
+get_next_line (char *contents, char **next_start)
+{
+       char *p = contents;
+
+       if (p == NULL || *p == '\0') {
+               *next_start = NULL;
+               return NULL;
+       }
+
+       while (*p != '\n' && *p != '\0')
+               p++;
+
+       if (*p == '\n') {
+               *p = '\0';
+               *next_start = p + 1;
+       } else
+               *next_start = NULL;
+
+       return contents;
+}
+
+static void
+init_suppressed_assemblies (void)
+{
+       char *content;
+       char *line;
+
+       coverage_profiler.suppressed_assemblies = mono_conc_hashtable_new (g_str_hash, g_str_equal);
+
+       /* Don't need to free content as it is referred to by the lines stored in @filters */
+       content = get_file_content (SUPPRESSION_DIR "/mono-profiler-coverage.suppression");
+       if (content == NULL)
+               content = get_file_content (SUPPRESSION_DIR "/mono-profiler-log.suppression");
+       if (content == NULL)
+               return;
+
+       while ((line = get_next_line (content, &content))) {
+               line = g_strchomp (g_strchug (line));
+               /* No locking needed as we're doing initialization */
+               mono_conc_hashtable_insert (coverage_profiler.suppressed_assemblies, line, line);
+       }
+}
+
+static void
+parse_cov_filter_file (GPtrArray *filters, const char *file)
+{
+       char *content;
+       char *line;
+
+       /* Don't need to free content as it is referred to by the lines stored in @filters */
+       content = get_file_content (file);
+       if (content == NULL) {
+               mono_profiler_printf_err ("Could not open coverage filter file '%s'.", file);
+               return;
+       }
+
+       while ((line = get_next_line (content, &content)))
+               g_ptr_array_add (filters, g_strchug (g_strchomp (line)));
+}
+
+static void
+unref_coverage_assemblies (gpointer key, gpointer value, gpointer userdata)
+{
+       MonoAssembly *assembly = (MonoAssembly *)value;
+       mono_assembly_close (assembly);
+}
+
+static void
+log_shutdown (MonoProfiler *prof)
+{
+       g_assert (prof == &coverage_profiler);
+
+       dump_coverage ();
+
+       mono_os_mutex_lock (&coverage_profiler.mutex);
+       mono_conc_hashtable_foreach (coverage_profiler.assemblies, unref_coverage_assemblies, NULL);
+       mono_os_mutex_unlock (&coverage_profiler.mutex);
+
+       mono_conc_hashtable_destroy (coverage_profiler.methods);
+       mono_conc_hashtable_destroy (coverage_profiler.assemblies);
+       mono_conc_hashtable_destroy (coverage_profiler.classes);
+       mono_conc_hashtable_destroy (coverage_profiler.filtered_classes);
+
+       mono_conc_hashtable_destroy (coverage_profiler.image_to_methods);
+       mono_conc_hashtable_destroy (coverage_profiler.suppressed_assemblies);
+       mono_os_mutex_destroy (&coverage_profiler.mutex);
+
+       if (*coverage_config.output_filename == '|') {
+               pclose (coverage_profiler.file);
+       } else if (*coverage_config.output_filename == '#') {
+               // do nothing
+       } else {
+               fclose (coverage_profiler.file);
+       }
+
+       g_free (coverage_profiler.args);
+}
+
+static void
+runtime_initialized (MonoProfiler *profiler)
+{
+       mono_counters_register ("Event: Coverage methods", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &coverage_methods_ctr);
+       mono_counters_register ("Event: Coverage statements", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &coverage_statements_ctr);
+       mono_counters_register ("Event: Coverage classes", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &coverage_classes_ctr);
+       mono_counters_register ("Event: Coverage assemblies", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &coverage_assemblies_ctr);
+}
+
+static void usage (void);
+
+static gboolean
+match_option (const char *arg, const char *opt_name, const char **rval)
+{
+       if (rval) {
+               const char *end = strchr (arg, '=');
+
+               *rval = NULL;
+               if (!end)
+                       return !strcmp (arg, opt_name);
+
+               if (strncmp (arg, opt_name, strlen (opt_name)) || (end - arg) > strlen (opt_name) + 1)
+                       return FALSE;
+               *rval = end + 1;
+               return TRUE;
+       } else {
+               //FIXME how should we handle passing a value to an arg that doesn't expect it?
+               return !strcmp (arg, opt_name);
+       }
+}
+
+static void
+parse_arg (const char *arg)
+{
+       const char *val;
+
+       if (match_option (arg, "help", NULL)) {
+               usage ();
+       // } else if (match_option (arg, "zip", NULL)) {
+       //      coverage_config.use_zip = TRUE;
+       } else if (match_option (arg, "output", &val)) {
+               coverage_config.output_filename = g_strdup (val);
+       // } else if (match_option (arg, "covfilter", &val)) {
+       //      g_error ("not supported");
+       } else if (match_option (arg, "covfilter-file", &val)) {
+               if (coverage_config.cov_filter_files == NULL)
+                       coverage_config.cov_filter_files = g_ptr_array_new ();
+               g_ptr_array_add (coverage_config.cov_filter_files, g_strdup (val));
+       } else {
+               mono_profiler_printf_err ("Could not parse argument: %s", arg);
+       }
+}
+
+static void
+parse_args (const char *desc)
+{
+       const char *p;
+       gboolean in_quotes = FALSE;
+       char quote_char = '\0';
+       char *buffer = malloc (strlen (desc));
+       int buffer_pos = 0;
+
+       for (p = desc; *p; p++){
+               switch (*p){
+               case ',':
+                       if (!in_quotes) {
+                               if (buffer_pos != 0){
+                                       buffer [buffer_pos] = 0;
+                                       parse_arg (buffer);
+                                       buffer_pos = 0;
+                               }
+                       } else {
+                               buffer [buffer_pos++] = *p;
+                       }
+                       break;
+
+               case '\\':
+                       if (p [1]) {
+                               buffer [buffer_pos++] = p[1];
+                               p++;
+                       }
+                       break;
+               case '\'':
+               case '"':
+                       if (in_quotes) {
+                               if (quote_char == *p)
+                                       in_quotes = FALSE;
+                               else
+                                       buffer [buffer_pos++] = *p;
+                       } else {
+                               in_quotes = TRUE;
+                               quote_char = *p;
+                       }
+                       break;
+               default:
+                       buffer [buffer_pos++] = *p;
+                       break;
+               }
+       }
+
+       if (buffer_pos != 0) {
+               buffer [buffer_pos] = 0;
+               parse_arg (buffer);
+       }
+
+       g_free (buffer);
+}
+
+static void
+usage (void)
+{
+       mono_profiler_printf ("Mono coverage profiler");
+       mono_profiler_printf ("Usage: mono --profile=coverage[:OPTION1[,OPTION2...]] program.exe\n");
+       mono_profiler_printf ("Options:");
+       mono_profiler_printf ("\thelp                 show this usage info");
+
+       // mono_profiler_printf ("\tcovfilter=ASSEMBLY   add ASSEMBLY to the code coverage filters");
+       // mono_profiler_printf ("\t                     prefix a + to include the assembly or a - to exclude it");
+       // mono_profiler_printf ("\t                     e.g. covfilter=-mscorlib");
+       mono_profiler_printf ("\tcovfilter-file=FILE  use FILE to generate the list of assemblies to be filtered");
+       mono_profiler_printf ("\toutput=FILENAME      write the data to file FILENAME (the file is always overwritten)");
+       mono_profiler_printf ("\toutput=+FILENAME     write the data to file FILENAME.pid (the file is always overwritten)");
+       mono_profiler_printf ("\toutput=|PROGRAM      write the data to the stdin of PROGRAM");
+       mono_profiler_printf ("\toutput=|PROGRAM      write the data to the stdin of PROGRAM");
+       // mono_profiler_printf ("\tzip                  compress the output data");
+}
+
+MONO_API void
+mono_profiler_init_coverage (const char *desc);
+
+void
+mono_profiler_init_coverage (const char *desc)
+{
+       GPtrArray *filters = NULL;
+
+       parse_args (desc [strlen("coverage")] == ':' ? desc + strlen ("coverage") + 1 : "");
+
+       if (coverage_config.cov_filter_files) {
+               filters = g_ptr_array_new ();
+               int i;
+               for (i = 0; i < coverage_config.cov_filter_files->len; ++i) {
+                       const char *name = coverage_config.cov_filter_files->pdata [i];
+                       parse_cov_filter_file (filters, name);
+               }
+       }
+
+       coverage_profiler.args = g_strdup (desc);
+
+       //If coverage_config.output_filename begin with +, append the pid at the end
+       if (!coverage_config.output_filename)
+               coverage_config.output_filename = "coverage.xml";
+       else if (*coverage_config.output_filename == '+')
+               coverage_config.output_filename = g_strdup_printf ("%s.%d", coverage_config.output_filename + 1, getpid ());
+
+       if (*coverage_config.output_filename == '|')
+               coverage_profiler.file = popen (coverage_config.output_filename + 1, "w");
+       else if (*coverage_config.output_filename == '#')
+               coverage_profiler.file = fdopen (strtol (coverage_config.output_filename + 1, NULL, 10), "a");
+       else
+               coverage_profiler.file = fopen (coverage_config.output_filename, "w");
+
+       if (!coverage_profiler.file) {
+               mono_profiler_printf_err ("Could not create coverage profiler output file '%s'.", coverage_config.output_filename);
+               exit (1);
+       }
+
+       mono_os_mutex_init (&coverage_profiler.mutex);
+       coverage_profiler.methods = mono_conc_hashtable_new (NULL, NULL);
+       coverage_profiler.assemblies = mono_conc_hashtable_new (NULL, NULL);
+       coverage_profiler.classes = mono_conc_hashtable_new (NULL, NULL);
+       coverage_profiler.filtered_classes = mono_conc_hashtable_new (NULL, NULL);
+       coverage_profiler.image_to_methods = mono_conc_hashtable_new (NULL, NULL);
+       init_suppressed_assemblies ();
+
+       coverage_profiler.filters = filters;
+
+       MonoProfilerHandle handle = coverage_profiler.handle = mono_profiler_create (&coverage_profiler);
+
+       /*
+        * Required callbacks. These are either necessary for the profiler itself
+        * to function, or provide metadata that's needed if other events (e.g.
+        * allocations, exceptions) are dynamically enabled/disabled.
+        */
+
+       mono_profiler_set_runtime_shutdown_end_callback (handle, log_shutdown);
+       mono_profiler_set_runtime_initialized_callback (handle, runtime_initialized);
+
+       mono_profiler_enable_coverage ();
+       mono_profiler_set_coverage_filter_callback (handle, coverage_filter);
+}
index 1b9c83586944fd6d007bec4655810afa0edf91bf..3cfadd1e5138ba73a8edc41cd0d618dceb975a53 100644 (file)
@@ -81,6 +81,7 @@ parse_arg (const char *arg, ProfilerConfig *config)
        } else if (match_option (arg, "calls", NULL)) {
                config->enter_leave = TRUE;
        } else if (match_option (arg, "coverage", NULL)) {
+               g_warning ("the log profiler support for code coverage is obsolete, use the \"coverage\" profiler");
                config->collect_coverage = TRUE;
        } else if (match_option (arg, "zip", NULL)) {
                config->use_zip = TRUE;
index 729b663f91ba9cfb66dbdee3e980641d7a843803..77f06635dde8017a61e1b2cdf018e915e63a44d7 100755 (executable)
@@ -519,7 +519,8 @@ TESTS_CS_SRC=               \
        imt_big_iface_test.cs \
        bug-58782-plain-throw.cs \
        bug-58782-capture-and-throw.cs \
-       recursive-struct-arrays.cs
+       recursive-struct-arrays.cs \
+       bug-59281.cs
 
 if AMD64
 TESTS_CS_SRC += async-exc-compilation.cs finally_guard.cs finally_block_ending_in_dead_bb.cs
diff --git a/mono/tests/bug-59281.cs b/mono/tests/bug-59281.cs
new file mode 100644 (file)
index 0000000..94250c2
--- /dev/null
@@ -0,0 +1,51 @@
+using System;
+using System.Threading;
+
+class Driver
+{
+
+       static readonly Mutex[] mutexes = new Mutex[2];
+
+       public static void Main(string[] args)
+       {
+               for (int i = 0; i < mutexes.Length; i++) {
+                       mutexes [i] = new Mutex();
+               }
+
+               Thread thread1 = new Thread(() => {
+                       for (int i = 0; i < 1; i++) {
+                               int idx = -1;
+                               try {
+                                       idx = WaitHandle.WaitAny (mutexes);
+                                       Console.WriteLine($"Thread 1 iter: {i} with mutex: {idx}");
+                               } finally {
+                                       if (idx != -1)
+                                               mutexes [idx].ReleaseMutex();
+                               }
+                       }
+
+                       Console.WriteLine("Thread 1 ended");
+               });
+
+               thread1.Start();
+               thread1.Join();
+
+               Thread thread2 = new Thread(() => {
+                       for (int i = 0; i < 1000; i++) {
+                               int idx = -1;
+                               try {
+                                       idx = WaitHandle.WaitAny (mutexes);
+                                       Console.WriteLine($"Thread 2 iter: {i} with mutex: {idx}");
+                               } finally {
+                                       if (idx != -1)
+                                               mutexes [idx].ReleaseMutex();
+                               }
+                       }
+
+                       Console.WriteLine("Thread 2 ended");
+               });
+
+               thread2.Start();
+               thread2.Join();
+       }
+}
\ No newline at end of file
index c60bc5397ce28ad027f8856df3061ebfad69d4f6..96337cadaaff450ce4e4e274987a19063fbedc2b 100644 (file)
@@ -794,12 +794,8 @@ class MsbuildGenerator {
 
                var refs = new StringBuilder ();
 
-               bool is_test = response.Contains ("_test_");
-               if (is_test) {
-                       // F:\src\mono\mcs\class\lib\net_2_0\nunit.framework.dll
-                       // F:\src\mono\mcs\class\SomeProject\SomeProject_test_-net_2_0.csproj
-                       var nunitLibPath = string.Format (@"..\lib\{0}\nunit.framework.dll", profile);
-                       refs.Append (string.Format ("    <Reference Include=\"{0}\" />" + NewLine, nunitLibPath));
+               if (response.Contains ("_test")) {
+                       refs.Append ($@"    <Reference Include=""..\lib\{profile}\nunitlite.dll"" />{NewLine}");
                }
 
                //
@@ -1049,7 +1045,7 @@ class MsbuildGenerator {
 
 public class Driver {
 
-       static IEnumerable<XElement> GetProjects (bool full = false)
+       static IEnumerable<XElement> GetProjects (bool withTests = false)
        {
                XDocument doc = XDocument.Load ("order.xml");
                foreach (XElement project in doc.Root.Elements ()) {
@@ -1071,12 +1067,6 @@ public class Driver {
                        //
                        if (!(dir.StartsWith ("class") || dir.StartsWith ("mcs") || dir.StartsWith ("basic")))
                                continue;
-
-                       if (full){
-                               if (!library.Contains ("tests"))
-                                       yield return project;
-                               continue;
-                       }
 #endif
                        //
                        // Do not do 2.1, it is not working yet
@@ -1094,11 +1084,12 @@ public class Driver {
                        if (dir.Contains ("nunit20"))
                                continue;
                        
-#if true
-                       if (profile != "net_4_x" || library.Contains ("tests")) 
+                       if (library.Contains ("tests") && !withTests)
                                continue;
-#endif
-                       //Console.WriteLine ("Going to handle {0}", library);
+
+                       if (profile != "net_4_x")
+                               continue;
+
                        yield return project;
                }
        }
@@ -1112,12 +1103,12 @@ public class Driver {
 
                if (args.Length == 1 && args [0].ToLower ().Contains ("-h")) {
                        Console.WriteLine ("Usage:");
-                       Console.WriteLine ("genproj.exe [visual_studio_release] [output_full_solutions]");
+                       Console.WriteLine ("genproj.exe [visual_studio_release] [output_full_solutions] [with_tests]");
                        Console.WriteLine ("If output_full_solutions is false, only the main System*.dll");
                        Console.WriteLine (" assemblies (and dependencies) is included in the solution.");
                        Console.WriteLine ("Example:");
-                       Console.WriteLine ("genproj.exe 2012 false");
-                       Console.WriteLine ("genproj.exe with no arguments is equivalent to 'genproj.exe 2012 true'\n\n");
+                       Console.WriteLine ("genproj.exe 2012 false false");
+                       Console.WriteLine ("genproj.exe with no arguments is equivalent to 'genproj.exe 2012 true false'\n\n");
                        Console.WriteLine ("genproj.exe deps");
                        Console.WriteLine ("Generates a Makefile dependency file from the projects input");
                        Environment.Exit (0);
@@ -1125,6 +1116,7 @@ public class Driver {
 
                var slnVersion = (args.Length > 0) ? args [0] : "2012";
                bool fullSolutions = (args.Length > 1) ? bool.Parse (args [1]) : true;
+               bool withTests = (args.Length > 2) ? bool.Parse (args [2]) : false;
 
                // To generate makefile depenedencies
                var makefileDeps =  (args.Length > 0 && args [0] == "deps");
@@ -1134,11 +1126,11 @@ public class Driver {
                var projects = new Dictionary<string,MsbuildGenerator> ();
 
                var duplicates = new List<string> ();
-               foreach (var project in GetProjects (makefileDeps)) {
+               foreach (var project in GetProjects (withTests)) {
                        var library_output = project.Element ("library_output").Value;
                        projects [library_output] = new MsbuildGenerator (project);
                }
-               foreach (var project in GetProjects (makefileDeps)){
+               foreach (var project in GetProjects (withTests)){
                        var library_output = project.Element ("library_output").Value;
                        //Console.WriteLine ("=== {0} ===", library_output);
                        var gen = projects [library_output];