Merge pull request #1636 from alexrp/profiler-stuff
authorAlex Rønne Petersen <alex@alexrp.com>
Tue, 17 Mar 2015 22:02:00 +0000 (23:02 +0100)
committerAlex Rønne Petersen <alex@alexrp.com>
Tue, 17 Mar 2015 22:02:00 +0000 (23:02 +0100)
[profiler] Redesign buffer flushing and method reporting.

configure.ac
mcs/.gitignore
mono/mini/jit.h
mono/mini/mini-runtime.c
mono/profiler/proflog.c

index 940b55744ba3bfd7064020406e5c2b6b82bead22..cc1338ac350f3fd02b9e046f63657b4f63e9c698 100644 (file)
@@ -2512,6 +2512,12 @@ fi
 
 AC_ARG_ENABLE(bcl-opt, [  --disable-bcl-opt    BCL is compiled with no optimizations (allows accurate BCL debugging)], test_bcl_opt=$enableval, test_bcl_opt=yes)
 
+AC_ARG_ENABLE(perf-events, [  --disable-perf-events Disable using `perf` for profiling on Linux], test_perf_events=$enableval, test_perf_events=yes)
+if test "x$test_perf_events" != "xyes"; then
+       AC_DEFINE(DISABLE_PERF_EVENTS, 1, [Disable using `perf` for profiling on Linux])
+       AC_SUBST(DISABLE_PERF_EVENTS)
+fi
+
 AC_MSG_CHECKING([if big-arrays are to be enabled])
 AC_ARG_ENABLE(big-arrays,  [  --enable-big-arrays      Enable the allocation and indexing of arrays greater than Int32.MaxValue], enable_big_arrays=$enableval, enable_big_arrays=no)
 if test "x$enable_big_arrays" = "xyes" ; then
index 404aa14ef19436d698ad8a398d3beafdcd632ea6..ee0bcdccdd807a633164a9208e785f71a8b1cf10 100644 (file)
@@ -10,6 +10,7 @@
 TestResult-*.log
 TestResult-*.xml
 TestResult*.xml
+.dep_dirs-*
 errors/*.log
 errors/dummy.xml
 class/Mono.Data.Sqlite/test.db
index 5200da4dd91d8167ec790c67fc2bbd2a2d8d5b88..e9315ba257ad2ec9169a722b3cf739256c41886e 100644 (file)
@@ -60,6 +60,14 @@ mono_jit_parse_options     (int argc, char * argv[]);
 
 MONO_API char*       mono_get_runtime_build_info    (void);
 
+/* The following APIs are not stable. Avoid if possible. */
+
+MONO_API MonoJitInfo *
+mono_get_jit_info_from_method (MonoDomain *domain, MonoMethod *method);
+
+MONO_API void *
+mono_aot_get_method (MonoDomain *domain, MonoMethod *method);
+
 MONO_END_DECLS
 
 #endif
index 8495f441c4e998576d71c5d21ed1c16c3386a652..b29dd830e1b27b5ae9c375900d9392a616e3c4c6 100755 (executable)
@@ -1745,6 +1745,12 @@ lookup_method (MonoDomain *domain, MonoMethod *method)
        return ji;
 }
 
+MonoJitInfo *
+mono_get_jit_info_from_method (MonoDomain *domain, MonoMethod *method)
+{
+       return lookup_method (domain, method);
+}
+
 #if ENABLE_JIT_MAP
 static FILE* perf_map_file;
 
index face15eff94e38103bcf99d8e40495daf36bbd4e..3dcb3a2d0b4a2300d8686d2c7787a52a33d73bac 100644 (file)
@@ -9,15 +9,19 @@
  */
 
 #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/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"
+
+#ifndef DISABLE_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;
@@ -382,7 +392,6 @@ struct _BinaryObject {
 };
 
 struct _MonoProfiler {
-       LogBuffer *buffers;
        StatBuffer *stat_buffers;
        FILE* file;
 #if defined (HAVE_SYS_ZLIB)
@@ -396,28 +405,49 @@ 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;
 };
 
+typedef struct _WriterQueueEntry WriterQueueEntry;
+struct _WriterQueueEntry {
+       MonoLockFreeQueueNode node;
+       GPtrArray *methods;
+       LogBuffer *buffer;
+};
+
+typedef struct _MethodInfo MethodInfo;
+struct _MethodInfo {
+       MonoMethod *method;
+       MonoJitInfo *ji;
+};
+
 #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 +483,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
@@ -531,7 +581,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;
@@ -542,6 +592,42 @@ emit_method (LogBuffer *logbuffer, void *method)
        assert (logbuffer->data <= logbuffer->data_end);
 }
 
+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)
+                       ji = mono_get_jit_info_from_method (domain, method);
+
+               // It could be AOT'd, so we need to force it to be loaded.
+               if (!ji) {
+                       // Loads the method as a side effect.
+                       mono_aot_get_method (domain, method);
+
+                       ji = mono_get_jit_info_from_method (domain, method);
+               }
+
+               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)
 {
@@ -640,6 +726,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)
 {
@@ -688,22 +784,25 @@ runtime_initialized (MonoProfiler *profiler)
        counters_sample (profiler, 0);
 #endif
        /* ensure the main thread data and startup are available soon */
-       safe_dump (profiler, ensure_logbuf (0));
+       safe_send (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)
 {
        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
@@ -789,7 +888,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);
 }
 
@@ -883,7 +982,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);
 }
@@ -1004,7 +1103,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);
 }
 
@@ -1042,7 +1141,7 @@ 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);
 }
 
@@ -1057,7 +1156,7 @@ 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);
        process_requests (prof);
 }
@@ -1073,10 +1172,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);
 }
 
@@ -1094,37 +1193,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
@@ -1189,7 +1269,7 @@ 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);
 }
 
@@ -1224,11 +1304,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
@@ -1674,36 +1754,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;
        }
@@ -1724,12 +1782,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);
@@ -1746,9 +1801,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;
        }
@@ -1756,13 +1815,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)
@@ -1845,7 +1897,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;
@@ -2135,7 +2187,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);
 }
@@ -2253,7 +2305,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);
 }
@@ -2312,7 +2364,7 @@ perfcounters_emit (MonoProfiler *profiler)
        }
        EXIT_LOG (logbuffer);
 
-       safe_dump (profiler, ensure_logbuf (0));
+       safe_send (profiler, ensure_logbuf (0));
 }
 
 static gboolean
@@ -2394,7 +2446,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);
 }
@@ -2418,13 +2470,14 @@ counters_and_perfcounters_sample (MonoProfiler *prof)
 static void
 log_shutdown (MonoProfiler *prof)
 {
+       void *res;
+
        in_shutdown = 1;
 #ifndef DISABLE_HELPER_THREAD
        counters_and_perfcounters_sample (prof);
 
        if (prof->command_port) {
                char c = 1;
-               void *res;
                ign_res (write (prof->pipes [1], &c, 1));
                pthread_join (prof->helper_thread, &res);
        }
@@ -2436,12 +2489,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);
@@ -2450,6 +2508,10 @@ log_shutdown (MonoProfiler *prof)
                pclose (prof->file);
        else
                fclose (prof->file);
+
+       mono_conc_hashtable_destroy (prof->method_table);
+       mono_mutex_destroy (&prof->method_table_mutex);
+
        free (prof);
 }
 
@@ -2512,11 +2574,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)
 {
@@ -2584,9 +2646,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;
                        }
@@ -2606,7 +2668,7 @@ helper_thread (void* arg)
                                }
                        }
 #endif
-                       safe_dump (prof, ensure_logbuf (0));
+                       safe_send (prof, ensure_logbuf (0));
                        return NULL;
                }
 #if USE_PERF_EVENTS
@@ -2617,7 +2679,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));
                                }
                        }
                }
@@ -2702,6 +2764,93 @@ 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 MonoProfiler*
 create_profiler (const char *filename)
 {
@@ -2775,8 +2924,14 @@ create_profiler (const char *filename)
        if (hs_mode_ondemand)
                fprintf (stderr, "Ondemand heapshot 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);
+
+       start_writer_thread (prof);
+
        prof->startup_time = current_time ();
-       dump_header (prof);
        return prof;
 }
 
@@ -3126,5 +3281,6 @@ mono_profiler_startup (const char *desc)
        mono_profiler_set_events (events);
 
        TLS_INIT (tlsbuffer);
+       TLS_INIT (tlsmethodlist);
 }