Merge pull request #1857 from slluis/fix-assembly-resolver
[mono.git] / mono / profiler / proflog.c
index cb3e70aa21994511c20ec7de9c66d8e2ea4b6bb9..1d3d6651ed7fd4706745b682d2914d0edb1798cb 100644 (file)
@@ -9,15 +9,22 @@
  */
 
 #include <config.h>
+#include "../mini/jit.h"
 #include <mono/metadata/profiler.h>
 #include <mono/metadata/threads.h>
 #include <mono/metadata/mono-gc.h>
 #include <mono/metadata/debug-helpers.h>
 #include <mono/metadata/mono-perfcounters.h>
+#include <mono/metadata/appdomain.h>
+#include <mono/metadata/assembly.h>
+#include <mono/metadata/tokentype.h>
+#include <mono/metadata/tabledefs.h>
 #include <mono/utils/atomic.h>
 #include <mono/utils/mono-membar.h>
 #include <mono/utils/mono-counters.h>
 #include <mono/utils/mono-mutex.h>
+#include <mono/utils/mono-conc-hashtable.h>
+#include <mono/utils/lock-free-queue.h>
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
 #endif
 
 #if defined(__linux__)
+
 #include <unistd.h>
 #include <sys/syscall.h>
 #include "perf_event.h"
+
+#ifdef ENABLE_PERF_EVENTS
 #define USE_PERF_EVENTS 1
+
 static int read_perf_mmap (MonoProfiler* prof, int cpu);
 #endif
 
+#endif
+
 #define BUFFER_SIZE (4096 * 16)
 static int nocalls = 0;
 static int notraces = 0;
@@ -83,7 +96,8 @@ static int use_zip = 0;
 static int do_report = 0;
 static int do_heap_shot = 0;
 static int max_call_depth = 100;
-static int runtime_inited = 0;
+static volatile int runtime_inited = 0;
+static int need_helper_thread = 0;
 static int command_port = 0;
 static int heapshot_requested = 0;
 static int sample_type = 0;
@@ -92,6 +106,8 @@ static int do_mono_sample = 0;
 static int in_shutdown = 0;
 static int do_debug = 0;
 static int do_counters = 0;
+static int do_coverage = 0;
+static gboolean debug_coverage = FALSE;
 static MonoProfileSamplingMode sampling_mode = MONO_PROFILER_STAT_MODE_PROCESS;
 
 /* For linux compile with:
@@ -338,7 +354,43 @@ typedef struct _LogBuffer LogBuffer;
  *             else:
  *                     [value: uleb128/sleb128/double] counter value, can be sleb128, uleb128 or double (determined by using type)
  *
+ * type coverage format
+ * type: TYPE_COVERAGE
+ * exinfo: one of TYPE_COVERAGE_METHOD, TYPE_COVERAGE_STATEMENT, TYPE_COVERAGE_ASSEMBLY, TYPE_COVERAGE_CLASS
+ * if exinfo == TYPE_COVERAGE_METHOD
+ *  [assembly: string] name of assembly
+ *  [class: string] name of the class
+ *  [name: string] name of the method
+ *  [signature: string] the signature of the method
+ *  [filename: string] the file path of the file that contains this method
+ *  [token: uleb128] the method token
+ *  [method_id: uleb128] an ID for this data to associate with the buffers of TYPE_COVERAGE_STATEMENTS
+ *  [len: uleb128] the number of TYPE_COVERAGE_BUFFERS associated with this method
+ * if exinfo == TYPE_COVERAGE_STATEMENTS
+ *  [method_id: uleb128] an the TYPE_COVERAGE_METHOD buffer to associate this with
+ *  [offset: uleb128] the il offset relative to the previous offset
+ *  [counter: uleb128] the counter for this instruction
+ *  [line: uleb128] the line of filename containing this instruction
+ *  [column: uleb128] the column containing this instruction
+ * if exinfo == TYPE_COVERAGE_ASSEMBLY
+ *  [name: string] assembly name
+ *  [guid: string] assembly GUID
+ *  [filename: string] assembly filename
+ *  [number_of_methods: uleb128] the number of methods in this assembly
+ *  [fully_covered: uleb128] the number of fully covered methods
+ *  [partially_covered: uleb128] the number of partially covered methods
+ *    currently partially_covered will always be 0, and fully_covered is the
+ *    number of methods that are fully and partially covered.
+ * if exinfo == TYPE_COVERAGE_CLASS
+ *  [name: string] assembly name
+ *  [class: string] class name
+ *  [number_of_methods: uleb128] the number of methods in this class
+ *  [fully_covered: uleb128] the number of fully covered methods
+ *  [partially_covered: uleb128] the number of partially covered methods
+ *    currently partially_covered will always be 0, and fully_covered is the
+ *    number of methods that are fully and partially covered.
  */
+
 struct _LogBuffer {
        LogBuffer *next;
        uint64_t time_base;
@@ -382,7 +434,6 @@ struct _BinaryObject {
 };
 
 struct _MonoProfiler {
-       LogBuffer *buffers;
        StatBuffer *stat_buffers;
        FILE* file;
 #if defined (HAVE_SYS_ZLIB)
@@ -396,28 +447,54 @@ struct _MonoProfiler {
        int pipes [2];
 #ifndef HOST_WIN32
        pthread_t helper_thread;
+       pthread_t writer_thread;
 #endif
+       volatile gint32 run_writer_thread;
+       MonoLockFreeQueue writer_queue;
+       MonoConcurrentHashTable *method_table;
+       mono_mutex_t method_table_mutex;
        BinaryObject *binary_objects;
+       GPtrArray *coverage_filters;
+};
+
+typedef struct _WriterQueueEntry WriterQueueEntry;
+struct _WriterQueueEntry {
+       MonoLockFreeQueueNode node;
+       GPtrArray *methods;
+       LogBuffer *buffer;
 };
 
+typedef struct _MethodInfo MethodInfo;
+struct _MethodInfo {
+       MonoMethod *method;
+       MonoJitInfo *ji;
+};
+
+#ifdef TLS_INIT
+#undef TLS_INIT
+#endif
+
 #ifdef HOST_WIN32
-#define TLS_SET(x,y) TlsSetValue(x, y)
-#define TLS_GET(x) ((LogBuffer *) TlsGetValue(x))
-#define TLS_INIT(x) x = TlsAlloc ()
+#define TLS_SET(x,y) (TlsSetValue (x, y))
+#define TLS_GET(t,x) ((t *) TlsGetValue (x))
+#define TLS_INIT(x) (x = TlsAlloc ())
 static int tlsbuffer;
+static int tlsmethodlist;
 #elif HAVE_KW_THREAD
-#define TLS_SET(x,y) x = y
-#define TLS_GET(x) x
+#define TLS_SET(x,y) (x = y)
+#define TLS_GET(t,x) (x)
 #define TLS_INIT(x)
 static __thread LogBuffer* tlsbuffer = NULL;
+static __thread GPtrArray* tlsmethodlist = NULL;
 #else
-#define TLS_SET(x,y) pthread_setspecific(x, y)
-#define TLS_GET(x) ((LogBuffer *) pthread_getspecific(x))
-#define TLS_INIT(x) pthread_key_create(&x, NULL)
+#define TLS_SET(x,y) (pthread_setspecific (x, y))
+#define TLS_GET(t,x) ((t *) pthread_getspecific (x))
+#define TLS_INIT(x) (pthread_key_create (&x, NULL))
 static pthread_key_t tlsbuffer;
+static pthread_key_t tlsmethodlist;
 #endif
 
-static void safe_dump (MonoProfiler *profiler, LogBuffer *logbuffer);
+static void safe_send (MonoProfiler *profiler, LogBuffer *logbuffer);
 
 static char*
 pstrdup (const char *s)
@@ -453,28 +530,48 @@ create_buffer (void)
 static void
 init_thread (void)
 {
-       LogBuffer *logbuffer;
-       if (TLS_GET (tlsbuffer))
-               return;
-       logbuffer = create_buffer ();
-       TLS_SET (tlsbuffer, logbuffer);
-       logbuffer->thread_id = thread_id ();
+       if (!TLS_GET (LogBuffer, tlsbuffer)) {
+               LogBuffer *logbuffer = create_buffer ();
+               TLS_SET (tlsbuffer, logbuffer);
+               logbuffer->thread_id = thread_id ();
+       }
+       if (!TLS_GET (GPtrArray, tlsmethodlist)) {
+               GPtrArray *methodlist = g_ptr_array_new ();
+               TLS_SET (tlsmethodlist, methodlist);
+       }
+
        //printf ("thread %p at time %llu\n", (void*)logbuffer->thread_id, logbuffer->time_base);
 }
 
-static LogBuffer*
-ensure_logbuf (int bytes)
+static LogBuffer *
+ensure_logbuf_inner (LogBuffer *old, int bytes)
 {
-       LogBuffer *old = TLS_GET (tlsbuffer);
        if (old && old->data + bytes + 100 < old->data_end)
                return old;
-       TLS_SET (tlsbuffer, NULL);
-       init_thread ();
-       TLS_GET (tlsbuffer)->next = old;
+
+       LogBuffer *new = create_buffer ();
+       new->thread_id = thread_id ();
+       new->next = old;
+
        if (old)
-               TLS_GET (tlsbuffer)->call_depth = old->call_depth;
-       //printf ("new logbuffer\n");
-       return TLS_GET (tlsbuffer);
+               new->call_depth = old->call_depth;
+
+       return new;
+}
+
+static LogBuffer*
+ensure_logbuf (int bytes)
+{
+       LogBuffer *old = TLS_GET (LogBuffer, tlsbuffer);
+       LogBuffer *new = ensure_logbuf_inner (old, bytes);
+
+       if (new == old)
+               return old; // Still enough space.
+
+       TLS_SET (tlsbuffer, new);
+       init_thread ();
+
+       return new;
 }
 
 static void
@@ -496,12 +593,10 @@ static void
 emit_time (LogBuffer *logbuffer, uint64_t value)
 {
        uint64_t tdiff = value - logbuffer->last_time;
-       unsigned char *p;
        if (value < logbuffer->last_time)
                printf ("time went backwards\n");
        //if (tdiff > 1000000)
        //      printf ("large time offset: %llu\n", tdiff);
-       p = logbuffer->data;
        encode_uleb128 (tdiff, logbuffer->data, &logbuffer->data);
        /*if (tdiff != decode_uleb128 (p, &p))
                printf ("incorrect encoding: %llu\n", tdiff);*/
@@ -533,7 +628,7 @@ emit_ptr (LogBuffer *logbuffer, void *ptr)
 }
 
 static void
-emit_method (LogBuffer *logbuffer, void *method)
+emit_method_inner (LogBuffer *logbuffer, void *method)
 {
        if (!logbuffer->method_base) {
                logbuffer->method_base = (intptr_t)method;
@@ -544,6 +639,67 @@ emit_method (LogBuffer *logbuffer, void *method)
        assert (logbuffer->data <= logbuffer->data_end);
 }
 
+typedef struct {
+       MonoMethod *method;
+       MonoJitInfo *found;
+} MethodSearch;
+
+static void
+find_method (MonoDomain *domain, void *user_data)
+{
+       MethodSearch *search = user_data;
+
+       if (search->found)
+               return;
+
+       MonoJitInfo *ji = mono_get_jit_info_from_method (domain, search->method);
+
+       // It could be AOT'd, so we need to get it from the AOT runtime's cache.
+       if (!ji) {
+               void *ip = mono_aot_get_method (domain, search->method);
+
+               // Avoid a slow path in mono_jit_info_table_find ().
+               if (ip)
+                       ji = mono_jit_info_table_find (domain, ip);
+       }
+
+       if (ji)
+               search->found = ji;
+}
+
+static void
+register_method_local (MonoProfiler *prof, MonoDomain *domain, MonoMethod *method, MonoJitInfo *ji)
+{
+       if (!domain)
+               g_assert (ji);
+
+       if (!mono_conc_hashtable_lookup (prof->method_table, method)) {
+               if (!ji) {
+                       MethodSearch search = { method, NULL };
+
+                       mono_domain_foreach (find_method, &search);
+
+                       ji = search.found;
+               }
+
+               g_assert (ji);
+
+               MethodInfo *info = malloc (sizeof (MethodInfo));
+
+               info->method = method;
+               info->ji = ji;
+
+               g_ptr_array_add (TLS_GET (GPtrArray, tlsmethodlist), info);
+       }
+}
+
+static void
+emit_method (MonoProfiler *prof, LogBuffer *logbuffer, MonoDomain *domain, MonoMethod *method)
+{
+       register_method_local (prof, domain, method, NULL);
+       emit_method_inner (logbuffer, method);
+}
+
 static void
 emit_obj (LogBuffer *logbuffer, void *ptr)
 {
@@ -642,6 +798,16 @@ dump_header (MonoProfiler *profiler)
 #endif
 }
 
+static void
+send_buffer (MonoProfiler *prof, GPtrArray *methods, LogBuffer *buffer)
+{
+       WriterQueueEntry *entry = calloc (1, sizeof (WriterQueueEntry));
+       mono_lock_free_queue_node_init (&entry->node, FALSE);
+       entry->methods = methods;
+       entry->buffer = buffer;
+       mono_lock_free_queue_enqueue (&prof->writer_queue, &entry->node);
+}
+
 static void
 dump_buffer (MonoProfiler *profiler, LogBuffer *buf)
 {
@@ -681,31 +847,32 @@ process_requests (MonoProfiler *profiler)
 static void counters_init (MonoProfiler *profiler);
 static void counters_sample (MonoProfiler *profiler, uint64_t timestamp);
 
-static void
-runtime_initialized (MonoProfiler *profiler)
-{
-       runtime_inited = 1;
-#ifndef DISABLE_HELPER_THREAD
-       counters_init (profiler);
-       counters_sample (profiler, 0);
-#endif
-       /* ensure the main thread data and startup are available soon */
-       safe_dump (profiler, ensure_logbuf (0));
-}
-
 /*
  * Can be called only at safe callback locations.
  */
 static void
-safe_dump (MonoProfiler *profiler, LogBuffer *logbuffer)
-{
+safe_send (MonoProfiler *profiler, LogBuffer *logbuffer)
+{
+       /* We need the runtime initialized so that we have threads and hazard
+        * pointers available. Otherwise, the lock free queue will not work and
+        * there won't be a thread to process the data.
+        *
+        * While the runtime isn't initialized, we just accumulate data in the
+        * thread local buffer list.
+        */
+       if (!InterlockedRead (&runtime_inited))
+               return;
+
        int cd = logbuffer->call_depth;
-       take_lock ();
-       dump_buffer (profiler, TLS_GET (tlsbuffer));
-       release_lock ();
+
+       send_buffer (profiler, TLS_GET (GPtrArray, tlsmethodlist), TLS_GET (LogBuffer, tlsbuffer));
+
        TLS_SET (tlsbuffer, NULL);
+       TLS_SET (tlsmethodlist, NULL);
+
        init_thread ();
-       TLS_GET (tlsbuffer)->call_depth = cd;
+
+       TLS_GET (LogBuffer, tlsbuffer)->call_depth = cd;
 }
 
 static int
@@ -791,7 +958,7 @@ gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) {
                heap_walk (profiler);
        EXIT_LOG (logbuffer);
        if (ev == MONO_GC_EVENT_POST_START_WORLD)
-               safe_dump (profiler, logbuffer);
+               safe_send (profiler, logbuffer);
        //printf ("gc event %d for generation %d\n", ev, generation);
 }
 
@@ -864,7 +1031,7 @@ gc_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
 {
        uint64_t now;
        uintptr_t len;
-       int do_bt = (nocalls && runtime_inited && !notraces)? TYPE_ALLOC_BT: 0;
+       int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces)? TYPE_ALLOC_BT: 0;
        FrameData data;
        LogBuffer *logbuffer;
        len = mono_object_get_size (obj);
@@ -885,7 +1052,7 @@ gc_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
                emit_bt (logbuffer, &data);
        EXIT_LOG (logbuffer);
        if (logbuffer->next)
-               safe_dump (prof, logbuffer);
+               safe_send (prof, logbuffer);
        process_requests (prof);
        //printf ("gc alloc %s at %p\n", mono_class_get_name (klass), obj);
 }
@@ -1006,7 +1173,7 @@ image_loaded (MonoProfiler *prof, MonoImage *image, int result)
        //printf ("loaded image %p (%s)\n", image, name);
        EXIT_LOG (logbuffer);
        if (logbuffer->next)
-               safe_dump (prof, logbuffer);
+               safe_send (prof, logbuffer);
        process_requests (prof);
 }
 
@@ -1020,7 +1187,7 @@ class_loaded (MonoProfiler *prof, MonoClass *klass, int result)
        LogBuffer *logbuffer;
        if (result != MONO_PROFILE_OK)
                return;
-       if (runtime_inited)
+       if (InterlockedRead (&runtime_inited))
                name = mono_type_get_name (mono_class_get_type (klass));
        else
                name = type_name (klass);
@@ -1044,10 +1211,14 @@ class_loaded (MonoProfiler *prof, MonoClass *klass, int result)
                free (name);
        EXIT_LOG (logbuffer);
        if (logbuffer->next)
-               safe_dump (prof, logbuffer);
+               safe_send (prof, logbuffer);
        process_requests (prof);
 }
 
+#ifndef DISABLE_HELPER_THREAD
+static void process_method_enter (MonoProfiler *prof, MonoMethod *method);
+#endif /* DISABLE_HELPER_THREAD */
+
 static void
 method_enter (MonoProfiler *prof, MonoMethod *method)
 {
@@ -1059,8 +1230,13 @@ method_enter (MonoProfiler *prof, MonoMethod *method)
        ENTER_LOG (logbuffer, "enter");
        emit_byte (logbuffer, TYPE_ENTER | TYPE_METHOD);
        emit_time (logbuffer, now);
-       emit_method (logbuffer, method);
+       emit_method (prof, logbuffer, mono_domain_get (), method);
        EXIT_LOG (logbuffer);
+
+#ifndef DISABLE_HELPER_THREAD
+       process_method_enter (prof, method);
+#endif /* DISABLE_HELPER_THREAD */
+
        process_requests (prof);
 }
 
@@ -1075,10 +1251,10 @@ method_leave (MonoProfiler *prof, MonoMethod *method)
        ENTER_LOG (logbuffer, "leave");
        emit_byte (logbuffer, TYPE_LEAVE | TYPE_METHOD);
        emit_time (logbuffer, now);
-       emit_method (logbuffer, method);
+       emit_method (prof, logbuffer, mono_domain_get (), method);
        EXIT_LOG (logbuffer);
        if (logbuffer->next)
-               safe_dump (prof, logbuffer);
+               safe_send (prof, logbuffer);
        process_requests (prof);
 }
 
@@ -1096,37 +1272,18 @@ method_exc_leave (MonoProfiler *prof, MonoMethod *method)
        ENTER_LOG (logbuffer, "eleave");
        emit_byte (logbuffer, TYPE_EXC_LEAVE | TYPE_METHOD);
        emit_time (logbuffer, now);
-       emit_method (logbuffer, method);
+       emit_method (prof, logbuffer, mono_domain_get (), method);
        EXIT_LOG (logbuffer);
        process_requests (prof);
 }
 
 static void
-method_jitted (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo, int result)
+method_jitted (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *ji, int result)
 {
-       uint64_t now;
-       char *name;
-       int nlen;
-       LogBuffer *logbuffer;
        if (result != MONO_PROFILE_OK)
                return;
-       name = mono_method_full_name (method, 1);
-       nlen = strlen (name) + 1;
-       logbuffer = ensure_logbuf (32 + nlen);
-       now = current_time ();
-       ENTER_LOG (logbuffer, "jit");
-       emit_byte (logbuffer, TYPE_JIT | TYPE_METHOD);
-       emit_time (logbuffer, now);
-       emit_method (logbuffer, method);
-       emit_ptr (logbuffer, mono_jit_info_get_code_start (jinfo));
-       emit_value (logbuffer, mono_jit_info_get_code_size (jinfo));
-       memcpy (logbuffer->data, name, nlen);
-       logbuffer->data += nlen;
-       mono_free (name);
-       EXIT_LOG (logbuffer);
-       if (logbuffer->next)
-               safe_dump (prof, logbuffer);
-       process_requests (prof);
+
+       register_method_local (prof, NULL, method, ji);
 }
 
 static void
@@ -1162,7 +1319,7 @@ code_buffer_new (MonoProfiler *prof, void *buffer, int size, MonoProfilerCodeBuf
 static void
 throw_exc (MonoProfiler *prof, MonoObject *object)
 {
-       int do_bt = (nocalls && runtime_inited && !notraces)? TYPE_EXCEPTION_BT: 0;
+       int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces)? TYPE_EXCEPTION_BT: 0;
        uint64_t now;
        FrameData data;
        LogBuffer *logbuffer;
@@ -1191,14 +1348,14 @@ clause_exc (MonoProfiler *prof, MonoMethod *method, int clause_type, int clause_
        emit_time (logbuffer, now);
        emit_value (logbuffer, clause_type);
        emit_value (logbuffer, clause_num);
-       emit_method (logbuffer, method);
+       emit_method (prof, logbuffer, mono_domain_get (), method);
        EXIT_LOG (logbuffer);
 }
 
 static void
 monitor_event (MonoProfiler *profiler, MonoObject *object, MonoProfilerMonitorEvent event)
 {
-       int do_bt = (nocalls && runtime_inited && !notraces && event == MONO_PROFILER_MONITOR_CONTENTION)? TYPE_MONITOR_BT: 0;
+       int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces && event == MONO_PROFILER_MONITOR_CONTENTION)? TYPE_MONITOR_BT: 0;
        uint64_t now;
        FrameData data;
        LogBuffer *logbuffer;
@@ -1226,11 +1383,11 @@ thread_start (MonoProfiler *prof, uintptr_t tid)
 static void
 thread_end (MonoProfiler *prof, uintptr_t tid)
 {
-       take_lock ();
-       if (TLS_GET (tlsbuffer))
-               dump_buffer (prof, TLS_GET (tlsbuffer));
-       release_lock ();
+       if (TLS_GET (LogBuffer, tlsbuffer))
+               send_buffer (prof, TLS_GET (GPtrArray, tlsmethodlist), TLS_GET (LogBuffer, tlsbuffer));
+
        TLS_SET (tlsbuffer, NULL);
+       TLS_SET (tlsmethodlist, NULL);
 }
 
 static void
@@ -1676,36 +1833,14 @@ dump_unmanaged_coderefs (MonoProfiler *prof)
 }
 
 static void
-dump_sample_hits_inner (MonoProfiler *prof, StatBuffer *sbuf, int recurse, GPtrArray **array);
-
-static void
-dump_sample_hits (MonoProfiler *prof, StatBuffer *sbuf, int recurse)
-{
-       GPtrArray *array = NULL;
-       dump_sample_hits_inner (prof, sbuf, recurse, &array);
-
-       if (array) {
-               int i;
-               g_ptr_array_sort (array, g_direct_equal);
-               for (i = 0; i < array->len; ++i) {
-                       MonoJitInfo *cur = array->pdata [i];
-                       //Ignore duplicates
-                       if (i > 0 && array->pdata [i - 1] == cur)
-                               continue;
-                       method_jitted (prof, mono_jit_info_get_method (cur), cur, MONO_PROFILE_OK);
-               }
-       }
-}
-
-static void
-dump_sample_hits_inner (MonoProfiler *prof, StatBuffer *sbuf, int recurse, GPtrArray **array)
+dump_sample_hits (MonoProfiler *prof, StatBuffer *sbuf)
 {
        uintptr_t *sample;
        LogBuffer *logbuffer;
        if (!sbuf)
                return;
-       if (recurse && sbuf->next) {
-               dump_sample_hits_inner (prof, sbuf->next, 1, array);
+       if (sbuf->next) {
+               dump_sample_hits (prof, sbuf->next);
                free_buffer (sbuf->next, sbuf->next->size);
                sbuf->next = NULL;
        }
@@ -1726,12 +1861,9 @@ dump_sample_hits_inner (MonoProfiler *prof, StatBuffer *sbuf, int recurse, GPtrA
 
                        if (!method) {
                                MonoJitInfo *ji = mono_jit_info_table_find (domain, address);
-                               if (ji) {
+
+                               if (ji)
                                        managed_sample_base [i * 4 + 0] = (uintptr_t)mono_jit_info_get_method (ji);
-                                       if (!*array)
-                                               *array = g_ptr_array_new ();
-                                       g_ptr_array_add (*array, ji);
-                               }
                        }
                }
                logbuffer = ensure_logbuf (20 + count * 8);
@@ -1748,9 +1880,13 @@ dump_sample_hits_inner (MonoProfiler *prof, StatBuffer *sbuf, int recurse, GPtrA
                /* new in data version 6 */
                emit_uvalue (logbuffer, mbt_count);
                for (i = 0; i < mbt_count; ++i) {
-                       emit_method (logbuffer, (void*)sample [i * 4]); /* method */
+                       MonoMethod *method = (MonoMethod *) sample [i * 4 + 0];
+                       MonoDomain *domain = (MonoDomain *) sample [i * 4 + 1];
+                       uintptr_t native_offset = sample [i * 4 + 3];
+
+                       emit_method (prof, logbuffer, domain, method);
                        emit_svalue (logbuffer, 0); /* il offset will always be 0 from now on */
-                       emit_svalue (logbuffer, sample [i * 4 + 3]); /* native offset */
+                       emit_svalue (logbuffer, native_offset);
                }
                sample += 4 * mbt_count;
        }
@@ -1758,13 +1894,6 @@ dump_sample_hits_inner (MonoProfiler *prof, StatBuffer *sbuf, int recurse, GPtrA
 }
 
 #if USE_PERF_EVENTS
-#ifndef __NR_perf_event_open
-#ifdef __arm__
-#define __NR_perf_event_open 364
-#else
-#define __NR_perf_event_open 241
-#endif
-#endif
 
 static int
 mono_cpu_count (void)
@@ -1847,7 +1976,7 @@ perf_event_syscall (struct perf_event_attr *attr, pid_t pid, int cpu, int group_
        return syscall(/*__NR_perf_event_open*/ 298, attr, pid, cpu, group_fd, flags);
 #elif defined(__i386__)
        return syscall(/*__NR_perf_event_open*/ 336, attr, pid, cpu, group_fd, flags);
-#elif defined(__arm__)
+#elif defined(__arm__) || defined (__aarch64__)
        return syscall(/*__NR_perf_event_open*/ 364, attr, pid, cpu, group_fd, flags);
 #else
        return -1;
@@ -2137,7 +2266,7 @@ counters_emit (MonoProfiler *profiler)
        }
        EXIT_LOG (logbuffer);
 
-       safe_dump (profiler, ensure_logbuf (0));
+       safe_send (profiler, ensure_logbuf (0));
 
        mono_mutex_unlock (&counters_mutex);
 }
@@ -2255,7 +2384,7 @@ counters_sample (MonoProfiler *profiler, uint64_t timestamp)
        emit_value (logbuffer, 0);
        EXIT_LOG (logbuffer);
 
-       safe_dump (profiler, ensure_logbuf (0));
+       safe_send (profiler, ensure_logbuf (0));
 
        mono_mutex_unlock (&counters_mutex);
 }
@@ -2314,7 +2443,7 @@ perfcounters_emit (MonoProfiler *profiler)
        }
        EXIT_LOG (logbuffer);
 
-       safe_dump (profiler, ensure_logbuf (0));
+       safe_send (profiler, ensure_logbuf (0));
 }
 
 static gboolean
@@ -2396,7 +2525,7 @@ perfcounters_sample (MonoProfiler *profiler, uint64_t timestamp)
        emit_value (logbuffer, 0);
        EXIT_LOG (logbuffer);
 
-       safe_dump (profiler, ensure_logbuf (0));
+       safe_send (profiler, ensure_logbuf (0));
 
        mono_mutex_unlock (&counters_mutex);
 }
@@ -2415,18 +2544,639 @@ counters_and_perfcounters_sample (MonoProfiler *prof)
        perfcounters_sample (prof, (now - start) / 1000/ 1000);
 }
 
+#define COVERAGE_DEBUG(x) if (debug_coverage) {x}
+static MonoConcurrentHashTable *coverage_methods = NULL;
+static mono_mutex_t coverage_methods_mutex;
+static MonoConcurrentHashTable *coverage_assemblies = NULL;
+static mono_mutex_t coverage_assemblies_mutex;
+static MonoConcurrentHashTable *coverage_classes = NULL;
+static mono_mutex_t coverage_classes_mutex;
+static MonoConcurrentHashTable *filtered_classes = NULL;
+static mono_mutex_t filtered_classes_mutex;
+static MonoConcurrentHashTable *entered_methods = NULL;
+static mono_mutex_t entered_methods_mutex;
+static MonoConcurrentHashTable *image_to_methods = NULL;
+static mono_mutex_t image_to_methods_mutex;
+static MonoConcurrentHashTable *suppressed_assemblies = NULL;
+static mono_mutex_t suppressed_assemblies_mutex;
+static gboolean coverage_initialized = FALSE;
+
+static GPtrArray *coverage_data = NULL;
+static int previous_offset = 0;
+
+typedef struct _MethodNode MethodNode;
+struct _MethodNode {
+       MonoLockFreeQueueNode node;
+       MonoMethod *method;
+};
+
+typedef struct _CoverageEntry CoverageEntry;
+struct _CoverageEntry {
+       int offset;
+       int counter;
+       char *filename;
+       int line;
+       int column;
+};
+
+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 MonoProfileCoverageEntry *entry)
+{
+       int offset = entry->iloffset - previous_offset;
+       CoverageEntry *e = g_new (CoverageEntry, 1);
+
+       previous_offset = entry->iloffset;
+
+       e->offset = offset;
+       e->counter = entry->counter;
+       e->filename = g_strdup(entry->filename ? entry->filename : "");
+       e->line = entry->line;
+       e->column = entry->col;
+
+       g_ptr_array_add (coverage_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 = 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 int method_id;
+static void
+build_method_buffer (gpointer key, gpointer value, gpointer userdata)
+{
+       MonoMethod *method = (MonoMethod *)value;
+       MonoProfiler *prof = (MonoProfiler *)userdata;
+       MonoClass *klass;
+       MonoImage *image;
+       char *class_name;
+       const char *image_name, *method_name, *sig, *first_filename;
+       LogBuffer *logbuffer;
+       int size = 1;
+       guint i;
+
+       previous_offset = 0;
+       coverage_data = g_ptr_array_new ();
+
+       mono_profiler_coverage_get (prof, method, obtain_coverage_for_method);
+
+       klass = mono_method_get_class (method);
+       image = mono_class_get_image (klass);
+       image_name = mono_image_get_name (image);
+
+       sig = 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_data->len != 0) {
+               CoverageEntry *entry = coverage_data->pdata[0];
+               first_filename = entry->filename ? entry->filename : "";
+       } else
+               first_filename = "";
+
+       image_name = image_name ? image_name : "";
+       sig = sig ? sig : "";
+       method_name = method_name ? method_name : "";
+
+       size += strlen (image_name) + 1;
+       size += strlen (class_name) + 1;
+       size += strlen (method_name) + 1;
+       size += strlen (first_filename) + 1;
+       size += strlen (sig) + 1;
+
+       size += 10 + 10 + 5; /* token + method_id + n_entries*/
+
+       logbuffer = ensure_logbuf (size);
+       ENTER_LOG (logbuffer, "coverage-methods");
+
+       emit_byte (logbuffer, TYPE_COVERAGE_METHOD | TYPE_COVERAGE);
+       emit_string (logbuffer, image_name, strlen (image_name) + 1);
+       emit_string (logbuffer, class_name, strlen (class_name) + 1);
+       emit_string (logbuffer, method_name, strlen (method_name) + 1);
+       emit_string (logbuffer, sig, strlen (sig) + 1);
+       emit_string (logbuffer, first_filename, strlen (first_filename) + 1);
+
+       emit_uvalue (logbuffer, mono_method_get_token (method));
+       emit_uvalue (logbuffer, method_id);
+       emit_value (logbuffer, coverage_data->len);
+
+       EXIT_LOG (logbuffer);
+       safe_send (prof, ensure_logbuf (0));
+
+       for (i = 0; i < coverage_data->len; i++) {
+               CoverageEntry *entry = coverage_data->pdata[i];
+
+               size = 1;
+               size += 10 * 5; /* method_id, offset, count, line, column */
+
+               logbuffer = ensure_logbuf (size);
+               ENTER_LOG (logbuffer, "coverage-statement");
+
+               emit_byte (logbuffer, TYPE_COVERAGE_STATEMENT | TYPE_COVERAGE);
+               emit_uvalue (logbuffer, method_id);
+               emit_uvalue (logbuffer, entry->offset);
+               emit_uvalue (logbuffer, entry->counter);
+               emit_uvalue (logbuffer, entry->line);
+               emit_uvalue (logbuffer, entry->column);
+
+               EXIT_LOG (logbuffer);
+               safe_send (prof, ensure_logbuf (0));
+       }
+
+       method_id++;
+
+       g_free (class_name);
+
+       g_ptr_array_foreach (coverage_data, free_coverage_entry, NULL);
+       g_ptr_array_free (coverage_data, TRUE);
+       coverage_data = NULL;
+}
+
+/* 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_lock_free_queue_node_free (node);
+       }
+
+       return count;
+}
+
+static void
+build_class_buffer (gpointer key, gpointer value, gpointer userdata)
+{
+       MonoClass *klass = (MonoClass *)key;
+       MonoLockFreeQueue *class_methods = (MonoLockFreeQueue *)value;
+       MonoProfiler *prof = (MonoProfiler *)userdata;
+       MonoImage *image;
+       char *class_name;
+       const char *assembly_name;
+       int number_of_methods, partially_covered;
+       guint fully_covered;
+       LogBuffer *logbuffer;
+       int size = 1;
+
+       image = mono_class_get_image (klass);
+       assembly_name = mono_image_get_name (image);
+       class_name = mono_type_get_name (mono_class_get_type (klass));
+
+       assembly_name = assembly_name ? assembly_name : "";
+       number_of_methods = mono_class_num_methods (klass);
+       fully_covered = count_queue (class_methods);
+       /* We don't handle partial covered yet */
+       partially_covered = 0;
+
+       size += strlen (assembly_name) + 1;
+       size += strlen (class_name) + 1;
+       size += 30; /* number_of_methods, fully_covered, partially_covered */
+
+       logbuffer = ensure_logbuf (size);
+
+       ENTER_LOG (logbuffer, "coverage-class");
+       emit_byte (logbuffer, TYPE_COVERAGE_CLASS | TYPE_COVERAGE);
+       emit_string (logbuffer, assembly_name, strlen (assembly_name) + 1);
+       emit_string (logbuffer, class_name, strlen (class_name) + 1);
+       emit_uvalue (logbuffer, number_of_methods);
+       emit_uvalue (logbuffer, fully_covered);
+       emit_uvalue (logbuffer, partially_covered);
+       EXIT_LOG (logbuffer);
+
+       safe_send (prof, ensure_logbuf (0));
+
+       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 = mono_conc_hashtable_lookup (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
+build_assembly_buffer (gpointer key, gpointer value, gpointer userdata)
+{
+       MonoAssembly *assembly = (MonoAssembly *)value;
+       MonoProfiler *prof = (MonoProfiler *)userdata;
+       MonoImage *image = mono_assembly_get_image (assembly);
+       LogBuffer *logbuffer;
+       const char *name, *guid, *filename;
+       int size = 1;
+       int number_of_methods = 0, partially_covered = 0;
+       guint fully_covered = 0;
+
+       name = mono_image_get_name (image);
+       guid = mono_image_get_guid (image);
+       filename = mono_image_get_filename (image);
+
+       name = name ? name : "";
+       guid = guid ? guid : "";
+       filename = filename ? filename : "";
+
+       get_coverage_for_image (image, &number_of_methods, &fully_covered, &partially_covered);
+
+       size += strlen (name) + 1;
+       size += strlen (guid) + 1;
+       size += strlen (filename) + 1;
+       size += 30; /* number_of_methods, fully_covered, partially_covered */
+       logbuffer = ensure_logbuf (size);
+
+       ENTER_LOG (logbuffer, "coverage-assemblies");
+       emit_byte (logbuffer, TYPE_COVERAGE_ASSEMBLY | TYPE_COVERAGE);
+       emit_string (logbuffer, name, strlen (name) + 1);
+       emit_string (logbuffer, guid, strlen (guid) + 1);
+       emit_string (logbuffer, filename, strlen (filename) + 1);
+       emit_uvalue (logbuffer, number_of_methods);
+       emit_uvalue (logbuffer, fully_covered);
+       emit_uvalue (logbuffer, partially_covered);
+       EXIT_LOG (logbuffer);
+
+       safe_send (prof, ensure_logbuf (0));
+}
+
+static void
+dump_coverage (MonoProfiler *prof)
+{
+       if (!coverage_initialized)
+               return;
+
+       COVERAGE_DEBUG(fprintf (stderr, "Coverage: Started dump\n");)
+       method_id = 0;
+
+       mono_conc_hashtable_foreach (coverage_assemblies, build_assembly_buffer, prof);
+       mono_conc_hashtable_foreach (coverage_classes, build_class_buffer, prof);
+       mono_conc_hashtable_foreach (coverage_methods, build_method_buffer, prof);
+
+       COVERAGE_DEBUG(fprintf (stderr, "Coverage: Finished dump\n");)
+}
+
+static void
+process_method_enter (MonoProfiler *prof, MonoMethod *method)
+{
+       MonoClass *klass;
+       MonoImage *image;
+
+       if (!coverage_initialized)
+               return;
+
+       klass = mono_method_get_class (method);
+       image = mono_class_get_image (klass);
+
+       if (mono_conc_hashtable_lookup (suppressed_assemblies, (gpointer) mono_image_get_name (image)))
+               return;
+
+       mono_conc_hashtable_insert (entered_methods, method, method);
+}
+
+static MonoLockFreeQueueNode *
+create_method_node (MonoMethod *method)
+{
+       MethodNode *node = 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)
+{
+       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;
+
+       if (!coverage_initialized)
+               return FALSE;
+
+       COVERAGE_DEBUG(fprintf (stderr, "Coverage filter for %s\n", mono_method_get_name (method));)
+
+       flags = mono_method_get_flags (method, &iflags);
+       if ((iflags & 0x1000 /*METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL*/) ||
+           (flags & 0x2000 /*METHOD_ATTRIBUTE_PINVOKE_IMPL*/)) {
+               COVERAGE_DEBUG(fprintf (stderr, "   Internal call or pinvoke - ignoring\n");)
+               return FALSE;
+       }
+
+       // Don't need to do anything else if we're already tracking this method
+       if (mono_conc_hashtable_lookup (coverage_methods, method)) {
+               COVERAGE_DEBUG(fprintf (stderr, "   Already tracking\n");)
+               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 (suppressed_assemblies, (gpointer) mono_image_get_name (image)) != NULL)
+               return FALSE;
+
+       if (prof->coverage_filters) {
+               /* Check already filtered classes first */
+               if (mono_conc_hashtable_lookup (filtered_classes, klass)) {
+                       COVERAGE_DEBUG(fprintf (stderr, "   Already filtered\n");)
+                       return FALSE;
+               }
+
+               classname = mono_type_get_name (mono_class_get_type (klass));
+
+               fqn = g_strdup_printf ("[%s]%s", mono_image_get_name (image), classname);
+
+               COVERAGE_DEBUG(fprintf (stderr, "   Looking for %s in filter\n", fqn);)
+               // Check positive filters first
+               has_positive = FALSE;
+               found = FALSE;
+               for (guint i = 0; i < prof->coverage_filters->len; ++i) {
+                       char *filter = g_ptr_array_index (prof->coverage_filters, i);
+
+                       if (filter [0] == '+') {
+                               filter = &filter [1];
+
+                               COVERAGE_DEBUG(fprintf (stderr, "   Checking against +%s ...", filter);)
+
+                               if (strstr (fqn, filter) != NULL) {
+                                       COVERAGE_DEBUG(fprintf (stderr, "matched\n");)
+                                       found = TRUE;
+                               } else
+                                       COVERAGE_DEBUG(fprintf (stderr, "no match\n");)
+
+                               has_positive = TRUE;
+                       }
+               }
+
+               if (has_positive && !found) {
+                       COVERAGE_DEBUG(fprintf (stderr, "   Positive match was not found\n");)
+
+                       mono_conc_hashtable_insert (filtered_classes, klass, klass);
+                       g_free (fqn);
+                       g_free (classname);
+
+                       return FALSE;
+               }
+
+               for (guint i = 0; i < prof->coverage_filters->len; ++i) {
+                       // FIXME: Is substring search sufficient?
+                       char *filter = g_ptr_array_index (prof->coverage_filters, i);
+                       if (filter [0] == '+')
+                               continue;
+
+                       // Skip '-'
+                       filter = &filter [1];
+                       COVERAGE_DEBUG(fprintf (stderr, "   Checking against -%s ...", filter);)
+
+                       if (strstr (fqn, filter) != NULL) {
+                               COVERAGE_DEBUG(fprintf (stderr, "matched\n");)
+
+                               mono_conc_hashtable_insert (filtered_classes, klass, klass);
+                               g_free (fqn);
+                               g_free (classname);
+
+                               return FALSE;
+                       } else
+                               COVERAGE_DEBUG(fprintf (stderr, "no match\n");)
+
+               }
+
+               g_free (fqn);
+               g_free (classname);
+       }
+
+       COVERAGE_DEBUG(fprintf (stderr, "   Handling coverage for %s\n", mono_method_get_name (method));)
+       header = mono_method_get_header (method);
+
+       mono_method_header_get_code (header, &code_size, NULL);
+
+       assembly = mono_image_get_assembly (image);
+
+       mono_conc_hashtable_insert (coverage_methods, method, method);
+       mono_conc_hashtable_insert (coverage_assemblies, assembly, assembly);
+
+       image_methods = mono_conc_hashtable_lookup (image_to_methods, image);
+
+       if (image_methods == NULL) {
+               image_methods = g_malloc (sizeof (MonoLockFreeQueue));
+               mono_lock_free_queue_init (image_methods);
+               mono_conc_hashtable_insert (image_to_methods, image, image_methods);
+       }
+
+       node = create_method_node (method);
+       mono_lock_free_queue_enqueue (image_methods, node);
+
+       class_methods = mono_conc_hashtable_lookup (coverage_classes, klass);
+
+       if (class_methods == NULL) {
+               class_methods = g_malloc (sizeof (MonoLockFreeQueue));
+               mono_lock_free_queue_init (class_methods);
+               mono_conc_hashtable_insert (coverage_classes, klass, class_methods);
+       }
+
+       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 (FILE *stream)
+{
+       char *buffer;
+       ssize_t bytes_read;
+       long filesize;
+       int res, offset = 0;
+
+       res = fseek (stream, 0, SEEK_END);
+       if (res < 0)
+         return NULL;
+
+       filesize = ftell (stream);
+       if (filesize < 0)
+         return NULL;
+
+       res = fseek (stream, 0, SEEK_SET);
+       if (res < 0)
+         return NULL;
+
+       if (filesize > MAX_FILE_SIZE)
+         return NULL;
+
+       buffer = 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';
+       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;
+       FILE *sa_file;
+
+       mono_mutex_init (&suppressed_assemblies_mutex);
+       suppressed_assemblies = mono_conc_hashtable_new (&suppressed_assemblies_mutex, g_str_hash, g_str_equal);
+       sa_file = fopen (SUPPRESSION_DIR "/mono-profiler-log.suppression", "r");
+       if (sa_file == NULL)
+               return;
+
+       /* Don't need to free @content as it is referred to by the lines stored in @suppressed_assemblies */
+       content = get_file_content (sa_file);
+       if (content == NULL) {
+               g_error ("mono-profiler-log.suppression is greater than 128kb - aborting\n");
+       }
+
+       while ((line = get_next_line (content, &content))) {
+               line = g_strchomp (g_strchug (line));
+               mono_conc_hashtable_insert (suppressed_assemblies, line, line);
+       }
+
+       fclose (sa_file);
+}
+
+static MonoConcurrentHashTable *
+init_hashtable (mono_mutex_t *mutex)
+{
+       mono_mutex_init (mutex);
+       return mono_conc_hashtable_new (mutex, NULL, NULL);
+}
+
+static void
+destroy_hashtable (MonoConcurrentHashTable *hashtable, mono_mutex_t *mutex)
+{
+       mono_conc_hashtable_destroy (hashtable);
+       mono_mutex_destroy (mutex);
+}
+
 #endif /* DISABLE_HELPER_THREAD */
 
+static void
+coverage_init (MonoProfiler *prof)
+{
+#ifndef DISABLE_HELPER_THREAD
+       assert (!coverage_initialized);
+
+       COVERAGE_DEBUG(fprintf (stderr, "Coverage initialized\n");)
+
+       coverage_methods = init_hashtable (&coverage_methods_mutex);
+       coverage_assemblies = init_hashtable (&coverage_assemblies_mutex);
+       coverage_classes = init_hashtable (&coverage_classes_mutex);
+       filtered_classes = init_hashtable (&filtered_classes_mutex);
+       entered_methods = init_hashtable (&entered_methods_mutex);
+       image_to_methods = init_hashtable (&image_to_methods_mutex);
+       init_suppressed_assemblies ();
+
+       coverage_initialized = TRUE;
+#endif /* DISABLE_HELPER_THREAD */
+}
+
 static void
 log_shutdown (MonoProfiler *prof)
 {
+       void *res;
+
        in_shutdown = 1;
 #ifndef DISABLE_HELPER_THREAD
        counters_and_perfcounters_sample (prof);
 
+       dump_coverage (prof);
+
        if (prof->command_port) {
                char c = 1;
-               void *res;
                ign_res (write (prof->pipes [1], &c, 1));
                pthread_join (prof->helper_thread, &res);
        }
@@ -2438,12 +3188,17 @@ log_shutdown (MonoProfiler *prof)
                        read_perf_mmap (prof, i);
        }
 #endif
-       dump_sample_hits (prof, prof->stat_buffers, 1);
-       take_lock ();
-       if (TLS_GET (tlsbuffer))
-               dump_buffer (prof, TLS_GET (tlsbuffer));
+       dump_sample_hits (prof, prof->stat_buffers);
+
+       if (TLS_GET (LogBuffer, tlsbuffer))
+               send_buffer (prof, TLS_GET (GPtrArray, tlsmethodlist), TLS_GET (LogBuffer, tlsbuffer));
+
        TLS_SET (tlsbuffer, NULL);
-       release_lock ();
+       TLS_SET (tlsmethodlist, NULL);
+
+       InterlockedWrite (&prof->run_writer_thread, 0);
+       pthread_join (prof->writer_thread, &res);
+
 #if defined (HAVE_SYS_ZLIB)
        if (prof->gzfile)
                gzclose (prof->gzfile);
@@ -2452,6 +3207,19 @@ log_shutdown (MonoProfiler *prof)
                pclose (prof->file);
        else
                fclose (prof->file);
+
+       destroy_hashtable (prof->method_table, &prof->method_table_mutex);
+
+       if (coverage_initialized) {
+               destroy_hashtable (coverage_methods, &coverage_methods_mutex);
+               destroy_hashtable (coverage_assemblies, &coverage_assemblies_mutex);
+               destroy_hashtable (coverage_classes, &coverage_classes_mutex);
+               destroy_hashtable (filtered_classes, &filtered_classes_mutex);
+               destroy_hashtable (entered_methods, &entered_methods_mutex);
+               destroy_hashtable (image_to_methods, &image_to_methods_mutex);
+               destroy_hashtable (suppressed_assemblies, &suppressed_assemblies_mutex);
+       }
+
        free (prof);
 }
 
@@ -2514,11 +3282,11 @@ new_filename (const char* filename)
        return res;
 }
 
-#ifndef DISABLE_HELPER_THREAD
-
 //this is exposed by the JIT, but it's not meant to be a supported API for now.
 extern void mono_threads_attach_tools_thread (void);
 
+#ifndef DISABLE_HELPER_THREAD
+
 static void*
 helper_thread (void* arg)
 {
@@ -2568,11 +3336,11 @@ helper_thread (void* arg)
                if (len < 0) {
                        if (errno == EINTR)
                                continue;
-                       
+
                        g_warning ("Error in proflog server: %s", strerror (errno));
                        return NULL;
                }
-               
+
                if (FD_ISSET (prof->pipes [0], &rfds)) {
                        char c;
                        int r = read (prof->pipes [0], &c, 1);
@@ -2586,9 +3354,9 @@ helper_thread (void* arg)
                                if (do_debug)
                                        fprintf (stderr, "stat buffer dump\n");
                                if (sbuf) {
-                                       dump_sample_hits (prof, sbuf, 1);
+                                       dump_sample_hits (prof, sbuf);
                                        free_buffer (sbuf, sbuf->size);
-                                       safe_dump (prof, ensure_logbuf (0));
+                                       safe_send (prof, ensure_logbuf (0));
                                }
                                continue;
                        }
@@ -2608,7 +3376,7 @@ helper_thread (void* arg)
                                }
                        }
 #endif
-                       safe_dump (prof, ensure_logbuf (0));
+                       safe_send (prof, ensure_logbuf (0));
                        return NULL;
                }
 #if USE_PERF_EVENTS
@@ -2619,7 +3387,7 @@ helper_thread (void* arg)
                                        continue;
                                if (FD_ISSET (perf_data [i].perf_fd, &rfds)) {
                                        read_perf_mmap (prof, i);
-                                       safe_dump (prof, ensure_logbuf (0));
+                                       safe_send (prof, ensure_logbuf (0));
                                }
                        }
                }
@@ -2637,7 +3405,7 @@ helper_thread (void* arg)
                        if (strcmp (buf, "heapshot\n") == 0) {
                                heapshot_requested = 1;
                                //fprintf (stderr, "perform heapshot\n");
-                               if (runtime_inited && !thread) {
+                               if (InterlockedRead (&runtime_inited) && !thread) {
                                        thread = mono_thread_attach (mono_get_root_domain ());
                                        /*fprintf (stderr, "attached\n");*/
                                }
@@ -2704,13 +3472,120 @@ start_helper_thread (MonoProfiler* prof)
 }
 #endif
 
+static void *
+writer_thread (void *arg)
+{
+       MonoProfiler *prof = arg;
+
+       mono_threads_attach_tools_thread ();
+
+       dump_header (prof);
+
+       while (InterlockedRead (&prof->run_writer_thread)) {
+               WriterQueueEntry *entry;
+
+               while ((entry = (WriterQueueEntry *) mono_lock_free_queue_dequeue (&prof->writer_queue))) {
+                       LogBuffer *method_buffer = NULL;
+                       gboolean new_methods = FALSE;
+
+                       if (entry->methods->len)
+                               method_buffer = create_buffer ();
+
+                       /*
+                        * Encode the method events in a temporary log buffer that we
+                        * flush to disk before the main buffer, ensuring that all
+                        * methods have metadata emitted before they're referenced.
+                        */
+                       for (guint i = 0; i < entry->methods->len; i++) {
+                               MethodInfo *info = g_ptr_array_index (entry->methods, i);
+
+                               if (mono_conc_hashtable_lookup (prof->method_table, info->method))
+                                       continue;
+
+                               new_methods = TRUE;
+
+                               /*
+                                * Other threads use this hash table to get a general
+                                * idea of whether a method has already been emitted to
+                                * the stream. Due to the way we add to this table, it
+                                * can easily happen that multiple threads queue up the
+                                * same methods, but that's OK since eventually all
+                                * methods will be in this table and the thread-local
+                                * method lists will just be empty for the rest of the
+                                * app's lifetime.
+                                */
+                               mono_conc_hashtable_insert (prof->method_table, info->method, info->method);
+
+                               char *name = mono_method_full_name (info->method, 1);
+                               int nlen = strlen (name) + 1;
+                               uint64_t now = current_time ();
+
+                               method_buffer = ensure_logbuf_inner (method_buffer, 32 + nlen);
+
+                               emit_byte (method_buffer, TYPE_JIT | TYPE_METHOD);
+                               emit_time (method_buffer, now);
+                               emit_method_inner (method_buffer, info->method);
+                               emit_ptr (method_buffer, mono_jit_info_get_code_start (info->ji));
+                               emit_value (method_buffer, mono_jit_info_get_code_size (info->ji));
+
+                               memcpy (method_buffer->data, name, nlen);
+                               method_buffer->data += nlen;
+
+                               mono_free (name);
+                               free (info);
+                       }
+
+                       g_ptr_array_free (entry->methods, TRUE);
+
+                       if (new_methods)
+                               dump_buffer (prof, method_buffer);
+                       else if (method_buffer)
+                               free_buffer (method_buffer, method_buffer->size);
+
+                       dump_buffer (prof, entry->buffer);
+
+                       free (entry);
+               }
+       }
+
+       return NULL;
+}
+
+static int
+start_writer_thread (MonoProfiler* prof)
+{
+       InterlockedWrite (&prof->run_writer_thread, 1);
+
+       return !pthread_create (&prof->writer_thread, NULL, writer_thread, prof);
+}
+
+static void
+runtime_initialized (MonoProfiler *profiler)
+{
+#ifndef DISABLE_HELPER_THREAD
+       if (hs_mode_ondemand || need_helper_thread) {
+               if (!start_helper_thread (profiler))
+                       profiler->command_port = 0;
+       }
+#endif
+
+       start_writer_thread (profiler);
+
+       InterlockedWrite (&runtime_inited, 1);
+#ifndef DISABLE_HELPER_THREAD
+       counters_init (profiler);
+       counters_sample (profiler, 0);
+#endif
+       /* ensure the main thread data and startup are available soon */
+       safe_send (profiler, ensure_logbuf (0));
+}
+
 static MonoProfiler*
-create_profiler (const char *filename)
+create_profiler (const char *filename, GPtrArray *filters)
 {
        MonoProfiler *prof;
        char *nf;
        int force_delete = 0;
-       int need_helper_thread = 0;
        prof = calloc (1, sizeof (MonoProfiler));
 
        prof->command_port = command_port;
@@ -2741,7 +3616,6 @@ create_profiler (const char *filename)
                int fd = strtol (nf + 1, NULL, 10);
                prof->file = fdopen (fd, "a");
        } else {
-               FILE *f;
                if (force_delete)
                        unlink (nf);
                prof->file = fopen (nf, "wb");
@@ -2769,17 +3643,25 @@ create_profiler (const char *filename)
        if (do_counters && !need_helper_thread) {
                need_helper_thread = 1;
        }
-#ifndef DISABLE_HELPER_THREAD
-       if (hs_mode_ondemand || need_helper_thread) {
-               if (!start_helper_thread (prof))
-                       prof->command_port = 0;
-       }
-#else
+
+#ifdef DISABLE_HELPER_THREAD
        if (hs_mode_ondemand)
                fprintf (stderr, "Ondemand heapshot unavailable on this arch.\n");
+
+       if (do_coverage)
+               fprintf (stderr, "Coverage unavailable on this arch.\n");
+
 #endif
+
+       mono_lock_free_queue_init (&prof->writer_queue);
+       mono_mutex_init (&prof->method_table_mutex);
+       prof->method_table = mono_conc_hashtable_new (&prof->method_table_mutex, NULL, NULL);
+
+       if (do_coverage)
+               coverage_init (prof);
+       prof->coverage_filters = filters;
+
        prof->startup_time = current_time ();
-       dump_header (prof);
        return prof;
 }
 
@@ -2789,24 +3671,29 @@ usage (int do_exit)
        printf ("Log profiler version %d.%d (format: %d)\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR, LOG_DATA_VERSION);
        printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
        printf ("Options:\n");
-       printf ("\thelp             show this usage info\n");
-       printf ("\t[no]alloc        enable/disable recording allocation info\n");
-       printf ("\t[no]calls        enable/disable recording enter/leave method events\n");
-       printf ("\theapshot[=MODE]  record heap shot info (by default at each major collection)\n");
-       printf ("\t                 MODE: every XXms milliseconds, every YYgc collections, ondemand\n");
-       printf ("\tcounters         sample counters every 1s\n");
-       printf ("\tsample[=TYPE]    use statistical sampling mode (by default cycles/1000)\n");
-       printf ("\t                 TYPE: cycles,instr,cacherefs,cachemiss,branches,branchmiss\n");
-       printf ("\t                 TYPE can be followed by /FREQUENCY\n");
-       printf ("\ttime=fast        use a faster (but more inaccurate) timer\n");
-       printf ("\tmaxframes=NUM    collect up to NUM stack frames\n");
-       printf ("\tcalldepth=NUM    ignore method events for call chain depth bigger than NUM\n");
-       printf ("\toutput=FILENAME  write the data to file FILENAME (-FILENAME to overwrite)\n");
-       printf ("\toutput=|PROGRAM  write the data to the stdin of PROGRAM\n");
-       printf ("\t                 %%t is subtituted with date and time, %%p with the pid\n");
-       printf ("\treport           create a report instead of writing the raw data to a file\n");
-       printf ("\tzip              compress the output data\n");
-       printf ("\tport=PORTNUM     use PORTNUM for the listening command server\n");
+       printf ("\thelp                 show this usage info\n");
+       printf ("\t[no]alloc            enable/disable recording allocation info\n");
+       printf ("\t[no]calls            enable/disable recording enter/leave method events\n");
+       printf ("\theapshot[=MODE]      record heap shot info (by default at each major collection)\n");
+       printf ("\t                     MODE: every XXms milliseconds, every YYgc collections, ondemand\n");
+       printf ("\tcounters             sample counters every 1s\n");
+       printf ("\tsample[=TYPE]        use statistical sampling mode (by default cycles/1000)\n");
+       printf ("\t                     TYPE: cycles,instr,cacherefs,cachemiss,branches,branchmiss\n");
+       printf ("\t                     TYPE can be followed by /FREQUENCY\n");
+       printf ("\ttime=fast            use a faster (but more inaccurate) timer\n");
+       printf ("\tmaxframes=NUM        collect up to NUM stack frames\n");
+       printf ("\tcalldepth=NUM        ignore method events for call chain depth bigger than NUM\n");
+       printf ("\toutput=FILENAME      write the data to file FILENAME (-FILENAME to overwrite)\n");
+       printf ("\toutput=|PROGRAM      write the data to the stdin of PROGRAM\n");
+       printf ("\t                     %%t is subtituted with date and time, %%p with the pid\n");
+       printf ("\treport               create a report instead of writing the raw data to a file\n");
+       printf ("\tzip                  compress the output data\n");
+       printf ("\tport=PORTNUM         use PORTNUM for the listening command server\n");
+       printf ("\tcoverage             enable collection of code coverage data\n");
+       printf ("\tcovfilter=ASSEMBLY   add an assembly to the code coverage filters\n");
+       printf ("\t                     add a + to include the assembly or a - to exclude it\n");
+       printf ("\t                     filter=-mscorlib\n");
+       printf ("\tcovfilter-file=FILE  use FILE to generate the list of assemblies to be filtered\n");
        if (do_exit)
                exit (1);
 }
@@ -2931,7 +3818,7 @@ set_hsmode (char* val, int allow_empty)
        free (val);
 }
 
-/* 
+/*
  * declaration to silence the compiler: this is the entry point that
  * mono will load from the shared library and call.
  */
@@ -2955,6 +3842,7 @@ void
 mono_profiler_startup (const char *desc)
 {
        MonoProfiler *prof;
+       GPtrArray *filters = NULL;
        char *filename = NULL;
        const char *p;
        const char *opt;
@@ -2962,10 +3850,12 @@ mono_profiler_startup (const char *desc)
        int calls_enabled = 0;
        int allocs_enabled = 0;
        int only_counters = 0;
+       int only_coverage = 0;
        int events = MONO_PROFILE_GC|MONO_PROFILE_ALLOCATIONS|
                MONO_PROFILE_GC_MOVES|MONO_PROFILE_CLASS_EVENTS|MONO_PROFILE_THREADS|
                MONO_PROFILE_ENTER_LEAVE|MONO_PROFILE_JIT_COMPILATION|MONO_PROFILE_EXCEPTIONS|
-               MONO_PROFILE_MONITOR_EVENTS|MONO_PROFILE_MODULE_EVENTS|MONO_PROFILE_GC_ROOTS;
+               MONO_PROFILE_MONITOR_EVENTS|MONO_PROFILE_MODULE_EVENTS|MONO_PROFILE_GC_ROOTS|
+               MONO_PROFILE_INS_COVERAGE;
 
        p = desc;
        if (strncmp (p, "log", 3))
@@ -3083,6 +3973,47 @@ mono_profiler_startup (const char *desc)
                        only_counters = 1;
                        continue;
                }
+               if ((opt = match_option (p, "coverage", NULL)) != p) {
+                       do_coverage = 1;
+                       events |= MONO_PROFILE_ENTER_LEAVE;
+                       debug_coverage = (g_getenv ("MONO_PROFILER_DEBUG_COVERAGE") != NULL);
+                       continue;
+               }
+               if ((opt = match_option (p, "onlycoverage", NULL)) != p) {
+                       only_coverage = 1;
+                       continue;
+               }
+               if ((opt = match_option (p, "covfilter-file", &val)) != p) {
+                       FILE *filter_file;
+                       char *line, *content;
+
+                       if (filters == NULL)
+                               filters = g_ptr_array_new ();
+
+                       filter_file = fopen (val, "r");
+                       if (filter_file == NULL) {
+                               fprintf (stderr, "Unable to open %s\n", val);
+                               exit (0);
+                       }
+
+                       /* Don't need to free content as it is referred to by the lines stored in @filters */
+                       content = get_file_content (filter_file);
+                       if (content == NULL)
+                               fprintf (stderr, "WARNING: %s is greater than 128kb - ignoring\n", val);
+
+                       while ((line = get_next_line (content, &content)))
+                               g_ptr_array_add (filters, g_strchug (g_strchomp (line)));
+
+                       fclose (filter_file);
+                       continue;
+               }
+               if ((opt = match_option (p, "covfilter", &val)) != p) {
+                       if (filters == NULL)
+                               filters = g_ptr_array_new ();
+
+                       g_ptr_array_add (filters, val);
+                       continue;
+               }
                if (opt == p) {
                        usage (0);
                        exit (0);
@@ -3096,9 +4027,12 @@ mono_profiler_startup (const char *desc)
                events |= MONO_PROFILE_ALLOCATIONS;
        if (only_counters)
                events = 0;
+       if (only_coverage)
+               events = MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_INS_COVERAGE;
+
        utils_init (fast_time);
 
-       prof = create_profiler (filename);
+       prof = create_profiler (filename, filters);
        if (!prof)
                return;
        init_thread ();
@@ -3118,8 +4052,9 @@ mono_profiler_startup (const char *desc)
        mono_profiler_install_exception (throw_exc, method_exc_leave, clause_exc);
        mono_profiler_install_monitor (monitor_event);
        mono_profiler_install_runtime_initialized (runtime_initialized);
+       if (do_coverage)
+               mono_profiler_install_coverage_filter (coverage_filter);
 
-       
        if (do_mono_sample && sample_type == SAMPLE_CYCLES && !only_counters) {
                events |= MONO_PROFILE_STATISTICAL;
                mono_profiler_set_statistical_mode (sampling_mode, 1000000 / sample_freq);
@@ -3129,5 +4064,5 @@ mono_profiler_startup (const char *desc)
        mono_profiler_set_events (events);
 
        TLS_INIT (tlsbuffer);
+       TLS_INIT (tlsmethodlist);
 }
-