[profiler] Redesign buffer flushing and method reporting.
authorAlex Rønne Petersen <alexrp@xamarin.com>
Mon, 9 Mar 2015 18:03:44 +0000 (19:03 +0100)
committerAlex Rønne Petersen <alexrp@xamarin.com>
Tue, 17 Mar 2015 15:35:45 +0000 (16:35 +0100)
The main goal of this change is to ensure that all methods have a `TYPE_JIT`
event emitted in the stream before they are referenced in other events. Before
this change, `TYPE_JIT` events could arrive out of order or not be emitted at
all because of generic sharing.

We now use a separate thread for writing log buffers to the output file.
Whenever a method is referenced in a TLS log buffer, we add it to a TLS list of
methods. When we ship the TLS log buffer off to the writer thread, we also send
the TLS list of methods. The writer thread first goes over all the methods and,
if some of them haven't had a `TYPE_JIT` event emitted yet, emits them to a
temporary buffer which is flushed just before flushing the main log buffer.

This approach ensures that `TYPE_JIT` events will *always* come before the
events that reference them.

Bookkeeping of which methods have been emitted to the output stream is done in
a global, concurrent hash table. Before a thread adds a method to its TLS list
of methods, it checks if the method is in the hash table. If it is, we know
that the method has already been emitted to the output stream, so it isn't
added to the list. The writer thread is the only thread that updates the hash
table (which happens when it flushes the temporary method buffer). So,
eventually, all TLS method lists will end up being empty because all methods
have been emitted to the output stream.

mono/profiler/proflog.c

index 80ad046ad60dec7177091b46608731b06fe81c54..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>
@@ -401,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)
@@ -458,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
@@ -536,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;
@@ -547,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)
 {
@@ -645,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)
 {
@@ -693,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
@@ -794,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);
 }
 
@@ -888,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);
 }
@@ -1009,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);
 }
 
@@ -1047,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);
 }
 
@@ -1062,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);
 }
@@ -1078,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);
 }
 
@@ -1099,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
@@ -1194,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);
 }
 
@@ -1229,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
@@ -1679,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;
        }
@@ -1729,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);
@@ -1751,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;
        }
@@ -2133,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);
 }
@@ -2251,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);
 }
@@ -2310,7 +2364,7 @@ perfcounters_emit (MonoProfiler *profiler)
        }
        EXIT_LOG (logbuffer);
 
-       safe_dump (profiler, ensure_logbuf (0));
+       safe_send (profiler, ensure_logbuf (0));
 }
 
 static gboolean
@@ -2392,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);
 }
@@ -2416,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);
        }
@@ -2434,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);
@@ -2448,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);
 }
 
@@ -2582,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;
                        }
@@ -2604,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
@@ -2615,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));
                                }
                        }
                }
@@ -2700,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)
 {
@@ -2773,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;
 }
 
@@ -3124,5 +3281,6 @@ mono_profiler_startup (const char *desc)
        mono_profiler_set_events (events);
 
        TLS_INIT (tlsbuffer);
+       TLS_INIT (tlsmethodlist);
 }