[profiler] Remove the need to pass a MonoProfiler argument to some functions.
[mono.git] / mono / profiler / mono-profiler-log.c
index 091272e5f6697688e52fda7c57c6c64a9cf707d7..78dbc8c91611cb77c5665ad6b404ba7c6f620812 100644 (file)
  */
 
 #include <config.h>
-#include "../mini/jit.h"
-#include "../metadata/metadata-internals.h"
-#include <mono/metadata/profiler.h>
-#include <mono/metadata/threads.h>
+#include <mono/metadata/assembly.h>
 #include <mono/metadata/debug-helpers.h>
+#include "../metadata/metadata-internals.h"
 #include <mono/metadata/mono-config.h>
 #include <mono/metadata/mono-gc.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/metadata/profiler.h>
 #include <mono/utils/atomic.h>
+#include <mono/utils/hazard-pointer.h>
+#include <mono/utils/lock-free-alloc.h>
+#include <mono/utils/lock-free-queue.h>
+#include <mono/utils/mono-conc-hashtable.h>
+#include <mono/utils/mono-counters.h>
+#include <mono/utils/mono-linked-list-set.h>
 #include <mono/utils/mono-membar.h>
 #include <mono/utils/mono-mmap.h>
-#include <mono/utils/mono-counters.h>
 #include <mono/utils/mono-os-mutex.h>
 #include <mono/utils/mono-os-semaphore.h>
-#include <mono/utils/mono-conc-hashtable.h>
-#include <mono/utils/mono-linked-list-set.h>
-#include <mono/utils/lock-free-alloc.h>
-#include <mono/utils/lock-free-queue.h>
-#include <mono/utils/hazard-pointer.h>
 #include <mono/utils/mono-threads.h>
 #include <mono/utils/mono-threads-api.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <glib.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef HAVE_SCHED_GETAFFINITY
-#include <sched.h>
-#endif
-#include <fcntl.h>
-#include <errno.h>
-#include <time.h>
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#if defined(__APPLE__)
-#include <mach/mach_time.h>
-#endif
-#if defined(HOST_WIN32) || defined(DISABLE_SOCKETS)
-#define DISABLE_HELPER_THREAD 1
-#endif
+#include "mono-profiler-log.h"
 
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
 #ifdef HAVE_DLFCN_H
 #include <dlfcn.h>
 #endif
-#ifdef HAVE_EXECINFO_H
-#include <execinfo.h>
-#endif
 #ifdef HAVE_LINK_H
 #include <link.h>
 #endif
-
-#ifndef DISABLE_HELPER_THREAD
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <sys/select.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
 #endif
-
-#ifdef HOST_WIN32
-#include <windows.h>
-#else
-#include <pthread.h>
+#if defined(__APPLE__)
+#include <mach/mach_time.h>
 #endif
-
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
+#include <netinet/in.h>
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
 #endif
-
-#include "mono-profiler-log.h"
-
+#include <sys/socket.h>
 #if defined (HAVE_SYS_ZLIB)
 #include <zlib.h>
 #endif
 
-#if defined(__linux__)
+#if defined(__linux__) && defined (ENABLE_PERF_EVENTS)
 
-#include <unistd.h>
-#include <sys/syscall.h>
-
-#ifdef ENABLE_PERF_EVENTS
 #include <linux/perf_event.h>
 
 #define USE_PERF_EVENTS 1
 
 static int read_perf_mmap (MonoProfiler* prof, int cpu);
-#endif
 
 #endif
 
@@ -126,7 +80,6 @@ static int do_report = 0;
 static int do_heap_shot = 0;
 static int max_call_depth = 100;
 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;
@@ -512,6 +465,12 @@ struct _LogBuffer {
 typedef struct {
        MonoLinkedListSetNode node;
 
+       // Convenience pointer to the profiler structure.
+       MonoProfiler *profiler;
+
+       // Was this thread added to the LLS?
+       gboolean attached;
+
        // The current log buffer for this thread.
        LogBuffer *buffer;
 
@@ -522,13 +481,11 @@ typedef struct {
        int call_depth;
 
        // Indicates whether this thread is currently writing to its `buffer`.
-       int busy;
-} MonoProfilerThread;
+       gboolean busy;
 
-static inline void
-ign_res (int G_GNUC_UNUSED unused, ...)
-{
-}
+       // Has this thread written a thread end event to `buffer`?
+       gboolean ended;
+} MonoProfilerThread;
 
 static uintptr_t
 thread_id (void)
@@ -615,16 +572,26 @@ init_time (void)
 
 #define ENTER_LOG(COUNTER, BUFFER, SIZE) \
        do { \
-               buffer_lock (); \
-               g_assert (!PROF_TLS_GET ()->busy++ && "Why are we trying to write a new event while already writing one?"); \
+               MonoProfilerThread *thread__ = PROF_TLS_GET (); \
+               if (thread__->attached) \
+                       buffer_lock (); \
+               g_assert (!thread__->busy && "Why are we trying to write a new event while already writing one?"); \
+               thread__->busy = TRUE; \
                InterlockedIncrement ((COUNTER)); \
                LogBuffer *BUFFER = ensure_logbuf_unsafe ((SIZE))
 
-#define EXIT_LOG \
-               PROF_TLS_GET ()->busy--; \
-               buffer_unlock (); \
+#define EXIT_LOG_EXPLICIT(SEND, REQUESTS) \
+               thread__->busy = FALSE; \
+               if ((SEND)) \
+                       send_log_unsafe (FALSE, TRUE); \
+               if (thread__->attached) \
+                       buffer_unlock (); \
+               if ((REQUESTS)) \
+                       process_requests (); \
        } while (0)
 
+#define EXIT_LOG EXIT_LOG_EXPLICIT (TRUE, TRUE)
+
 static volatile gint32 buffer_rwlock_count;
 static volatile gpointer buffer_rwlock_exclusive;
 
@@ -823,6 +790,12 @@ create_buffer (void)
        return buf;
 }
 
+/*
+ * Must be called with the reader lock held if thread is the current thread, or
+ * the exclusive lock if thread is a different thread. However, if thread is
+ * the current thread, and init_thread () was called with add_to_lls = FALSE,
+ * then no locking is necessary.
+ */
 static void
 init_buffer_state (MonoProfilerThread *thread)
 {
@@ -839,7 +812,7 @@ clear_hazard_pointers (MonoThreadHazardPointers *hp)
 }
 
 static MonoProfilerThread *
-init_thread (gboolean add_to_lls)
+init_thread (MonoProfiler *prof, gboolean add_to_lls)
 {
        MonoProfilerThread *thread = PROF_TLS_GET ();
 
@@ -859,8 +832,11 @@ init_thread (gboolean add_to_lls)
 
        thread = malloc (sizeof (MonoProfilerThread));
        thread->node.key = thread_id ();
+       thread->profiler = prof;
+       thread->attached = add_to_lls;
        thread->call_depth = 0;
        thread->busy = 0;
+       thread->ended = FALSE;
 
        init_buffer_state (thread);
 
@@ -883,6 +859,8 @@ init_thread (gboolean add_to_lls)
 static void
 deinit_thread (MonoProfilerThread *thread)
 {
+       g_assert (!thread->attached && "Why are we manually freeing an attached thread?");
+
        free (thread);
        PROF_TLS_SET (NULL);
 }
@@ -972,29 +950,26 @@ emit_byte (LogBuffer *logbuffer, int value)
 {
        logbuffer->cursor [0] = value;
        logbuffer->cursor++;
-       assert (logbuffer->cursor <= logbuffer->buf_end);
+
+       g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
 }
 
 static void
 emit_value (LogBuffer *logbuffer, int value)
 {
        encode_uleb128 (value, logbuffer->cursor, &logbuffer->cursor);
-       assert (logbuffer->cursor <= logbuffer->buf_end);
+
+       g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
 }
 
 static void
 emit_time (LogBuffer *logbuffer, uint64_t value)
 {
        uint64_t tdiff = value - logbuffer->last_time;
-       //if (value < logbuffer->last_time)
-       //      printf ("time went backwards\n");
-       //if (tdiff > 1000000)
-       //      printf ("large time offset: %llu\n", tdiff);
        encode_uleb128 (tdiff, logbuffer->cursor, &logbuffer->cursor);
-       /*if (tdiff != decode_uleb128 (p, &p))
-               printf ("incorrect encoding: %llu\n", tdiff);*/
        logbuffer->last_time = value;
-       assert (logbuffer->cursor <= logbuffer->buf_end);
+
+       g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
 }
 
 static void
@@ -1014,112 +989,64 @@ static void
 emit_svalue (LogBuffer *logbuffer, int64_t value)
 {
        encode_sleb128 (value, logbuffer->cursor, &logbuffer->cursor);
-       assert (logbuffer->cursor <= logbuffer->buf_end);
+
+       g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
 }
 
 static void
 emit_uvalue (LogBuffer *logbuffer, uint64_t value)
 {
        encode_uleb128 (value, logbuffer->cursor, &logbuffer->cursor);
-       assert (logbuffer->cursor <= logbuffer->buf_end);
+
+       g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
 }
 
 static void
 emit_ptr (LogBuffer *logbuffer, void *ptr)
 {
        if (!logbuffer->ptr_base)
-               logbuffer->ptr_base = (uintptr_t)ptr;
-       emit_svalue (logbuffer, (intptr_t)ptr - logbuffer->ptr_base);
-       assert (logbuffer->cursor <= logbuffer->buf_end);
+               logbuffer->ptr_base = (uintptr_t) ptr;
+
+       emit_svalue (logbuffer, (intptr_t) ptr - logbuffer->ptr_base);
+
+       g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
 }
 
 static void
 emit_method_inner (LogBuffer *logbuffer, void *method)
 {
        if (!logbuffer->method_base) {
-               logbuffer->method_base = (intptr_t)method;
-               logbuffer->last_method = (intptr_t)method;
+               logbuffer->method_base = (intptr_t) method;
+               logbuffer->last_method = (intptr_t) method;
        }
-       encode_sleb128 ((intptr_t)((char*)method - (char*)logbuffer->last_method), logbuffer->cursor, &logbuffer->cursor);
-       logbuffer->last_method = (intptr_t)method;
-       assert (logbuffer->cursor <= logbuffer->buf_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);
-       }
+       encode_sleb128 ((intptr_t) ((char *) method - (char *) logbuffer->last_method), logbuffer->cursor, &logbuffer->cursor);
+       logbuffer->last_method = (intptr_t) method;
 
-       if (ji)
-               search->found = ji;
+       g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
 }
-*/
 
 static void
-register_method_local (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *ji)
+register_method_local (MonoMethod *method, MonoJitInfo *ji)
 {
-       if (!mono_conc_hashtable_lookup (prof->method_table, method)) {
-               /*
-                * FIXME: In some cases, we crash while looking up JIT info for AOT'd methods.
-                * This usually happens for static constructors. This code is disabled for now
-                * as we don't need this info for anything critical.
-                *
-                * https://bugzilla.xamarin.com/show_bug.cgi?id=35171
-                */
-               /*
-               if (!ji) {
-                       MethodSearch search = { method, NULL };
-
-                       mono_domain_foreach (find_method, &search);
-
-                       ji = search.found;
-               }
-               */
-
-               /*
-                * FIXME: We can't always find JIT info for a generic shared method, especially
-                * if we obtained the MonoMethod during an async stack walk. For now, we deal
-                * with this by giving the generic shared method name and dummy code start/size
-                * information (i.e. zeroes).
-                */
-               //g_assert (ji);
+       MonoProfilerThread *thread = PROF_TLS_GET ();
 
+       if (!mono_conc_hashtable_lookup (thread->profiler->method_table, method)) {
                MethodInfo *info = (MethodInfo *) malloc (sizeof (MethodInfo));
 
                info->method = method;
                info->ji = ji;
                info->time = current_time ();
 
-               MonoProfilerThread *thread = PROF_TLS_GET ();
                GPtrArray *arr = thread->methods ? thread->methods : (thread->methods = g_ptr_array_new ());
                g_ptr_array_add (arr, info);
        }
 }
 
 static void
-emit_method (MonoProfiler *prof, LogBuffer *logbuffer, MonoMethod *method)
+emit_method (LogBuffer *logbuffer, MonoMethod *method)
 {
-       register_method_local (prof, method, NULL);
+       register_method_local (method, NULL);
        emit_method_inner (logbuffer, method);
 }
 
@@ -1127,9 +1054,11 @@ static void
 emit_obj (LogBuffer *logbuffer, void *ptr)
 {
        if (!logbuffer->obj_base)
-               logbuffer->obj_base = (uintptr_t)ptr >> 3;
-       emit_svalue (logbuffer, ((uintptr_t)ptr >> 3) - logbuffer->obj_base);
-       assert (logbuffer->cursor <= logbuffer->buf_end);
+               logbuffer->obj_base = (uintptr_t) ptr >> 3;
+
+       emit_svalue (logbuffer, ((uintptr_t) ptr >> 3) - logbuffer->obj_base);
+
+       g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
 }
 
 static void
@@ -1255,59 +1184,64 @@ dump_header (MonoProfiler *profiler)
        free (hbuf);
 }
 
+/*
+ * Must be called with the reader lock held if thread is the current thread, or
+ * the exclusive lock if thread is a different thread. However, if thread is
+ * the current thread, and init_thread () was called with add_to_lls = FALSE,
+ * then no locking is necessary.
+ */
 static void
-send_buffer (MonoProfiler *prof, MonoProfilerThread *thread)
+send_buffer (MonoProfilerThread *thread)
 {
-       WriterQueueEntry *entry = mono_lock_free_alloc (&prof->writer_entry_allocator);
+       WriterQueueEntry *entry = mono_lock_free_alloc (&thread->profiler->writer_entry_allocator);
        entry->methods = thread->methods;
        entry->buffer = thread->buffer;
 
        mono_lock_free_queue_node_init (&entry->node, FALSE);
 
-       mono_lock_free_queue_enqueue (&prof->writer_queue, &entry->node);
-       mono_os_sem_post (&prof->writer_queue_sem);
+       mono_lock_free_queue_enqueue (&thread->profiler->writer_queue, &entry->node);
+       mono_os_sem_post (&thread->profiler->writer_queue_sem);
 }
 
 static void
-remove_thread (MonoProfiler *prof, MonoProfilerThread *thread, gboolean from_callback)
+free_thread (gpointer p)
 {
-       MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
+       MonoProfilerThread *thread = p;
 
-       if (mono_lls_remove (&profiler_thread_list, hp, &thread->node)) {
+       if (!thread->ended) {
                /*
-                * No need to take the buffer lock here as no other threads can
-                * be accessing this buffer anymore.
+                * The thread is being cleaned up by the main thread during
+                * shutdown. This typically happens for internal runtime
+                * threads. We need to synthesize a thread end event.
                 */
 
-               if (!from_callback) {
-                       /*
-                        * The thread is being cleaned up by the main thread during
-                        * shutdown. This typically happens for internal runtime
-                        * threads. We need to synthesize a thread end event.
-                        */
+               InterlockedIncrement (&thread_ends_ctr);
 
-                       InterlockedIncrement (&thread_ends_ctr);
+               thread->buffer = ensure_logbuf_inner (thread->buffer,
+                       EVENT_SIZE /* event */ +
+                       BYTE_SIZE /* type */ +
+                       LEB128_SIZE /* tid */
+               );
 
-                       thread->buffer = ensure_logbuf_inner (thread->buffer,
-                               EVENT_SIZE /* event */ +
-                               BYTE_SIZE /* type */ +
-                               LEB128_SIZE /* tid */
-                       );
+               emit_event (thread->buffer, TYPE_END_UNLOAD | TYPE_METADATA);
+               emit_byte (thread->buffer, TYPE_THREAD);
+               emit_ptr (thread->buffer, (void *) thread->node.key);
+       }
 
-                       emit_event (thread->buffer, TYPE_END_UNLOAD | TYPE_METADATA);
-                       emit_byte (thread->buffer, TYPE_THREAD);
-                       emit_ptr (thread->buffer, (void *) thread->node.key);
-               }
+       send_buffer (thread);
+
+       free (thread);
+}
 
-               send_buffer (prof, thread);
+static void
+remove_thread (MonoProfilerThread *thread)
+{
+       MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
 
-               mono_thread_hazardous_try_free (thread, free);
-       }
+       if (mono_lls_remove (&profiler_thread_list, hp, &thread->node))
+               mono_thread_hazardous_try_free (thread, free_thread);
 
        clear_hazard_pointers (hp);
-
-       if (from_callback)
-               PROF_TLS_SET (NULL);
 }
 
 static void
@@ -1319,24 +1253,26 @@ dump_buffer (MonoProfiler *profiler, LogBuffer *buf)
        if (buf->next)
                dump_buffer (profiler, buf->next);
 
-       p = write_int32 (p, BUF_ID);
-       p = write_int32 (p, buf->cursor - buf->buf);
-       p = write_int64 (p, buf->time_base);
-       p = write_int64 (p, buf->ptr_base);
-       p = write_int64 (p, buf->obj_base);
-       p = write_int64 (p, buf->thread_id);
-       p = write_int64 (p, buf->method_base);
+       if (buf->cursor - buf->buf) {
+               p = write_int32 (p, BUF_ID);
+               p = write_int32 (p, buf->cursor - buf->buf);
+               p = write_int64 (p, buf->time_base);
+               p = write_int64 (p, buf->ptr_base);
+               p = write_int64 (p, buf->obj_base);
+               p = write_int64 (p, buf->thread_id);
+               p = write_int64 (p, buf->method_base);
 
 #if defined (HAVE_SYS_ZLIB)
-       if (profiler->gzfile) {
-               gzwrite (profiler->gzfile, hbuf, p - hbuf);
-               gzwrite (profiler->gzfile, buf->buf, buf->cursor - buf->buf);
-       } else
+               if (profiler->gzfile) {
+                       gzwrite (profiler->gzfile, hbuf, p - hbuf);
+                       gzwrite (profiler->gzfile, buf->buf, buf->cursor - buf->buf);
+               } else
 #endif
-       {
-               fwrite (hbuf, p - hbuf, 1, profiler->file);
-               fwrite (buf->buf, buf->cursor - buf->buf, 1, profiler->file);
-               fflush (profiler->file);
+               {
+                       fwrite (hbuf, p - hbuf, 1, profiler->file);
+                       fwrite (buf->buf, buf->cursor - buf->buf, 1, profiler->file);
+                       fflush (profiler->file);
+               }
        }
 
        free_buffer (buf, buf->size);
@@ -1352,75 +1288,49 @@ dump_buffer_threadless (MonoProfiler *profiler, LogBuffer *buf)
 }
 
 static void
-process_requests (MonoProfiler *profiler)
+process_requests (void)
 {
        if (heapshot_requested)
                mono_gc_collect (mono_gc_max_generation ());
 }
 
+// Avoid calling this directly if possible. Use the functions below.
 static void
-safe_send (MonoProfiler *profiler)
+send_log_unsafe (gboolean lock, gboolean if_needed)
 {
-       /* 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;
-
        MonoProfilerThread *thread = PROF_TLS_GET ();
 
-       buffer_lock ();
-
-       send_buffer (profiler, thread);
-       init_buffer_state (thread);
-
-       buffer_unlock ();
-}
-
-static void
-send_if_needed (MonoProfiler *prof)
-{
-       if (PROF_TLS_GET ()->buffer->next)
-               safe_send (prof);
-}
+       if (lock)
+               buffer_lock ();
 
-static void
-safe_send_threadless (MonoProfiler *prof)
-{
-       LogBuffer *buf = PROF_TLS_GET ()->buffer;
-
-       for (LogBuffer *iter = buf; iter; iter = iter->next)
-               iter->thread_id = 0;
+       if (!if_needed || (if_needed && thread->buffer->next)) {
+               if (!thread->attached)
+                       for (LogBuffer *iter = thread->buffer; iter; iter = iter->next)
+                               iter->thread_id = 0;
 
-       safe_send (prof);
-}
+               send_buffer (thread);
+               init_buffer_state (thread);
+       }
 
-static void
-send_if_needed_threadless (MonoProfiler *prof)
-{
-       if (PROF_TLS_GET ()->buffer->next)
-               safe_send_threadless (prof);
+       if (lock)
+               buffer_unlock ();
 }
 
 // Assumes that the exclusive lock is held.
 static void
-sync_point_flush (MonoProfiler *prof)
+sync_point_flush (void)
 {
        g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id () && "Why don't we hold the exclusive lock?");
 
        MONO_LLS_FOREACH_SAFE (&profiler_thread_list, MonoProfilerThread, thread) {
-               send_buffer (prof, thread);
+               send_buffer (thread);
                init_buffer_state (thread);
        } MONO_LLS_FOREACH_SAFE_END
 }
 
 // Assumes that the exclusive lock is held.
 static void
-sync_point_mark (MonoProfiler *prof, MonoProfilerSyncPointType type)
+sync_point_mark (MonoProfilerSyncPointType type)
 {
        g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id () && "Why don't we hold the exclusive lock?");
 
@@ -1432,28 +1342,17 @@ sync_point_mark (MonoProfiler *prof, MonoProfilerSyncPointType type)
        emit_event (logbuffer, TYPE_META | TYPE_SYNC_POINT);
        emit_byte (logbuffer, type);
 
-       EXIT_LOG;
+       EXIT_LOG_EXPLICIT (FALSE, FALSE);
 
-       switch (type) {
-       case SYNC_POINT_PERIODIC:
-               safe_send_threadless (prof);
-               break;
-       case SYNC_POINT_WORLD_STOP:
-       case SYNC_POINT_WORLD_START:
-               safe_send (prof);
-               break;
-       default:
-               g_assert_not_reached ();
-               break;
-       }
+       send_log_unsafe (FALSE, FALSE);
 }
 
 // Assumes that the exclusive lock is held.
 static void
-sync_point (MonoProfiler *prof, MonoProfilerSyncPointType type)
+sync_point (MonoProfilerSyncPointType type)
 {
-       sync_point_flush (prof);
-       sync_point_mark (prof, type);
+       sync_point_flush ();
+       sync_point_mark (type);
 }
 
 static int
@@ -1489,7 +1388,7 @@ gc_reference (MonoObject *obj, MonoClass *klass, uintptr_t size, uintptr_t num,
                emit_obj (logbuffer, refs [i]);
        }
 
-       EXIT_LOG;
+       EXIT_LOG_EXPLICIT (TRUE, FALSE);
 
        return 0;
 }
@@ -1510,7 +1409,7 @@ heap_walk (MonoProfiler *profiler)
 
        emit_event (logbuffer, TYPE_HEAP_START | TYPE_HEAP);
 
-       EXIT_LOG;
+       EXIT_LOG_EXPLICIT (TRUE, FALSE);
 
        mono_gc_walk_heap (0, gc_reference, NULL);
 
@@ -1520,7 +1419,7 @@ heap_walk (MonoProfiler *profiler)
 
        emit_event (logbuffer, TYPE_HEAP_END | TYPE_HEAP);
 
-       EXIT_LOG;
+       EXIT_LOG_EXPLICIT (TRUE, FALSE);
 }
 
 static void
@@ -1547,7 +1446,7 @@ gc_roots (MonoProfiler *prof, int num, void **objects, int *root_types, uintptr_
                emit_value (logbuffer, extra_info [i]);
        }
 
-       EXIT_LOG;
+       EXIT_LOG_EXPLICIT (TRUE, FALSE);
 }
 
 static void
@@ -1563,7 +1462,7 @@ gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation)
        emit_byte (logbuffer, ev);
        emit_byte (logbuffer, generation);
 
-       EXIT_LOG;
+       EXIT_LOG_EXPLICIT (FALSE, FALSE);
 
        switch (ev) {
        case MONO_GC_EVENT_START:
@@ -1596,7 +1495,7 @@ gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation)
                 * committed to the log file before any object move events
                 * that will be produced during this GC.
                 */
-               sync_point (profiler, SYNC_POINT_WORLD_STOP);
+               sync_point (SYNC_POINT_WORLD_STOP);
                break;
        case MONO_GC_EVENT_PRE_START_WORLD:
                if (do_heap_shot && do_heap_walk) {
@@ -1614,7 +1513,7 @@ gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation)
                 * object allocation events for certain addresses could come
                 * after the move events that made those addresses available.
                 */
-               sync_point_mark (profiler, SYNC_POINT_WORLD_START);
+               sync_point_mark (SYNC_POINT_WORLD_START);
 
                /*
                 * Finally, it is safe to allow other threads to write to
@@ -1638,7 +1537,7 @@ gc_resize (MonoProfiler *profiler, int64_t new_size)
        emit_event (logbuffer, TYPE_GC_RESIZE | TYPE_GC);
        emit_value (logbuffer, new_size);
 
-       EXIT_LOG;
+       EXIT_LOG_EXPLICIT (TRUE, FALSE);
 }
 
 // If you alter MAX_FRAMES, you may need to alter SAMPLE_BLOCK_SIZE too.
@@ -1690,14 +1589,14 @@ emit_bt (MonoProfiler *prof, LogBuffer *logbuffer, FrameData *data)
        //if (*p != data.count) {
        //      printf ("bad num frames enc at %d: %d -> %d\n", count, data.count, *p); printf ("frames end: %p->%p\n", p, logbuffer->cursor); exit(0);}
        while (data->count) {
-               emit_method (prof, logbuffer, data->methods [--data->count]);
+               emit_method (logbuffer, data->methods [--data->count]);
        }
 }
 
 static void
 gc_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
 {
-       init_thread (TRUE);
+       init_thread (prof, TRUE);
 
        int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces) ? TYPE_ALLOC_BT : 0;
        FrameData data;
@@ -1731,10 +1630,6 @@ gc_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
                emit_bt (prof, logbuffer, &data);
 
        EXIT_LOG;
-
-       send_if_needed (prof);
-
-       process_requests (prof);
 }
 
 static void
@@ -1754,7 +1649,7 @@ gc_moves (MonoProfiler *prof, void **objects, int num)
        for (int i = 0; i < num; ++i)
                emit_obj (logbuffer, objects [i]);
 
-       EXIT_LOG;
+       EXIT_LOG_EXPLICIT (TRUE, FALSE);
 }
 
 static void
@@ -1800,8 +1695,6 @@ gc_handle (MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *o
                emit_bt (prof, logbuffer, &data);
 
        EXIT_LOG;
-
-       process_requests (prof);
 }
 
 static void
@@ -1814,8 +1707,6 @@ finalize_begin (MonoProfiler *prof)
        emit_event (buf, TYPE_GC_FINALIZE_START | TYPE_GC);
 
        EXIT_LOG;
-
-       process_requests (prof);
 }
 
 static void
@@ -1828,8 +1719,6 @@ finalize_end (MonoProfiler *prof)
        emit_event (buf, TYPE_GC_FINALIZE_END | TYPE_GC);
 
        EXIT_LOG;
-
-       process_requests (prof);
 }
 
 static void
@@ -1844,8 +1733,6 @@ finalize_object_begin (MonoProfiler *prof, MonoObject *obj)
        emit_obj (buf, obj);
 
        EXIT_LOG;
-
-       process_requests (prof);
 }
 
 static void
@@ -1860,8 +1747,6 @@ finalize_object_end (MonoProfiler *prof, MonoObject *obj)
        emit_obj (buf, obj);
 
        EXIT_LOG;
-
-       process_requests (prof);
 }
 
 static char*
@@ -1923,10 +1808,6 @@ image_loaded (MonoProfiler *prof, MonoImage *image, int result)
        logbuffer->cursor += nlen;
 
        EXIT_LOG;
-
-       send_if_needed (prof);
-
-       process_requests (prof);
 }
 
 static void
@@ -1949,10 +1830,6 @@ image_unloaded (MonoProfiler *prof, MonoImage *image)
        logbuffer->cursor += nlen;
 
        EXIT_LOG;
-
-       send_if_needed (prof);
-
-       process_requests (prof);
 }
 
 static void
@@ -1980,10 +1857,6 @@ assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly, int result)
        EXIT_LOG;
 
        mono_free (name);
-
-       send_if_needed (prof);
-
-       process_requests (prof);
 }
 
 static void
@@ -2008,10 +1881,6 @@ assembly_unloaded (MonoProfiler *prof, MonoAssembly *assembly)
        EXIT_LOG;
 
        mono_free (name);
-
-       send_if_needed (prof);
-
-       process_requests (prof);
 }
 
 static void
@@ -2051,10 +1920,6 @@ class_loaded (MonoProfiler *prof, MonoClass *klass, int result)
                mono_free (name);
        else
                g_free (name);
-
-       send_if_needed (prof);
-
-       process_requests (prof);
 }
 
 static void
@@ -2091,22 +1956,14 @@ class_unloaded (MonoProfiler *prof, MonoClass *klass)
                mono_free (name);
        else
                g_free (name);
-
-       send_if_needed (prof);
-
-       process_requests (prof);
 }
 
-#ifndef DISABLE_HELPER_THREAD
 static void process_method_enter_coverage (MonoProfiler *prof, MonoMethod *method);
-#endif /* DISABLE_HELPER_THREAD */
 
 static void
 method_enter (MonoProfiler *prof, MonoMethod *method)
 {
-#ifndef DISABLE_HELPER_THREAD
        process_method_enter_coverage (prof, method);
-#endif /* DISABLE_HELPER_THREAD */
 
        if (PROF_TLS_GET ()->call_depth++ <= max_call_depth) {
                ENTER_LOG (&method_entries_ctr, logbuffer,
@@ -2115,14 +1972,10 @@ method_enter (MonoProfiler *prof, MonoMethod *method)
                );
 
                emit_event (logbuffer, TYPE_ENTER | TYPE_METHOD);
-               emit_method (prof, logbuffer, method);
+               emit_method (logbuffer, method);
 
                EXIT_LOG;
        }
-
-       send_if_needed (prof);
-
-       process_requests (prof);
 }
 
 static void
@@ -2135,14 +1988,10 @@ method_leave (MonoProfiler *prof, MonoMethod *method)
                );
 
                emit_event (logbuffer, TYPE_LEAVE | TYPE_METHOD);
-               emit_method (prof, logbuffer, method);
+               emit_method (logbuffer, method);
 
                EXIT_LOG;
        }
-
-       send_if_needed (prof);
-
-       process_requests (prof);
 }
 
 static void
@@ -2155,14 +2004,10 @@ method_exc_leave (MonoProfiler *prof, MonoMethod *method)
                );
 
                emit_event (logbuffer, TYPE_EXC_LEAVE | TYPE_METHOD);
-               emit_method (prof, logbuffer, method);
+               emit_method (logbuffer, method);
 
                EXIT_LOG;
        }
-
-       send_if_needed (prof);
-
-       process_requests (prof);
 }
 
 static void
@@ -2171,9 +2016,7 @@ method_jitted (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *ji, int resu
        if (result != MONO_PROFILE_OK)
                return;
 
-       register_method_local (prof, method, ji);
-
-       process_requests (prof);
+       register_method_local (method, ji);
 }
 
 static void
@@ -2211,8 +2054,6 @@ code_buffer_new (MonoProfiler *prof, void *buffer, int size, MonoProfilerCodeBuf
        }
 
        EXIT_LOG;
-
-       process_requests (prof);
 }
 
 static void
@@ -2242,8 +2083,6 @@ throw_exc (MonoProfiler *prof, MonoObject *object)
                emit_bt (prof, logbuffer, &data);
 
        EXIT_LOG;
-
-       process_requests (prof);
 }
 
 static void
@@ -2259,11 +2098,9 @@ clause_exc (MonoProfiler *prof, MonoMethod *method, int clause_type, int clause_
        emit_event (logbuffer, TYPE_EXCEPTION | TYPE_CLAUSE);
        emit_byte (logbuffer, clause_type);
        emit_value (logbuffer, clause_num);
-       emit_method (prof, logbuffer, method);
+       emit_method (logbuffer, method);
 
        EXIT_LOG;
-
-       process_requests (prof);
 }
 
 static void
@@ -2310,14 +2147,12 @@ monitor_event (MonoProfiler *profiler, MonoObject *object, MonoProfilerMonitorEv
                emit_bt (profiler, logbuffer, &data);
 
        EXIT_LOG;
-
-       process_requests (profiler);
 }
 
 static void
 thread_start (MonoProfiler *prof, uintptr_t tid)
 {
-       init_thread (TRUE);
+       init_thread (prof, TRUE);
 
        ENTER_LOG (&thread_starts_ctr, logbuffer,
                EVENT_SIZE /* event */ +
@@ -2330,10 +2165,6 @@ thread_start (MonoProfiler *prof, uintptr_t tid)
        emit_ptr (logbuffer, (void*) tid);
 
        EXIT_LOG;
-
-       send_if_needed (prof);
-
-       process_requests (prof);
 }
 
 static void
@@ -2349,11 +2180,14 @@ thread_end (MonoProfiler *prof, uintptr_t tid)
        emit_byte (logbuffer, TYPE_THREAD);
        emit_ptr (logbuffer, (void*) tid);
 
-       EXIT_LOG;
+       EXIT_LOG_EXPLICIT (FALSE, FALSE);
+
+       MonoProfilerThread *thread = PROF_TLS_GET ();
 
-       // Don't process requests as the thread is detached from the runtime.
+       thread->ended = TRUE;
+       remove_thread (thread);
 
-       remove_thread (prof, PROF_TLS_GET (), TRUE);
+       PROF_TLS_SET (NULL);
 }
 
 static void
@@ -2375,10 +2209,6 @@ thread_name (MonoProfiler *prof, uintptr_t tid, const char *name)
        logbuffer->cursor += len;
 
        EXIT_LOG;
-
-       send_if_needed (prof);
-
-       process_requests (prof);
 }
 
 static void
@@ -2398,10 +2228,6 @@ domain_loaded (MonoProfiler *prof, MonoDomain *domain, int result)
        emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
 
        EXIT_LOG;
-
-       send_if_needed (prof);
-
-       process_requests (prof);
 }
 
 static void
@@ -2418,10 +2244,6 @@ domain_unloaded (MonoProfiler *prof, MonoDomain *domain)
        emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
 
        EXIT_LOG;
-
-       send_if_needed (prof);
-
-       process_requests (prof);
 }
 
 static void
@@ -2443,10 +2265,6 @@ domain_name (MonoProfiler *prof, MonoDomain *domain, const char *name)
        logbuffer->cursor += nlen;
 
        EXIT_LOG;
-
-       send_if_needed (prof);
-
-       process_requests (prof);
 }
 
 static void
@@ -2465,10 +2283,6 @@ context_loaded (MonoProfiler *prof, MonoAppContext *context)
        emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_domain_id (context));
 
        EXIT_LOG;
-
-       send_if_needed (prof);
-
-       process_requests (prof);
 }
 
 static void
@@ -2487,10 +2301,6 @@ context_unloaded (MonoProfiler *prof, MonoAppContext *context)
        emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_domain_id (context));
 
        EXIT_LOG;
-
-       send_if_needed (prof);
-
-       process_requests (prof);
 }
 
 typedef struct {
@@ -2580,14 +2390,6 @@ mono_sample_hit (MonoProfiler *profiler, unsigned char *ip, void *context)
        sample->tid = thread_id ();
        sample->ip = ip;
 
-       if (do_debug) {
-               int len;
-               char buf [256];
-               snprintf (buf, sizeof (buf), "hit at %p in thread %p after %llu ms\n", ip, (void *) sample->tid, (unsigned long long int) ((sample->time - profiler->startup_time) / 10000 / 100));
-               len = strlen (buf);
-               ign_res (write (2, buf, len));
-       }
-
        mono_thread_hazardous_try_free (sample, enqueue_sample_hit);
 }
 
@@ -2648,7 +2450,7 @@ add_code_pointer (uintptr_t ip)
 //#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
 #if 0
 static void
-dump_ubin (const char *filename, uintptr_t load_addr, uint64_t offset, uintptr_t size)
+dump_ubin (MonoProfiler *prof, const char *filename, uintptr_t load_addr, uint64_t offset, uintptr_t size)
 {
        int len = strlen (filename) + 1;
 
@@ -2667,12 +2469,12 @@ dump_ubin (const char *filename, uintptr_t load_addr, uint64_t offset, uintptr_t
        memcpy (logbuffer->cursor, filename, len);
        logbuffer->cursor += len;
 
-       EXIT_LOG;
+       EXIT_LOG_EXPLICIT (TRUE, FALSE);
 }
 #endif
 
 static void
-dump_usym (const char *name, uintptr_t value, uintptr_t size)
+dump_usym (MonoProfiler *prof, const char *name, uintptr_t value, uintptr_t size)
 {
        int len = strlen (name) + 1;
 
@@ -2689,7 +2491,7 @@ dump_usym (const char *name, uintptr_t value, uintptr_t size)
        memcpy (logbuffer->cursor, name, len);
        logbuffer->cursor += len;
 
-       EXIT_LOG;
+       EXIT_LOG_EXPLICIT (TRUE, FALSE);
 }
 
 /* ELF code crashes on some systems. */
@@ -2708,7 +2510,7 @@ dump_usym (const char *name, uintptr_t value, uintptr_t size)
 #endif
 
 static void
-dump_elf_symbols (ElfW(Sym) *symbols, int num_symbols, const char *strtab, void *load_addr)
+dump_elf_symbols (MonoProfiler *prof, ElfW(Sym) *symbols, int num_symbols, const char *strtab, void *load_addr)
 {
        int i;
        for (i = 0; i < num_symbols; ++i) {
@@ -2831,7 +2633,7 @@ elf_dl_callback (struct dl_phdr_info *info, size_t size, void *data)
                                        header->e_ident [EI_MAG3] != ELFMAG3 ) {
                                header = NULL;
                        }
-                       dump_ubin (filename, info->dlpi_addr + info->dlpi_phdr[i].p_vaddr, info->dlpi_phdr[i].p_offset, info->dlpi_phdr[i].p_memsz);
+                       dump_ubin (prof, filename, info->dlpi_addr + info->dlpi_phdr[i].p_vaddr, info->dlpi_phdr[i].p_offset, info->dlpi_phdr[i].p_memsz);
                } else if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) {
                        dyn = (ElfW(Dyn) *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
                }
@@ -2856,7 +2658,7 @@ elf_dl_callback (struct dl_phdr_info *info, size_t size, void *data)
        if (!hash_table)
                return 0;
        num_sym = hash_table [1];
-       dump_elf_symbols (symtab, num_sym, strtab, (void*)info->dlpi_addr);
+       dump_elf_symbols (prof, symtab, num_sym, strtab, (void*)info->dlpi_addr);
        return 0;
 }
 
@@ -2922,7 +2724,7 @@ dump_unmanaged_coderefs (MonoProfiler *prof)
                        last_symbol = sym;
                        if (!sym)
                                continue;
-                       dump_usym (sym, addr, 0); /* let's not guess the size */
+                       dump_usym (prof, sym, addr, 0); /* let's not guess the size */
                        //printf ("found symbol at %p: %s\n", (void*)addr, sym);
                }
        }
@@ -3085,6 +2887,8 @@ dump_perf_hits (MonoProfiler *prof, void *buf, int size)
                printf ("sample: %d, size: %d, ip: %p (%s), timestamp: %llu, nframes: %llu\n",
                        s->h.type, s->h.size, ip, symbol_for (ip), s->timestamp, s->nframes);*/
 
+               InterlockedIncrement (&sample_hits_ctr);
+
                ENTER_LOG (&sample_hits_ctr, logbuffer,
                        EVENT_SIZE /* event */ +
                        BYTE_SIZE /* type */ +
@@ -3112,7 +2916,7 @@ dump_perf_hits (MonoProfiler *prof, void *buf, int size)
                /* no support here yet for the managed backtrace */
                emit_uvalue (logbuffer, mbt_count);
 
-               EXIT_LOG;
+               EXIT_LOG_EXPLICIT (TRUE, FALSE);
 
                add_code_pointer (s->ip);
                buf = (char*)buf + s->h.size;
@@ -3226,8 +3030,6 @@ setup_perf_event (void)
 
 #endif /* USE_PERF_EVENTS */
 
-#ifndef DISABLE_HELPER_THREAD
-
 typedef struct MonoCounterAgent {
        MonoCounter *counter;
        // MonoCounterAgent specific data :
@@ -3353,7 +3155,7 @@ counters_emit (MonoProfiler *profiler)
                agent->emitted = 1;
        }
 
-       EXIT_LOG;
+       EXIT_LOG_EXPLICIT (TRUE, FALSE);
 
 done:
        mono_os_mutex_unlock (&counters_mutex);
@@ -3463,7 +3265,7 @@ counters_sample (MonoProfiler *profiler, uint64_t timestamp)
                        }
                        break;
                default:
-                       assert (0);
+                       g_assert_not_reached ();
                }
 
                if (type == MONO_COUNTER_STRING && size > agent->value_size) {
@@ -3478,7 +3280,7 @@ counters_sample (MonoProfiler *profiler, uint64_t timestamp)
 
        emit_value (logbuffer, 0);
 
-       EXIT_LOG;
+       EXIT_LOG_EXPLICIT (TRUE, FALSE);
 
        mono_os_mutex_unlock (&counters_mutex);
 }
@@ -3548,7 +3350,7 @@ perfcounters_emit (MonoProfiler *profiler)
                pcagent->emitted = 1;
        }
 
-       EXIT_LOG;
+       EXIT_LOG_EXPLICIT (TRUE, FALSE);
 }
 
 static gboolean
@@ -3641,7 +3443,7 @@ perfcounters_sample (MonoProfiler *profiler, uint64_t timestamp)
 
        emit_value (logbuffer, 0);
 
-       EXIT_LOG;
+       EXIT_LOG_EXPLICIT (TRUE, FALSE);
 
 done:
        mono_os_mutex_unlock (&counters_mutex);
@@ -3814,9 +3616,7 @@ build_method_buffer (gpointer key, gpointer value, gpointer userdata)
        emit_uvalue (logbuffer, method_id);
        emit_value (logbuffer, coverage_data->len);
 
-       EXIT_LOG;
-
-       send_if_needed (prof);
+       EXIT_LOG_EXPLICIT (TRUE, FALSE);
 
        for (i = 0; i < coverage_data->len; i++) {
                CoverageEntry *entry = (CoverageEntry *)coverage_data->pdata[i];
@@ -3837,9 +3637,7 @@ build_method_buffer (gpointer key, gpointer value, gpointer userdata)
                emit_uvalue (logbuffer, entry->line);
                emit_uvalue (logbuffer, entry->column);
 
-               EXIT_LOG;
-
-               send_if_needed (prof);
+               EXIT_LOG_EXPLICIT (TRUE, FALSE);
        }
 
        method_id++;
@@ -3871,7 +3669,6 @@ 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;
@@ -3904,9 +3701,7 @@ build_class_buffer (gpointer key, gpointer value, gpointer userdata)
        emit_uvalue (logbuffer, fully_covered);
        emit_uvalue (logbuffer, partially_covered);
 
-       EXIT_LOG;
-
-       send_if_needed (prof);
+       EXIT_LOG_EXPLICIT (TRUE, FALSE);
 
        g_free (class_name);
 }
@@ -3930,7 +3725,6 @@ 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);
        const char *name, *guid, *filename;
        int number_of_methods = 0, partially_covered = 0;
@@ -3964,9 +3758,7 @@ build_assembly_buffer (gpointer key, gpointer value, gpointer userdata)
        emit_uvalue (logbuffer, fully_covered);
        emit_uvalue (logbuffer, partially_covered);
 
-       EXIT_LOG;
-
-       send_if_needed (prof);
+       EXIT_LOG_EXPLICIT (TRUE, FALSE);
 }
 
 static void
@@ -3979,8 +3771,8 @@ dump_coverage (MonoProfiler *prof)
        method_id = 0;
 
        mono_os_mutex_lock (&coverage_mutex);
-       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_assemblies, build_assembly_buffer, NULL);
+       mono_conc_hashtable_foreach (coverage_classes, build_class_buffer, NULL);
        mono_conc_hashtable_foreach (coverage_methods, build_method_buffer, prof);
        mono_os_mutex_unlock (&coverage_mutex);
 
@@ -4031,8 +3823,7 @@ coverage_filter (MonoProfiler *prof, MonoMethod *method)
        MonoLockFreeQueue *image_methods, *class_methods;
        MonoLockFreeQueueNode *node;
 
-       if (!coverage_initialized)
-               return FALSE;
+       g_assert (coverage_initialized && "Why are we being asked for coverage filter info when we're not doing coverage?");
 
        COVERAGE_DEBUG(fprintf (stderr, "Coverage filter for %s\n", mono_method_get_name (method));)
 
@@ -4261,13 +4052,10 @@ init_suppressed_assemblies (void)
        fclose (sa_file);
 }
 
-#endif /* DISABLE_HELPER_THREAD */
-
 static void
 coverage_init (MonoProfiler *prof)
 {
-#ifndef DISABLE_HELPER_THREAD
-       assert (!coverage_initialized);
+       g_assert (!coverage_initialized && "Why are we initializing coverage twice?");
 
        COVERAGE_DEBUG(fprintf (stderr, "Coverage initialized\n");)
 
@@ -4281,7 +4069,6 @@ coverage_init (MonoProfiler *prof)
        init_suppressed_assemblies ();
 
        coverage_initialized = TRUE;
-#endif /* DISABLE_HELPER_THREAD */
 }
 
 static void
@@ -4312,17 +4099,20 @@ 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;
-               ign_res (write (prof->pipes [1], &c, 1));
-               pthread_join (prof->helper_thread, &res);
+       char c = 1;
+
+       if (write (prof->pipes [1], &c, 1) != 1) {
+               fprintf (stderr, "Could not write to pipe: %s\n", strerror (errno));
+               exit (1);
        }
 
+       pthread_join (prof->helper_thread, &res);
+
        mono_os_mutex_destroy (&counters_mutex);
 
        MonoCounterAgent *mc_next;
@@ -4338,7 +4128,7 @@ log_shutdown (MonoProfiler *prof)
                pc_next = cur->next;
                g_free (cur);
        }
-#endif
+
 #if USE_PERF_EVENTS
        if (perf_data) {
                int i;
@@ -4354,10 +4144,16 @@ log_shutdown (MonoProfiler *prof)
         */
        while (profiler_thread_list.head) {
                MONO_LLS_FOREACH_SAFE (&profiler_thread_list, MonoProfilerThread, thread) {
-                       remove_thread (prof, thread, FALSE);
+                       remove_thread (thread);
                } MONO_LLS_FOREACH_SAFE_END
        }
 
+       /*
+        * Ensure that all threads have been freed, so that we don't miss any
+        * buffers when we shut down the writer thread below.
+        */
+       mono_thread_hazardous_try_free_all ();
+
        InterlockedWrite (&prof->run_dumper_thread, 0);
        mono_os_sem_post (&prof->dumper_queue_sem);
        pthread_join (prof->dumper_thread, &res);
@@ -4368,14 +4164,19 @@ log_shutdown (MonoProfiler *prof)
        pthread_join (prof->writer_thread, &res);
        mono_os_sem_destroy (&prof->writer_queue_sem);
 
+       /*
+        * Free all writer queue entries, and ensure that all sample hits will be
+        * added to the sample reuse queue.
+        */
+       mono_thread_hazardous_try_free_all ();
+
        cleanup_reusable_samples (prof);
 
        /*
-        * Pump the entire hazard free queue to make sure that anything we allocated
-        * in the profiler will be freed. If we don't do this, the runtime could get
-        * around to freeing some items after the profiler has been unloaded, which
-        * would mean calling into functions in the profiler library, leading to a
-        * crash.
+        * Finally, make sure that all sample hits are freed. This should cover all
+        * hazardous data from the profiler. We can now be sure that the runtime
+        * won't later invoke free functions in the profiler library after it has
+        * been unloaded.
         */
        mono_thread_hazardous_try_free_all ();
 
@@ -4475,11 +4276,6 @@ new_filename (const char* filename)
        return res;
 }
 
-//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)
 {
@@ -4491,7 +4287,7 @@ helper_thread (void* arg)
        mono_threads_attach_tools_thread ();
        mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler helper");
 
-       init_thread (FALSE);
+       MonoProfilerThread *thread = init_thread (prof, FALSE);
 
        //fprintf (stderr, "Server listening\n");
        command_socket = -1;
@@ -4527,7 +4323,7 @@ helper_thread (void* arg)
 
                buffer_lock_excl ();
 
-               sync_point (prof, SYNC_POINT_PERIODIC);
+               sync_point (SYNC_POINT_PERIODIC);
 
                buffer_unlock_excl ();
 
@@ -4548,6 +4344,7 @@ helper_thread (void* arg)
                        read (prof->pipes [0], &c, 1);
                        if (do_debug)
                                fprintf (stderr, "helper shutdown\n");
+
 #if USE_PERF_EVENTS
                        if (perf_data) {
                                int i;
@@ -4559,7 +4356,8 @@ helper_thread (void* arg)
                                }
                        }
 #endif
-                       safe_send_threadless (prof);
+
+                       send_log_unsafe (FALSE, FALSE);
                        return NULL;
                }
 #if USE_PERF_EVENTS
@@ -4568,10 +4366,8 @@ helper_thread (void* arg)
                        for ( i = 0; i < num_perf; ++i) {
                                if (perf_data [i].perf_fd < 0)
                                        continue;
-                               if (FD_ISSET (perf_data [i].perf_fd, &rfds)) {
+                               if (FD_ISSET (perf_data [i].perf_fd, &rfds))
                                        read_perf_mmap (prof, i);
-                                       send_if_needed_threadless (prof);
-                               }
                        }
                }
 #endif
@@ -4601,54 +4397,65 @@ helper_thread (void* arg)
                //fprintf (stderr, "Accepted connection\n");
        }
 
+       deinit_thread (thread);
+
        mono_thread_info_detach ();
 
        return NULL;
 }
 
-static int
+static void
 start_helper_thread (MonoProfiler* prof)
 {
-       struct sockaddr_in server_address;
-       int r;
-       socklen_t slen;
-       if (pipe (prof->pipes) < 0) {
-               fprintf (stderr, "Cannot create pipe\n");
-               return 0;
+       if (pipe (prof->pipes) == -1) {
+               fprintf (stderr, "Cannot create pipe: %s\n", strerror (errno));
+               exit (1);
        }
+
        prof->server_socket = socket (PF_INET, SOCK_STREAM, 0);
-       if (prof->server_socket < 0) {
-               fprintf (stderr, "Cannot create server socket\n");
-               return 0;
+
+       if (prof->server_socket == -1) {
+               fprintf (stderr, "Cannot create server socket: %s\n", strerror (errno));
+               exit (1);
        }
+
+       struct sockaddr_in server_address;
+
        memset (&server_address, 0, sizeof (server_address));
        server_address.sin_family = AF_INET;
        server_address.sin_addr.s_addr = INADDR_ANY;
        server_address.sin_port = htons (prof->command_port);
-       if (bind (prof->server_socket, (struct sockaddr *) &server_address, sizeof (server_address)) < 0) {
-               fprintf (stderr, "Cannot bind server socket, port: %d: %s\n", prof->command_port, strerror (errno));
+
+       if (bind (prof->server_socket, (struct sockaddr *) &server_address, sizeof (server_address)) == -1) {
+               fprintf (stderr, "Cannot bind server socket on port %d: %s\n", prof->command_port, strerror (errno));
                close (prof->server_socket);
-               return 0;
+               exit (1);
        }
-       if (listen (prof->server_socket, 1) < 0) {
-               fprintf (stderr, "Cannot listen server socket\n");
+
+       if (listen (prof->server_socket, 1) == -1) {
+               fprintf (stderr, "Cannot listen on server socket: %s\n", strerror (errno));
                close (prof->server_socket);
-               return 0;
+               exit (1);
        }
-       slen = sizeof (server_address);
-       if (getsockname (prof->server_socket, (struct sockaddr *)&server_address, &slen) == 0) {
-               prof->command_port = ntohs (server_address.sin_port);
-               /*fprintf (stderr, "Assigned server port: %d\n", prof->command_port);*/
+
+       socklen_t slen = sizeof (server_address);
+
+       if (getsockname (prof->server_socket, (struct sockaddr *)&server_address, &slen)) {
+               fprintf (stderr, "Could not get assigned port: %s\n", strerror (errno));
+               close (prof->server_socket);
+               exit (1);
        }
 
-       r = pthread_create (&prof->helper_thread, NULL, helper_thread, prof);
-       if (r) {
+       prof->command_port = ntohs (server_address.sin_port);
+
+       int r;
+
+       if ((r = pthread_create (&prof->helper_thread, NULL, helper_thread, prof))) {
+               fprintf (stderr, "Could not start helper thread: %s\n", strerror (r));
                close (prof->server_socket);
-               return 0;
+               exit (1);
        }
-       return 1;
 }
-#endif
 
 static void
 free_writer_entry (gpointer p)
@@ -4665,7 +4472,7 @@ handle_writer_queue_entry (MonoProfiler *prof)
                if (!entry->methods)
                        goto no_methods;
 
-               LogBuffer *buf = NULL;
+               gboolean wrote_methods = FALSE;
 
                /*
                 * Encode the method events in a temporary log buffer that we
@@ -4703,9 +4510,7 @@ handle_writer_queue_entry (MonoProfiler *prof)
                        void *cstart = info->ji ? mono_jit_info_get_code_start (info->ji) : NULL;
                        int csize = info->ji ? mono_jit_info_get_code_size (info->ji) : 0;
 
-                       InterlockedIncrement (&method_jits_ctr);
-
-                       buf = ensure_logbuf_unsafe (
+                       ENTER_LOG (&method_jits_ctr, logbuffer,
                                EVENT_SIZE /* event */ +
                                LEB128_SIZE /* method */ +
                                LEB128_SIZE /* start */ +
@@ -4713,24 +4518,28 @@ handle_writer_queue_entry (MonoProfiler *prof)
                                nlen /* name */
                        );
 
-                       emit_event_time (buf, TYPE_JIT | TYPE_METHOD, info->time);
-                       emit_method_inner (buf, info->method);
-                       emit_ptr (buf, cstart);
-                       emit_value (buf, csize);
+                       emit_event_time (logbuffer, TYPE_JIT | TYPE_METHOD, info->time);
+                       emit_method_inner (logbuffer, info->method);
+                       emit_ptr (logbuffer, cstart);
+                       emit_value (logbuffer, csize);
+
+                       memcpy (logbuffer->cursor, name, nlen);
+                       logbuffer->cursor += nlen;
 
-                       memcpy (buf->cursor, name, nlen);
-                       buf->cursor += nlen;
+                       EXIT_LOG_EXPLICIT (FALSE, FALSE);
 
                        mono_free (name);
 
+                       wrote_methods = TRUE;
+
                free_info:
                        g_free (info);
                }
 
                g_ptr_array_free (entry->methods, TRUE);
 
-               if (buf) {
-                       dump_buffer_threadless (prof, buf);
+               if (wrote_methods) {
+                       dump_buffer_threadless (prof, PROF_TLS_GET ()->buffer);
                        init_buffer_state (PROF_TLS_GET ());
                }
 
@@ -4755,7 +4564,7 @@ writer_thread (void *arg)
 
        dump_header (prof);
 
-       MonoProfilerThread *thread = init_thread (FALSE);
+       MonoProfilerThread *thread = init_thread (prof, FALSE);
 
        while (InterlockedRead (&prof->run_writer_thread)) {
                mono_os_sem_wait (&prof->writer_queue_sem, MONO_SEM_FLAGS_NONE);
@@ -4773,12 +4582,17 @@ writer_thread (void *arg)
        return NULL;
 }
 
-static int
+static void
 start_writer_thread (MonoProfiler* prof)
 {
        InterlockedWrite (&prof->run_writer_thread, 1);
 
-       return !pthread_create (&prof->writer_thread, NULL, writer_thread, prof);
+       int r;
+
+       if ((r = pthread_create (&prof->writer_thread, NULL, writer_thread, prof))) {
+               fprintf (stderr, "Could not start writer thread: %s\n", strerror (r));
+               exit (1);
+       }
 }
 
 static void
@@ -4812,9 +4626,7 @@ handle_dumper_queue_entry (MonoProfiler *prof)
                        }
                }
 
-               InterlockedIncrement (&sample_hits_ctr);
-
-               LogBuffer *logbuffer = ensure_logbuf_unsafe (
+               ENTER_LOG (&sample_hits_ctr, logbuffer,
                        EVENT_SIZE /* event */ +
                        BYTE_SIZE /* type */ +
                        LEB128_SIZE /* tid */ +
@@ -4843,13 +4655,13 @@ handle_dumper_queue_entry (MonoProfiler *prof)
                emit_uvalue (logbuffer, sample->count);
 
                for (int i = 0; i < sample->count; ++i)
-                       emit_method (prof, logbuffer, sample->frames [i].method);
+                       emit_method (logbuffer, sample->frames [i].method);
+
+               EXIT_LOG_EXPLICIT (TRUE, FALSE);
 
                mono_thread_hazardous_try_free (sample, reuse_sample_hit);
 
                dump_unmanaged_coderefs (prof);
-
-               send_if_needed_threadless (prof);
        }
 
        return FALSE;
@@ -4863,7 +4675,7 @@ dumper_thread (void *arg)
        mono_threads_attach_tools_thread ();
        mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler dumper");
 
-       MonoProfilerThread *thread = init_thread (FALSE);
+       MonoProfilerThread *thread = init_thread (prof, FALSE);
 
        while (InterlockedRead (&prof->run_dumper_thread)) {
                mono_os_sem_wait (&prof->dumper_queue_sem, MONO_SEM_FLAGS_NONE);
@@ -4873,7 +4685,7 @@ dumper_thread (void *arg)
        /* Drain any remaining entries on shutdown. */
        while (handle_dumper_queue_entry (prof));
 
-       safe_send_threadless (prof);
+       send_log_unsafe (FALSE, FALSE);
        deinit_thread (thread);
 
        mono_thread_info_detach ();
@@ -4881,12 +4693,17 @@ dumper_thread (void *arg)
        return NULL;
 }
 
-static int
+static void
 start_dumper_thread (MonoProfiler* prof)
 {
        InterlockedWrite (&prof->run_dumper_thread, 1);
 
-       return !pthread_create (&prof->dumper_thread, NULL, dumper_thread, prof);
+       int r;
+
+       if ((r = pthread_create (&prof->dumper_thread, NULL, dumper_thread, prof))) {
+               fprintf (stderr, "Could not start dumper thread: %s\n", strerror (r));
+               exit (1);
+       }
 }
 
 static void
@@ -4957,17 +4774,9 @@ runtime_initialized (MonoProfiler *profiler)
        register_counter ("Event: Coverage classes", &coverage_classes_ctr);
        register_counter ("Event: Coverage assemblies", &coverage_assemblies_ctr);
 
-#ifndef DISABLE_HELPER_THREAD
        counters_init (profiler);
 
-       if (hs_mode_ondemand || need_helper_thread) {
-               if (!start_helper_thread (profiler))
-                       profiler->command_port = 0;
-       }
-#endif
-
-       /* ensure the main thread data and startup are available soon */
-       safe_send (profiler);
+       start_helper_thread (profiler);
 }
 
 static MonoProfiler*
@@ -5015,24 +4824,20 @@ create_profiler (const char *args, const char *filename, GPtrArray *filters)
                fprintf (stderr, "Cannot create profiler output: %s\n", nf);
                exit (1);
        }
+
 #if defined (HAVE_SYS_ZLIB)
        if (use_zip)
                prof->gzfile = gzdopen (fileno (prof->file), "wb");
 #endif
+
 #if USE_PERF_EVENTS
-       if (sample_type && sample_freq && !do_mono_sample)
-               need_helper_thread = setup_perf_event ();
+       setup_perf_event ();
+
        if (!perf_data) {
                /* FIXME: warn if different freq or sample type */
                do_mono_sample = 1;
        }
 #endif
-       if (do_mono_sample) {
-               need_helper_thread = 1;
-       }
-       if (do_counters && !need_helper_thread) {
-               need_helper_thread = 1;
-       }
 
        /*
         * If you hit this assert while increasing MAX_FRAMES, you need to increase
@@ -5046,15 +4851,6 @@ create_profiler (const char *args, const char *filename, GPtrArray *filters)
 
        mono_lock_free_queue_init (&prof->sample_reuse_queue);
 
-#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
-
        g_assert (sizeof (WriterQueueEntry) * 2 < LOCK_FREE_ALLOC_SB_USABLE_SIZE (WRITER_ENTRY_BLOCK_SIZE));
 
        // FIXME: We should free this stuff too.
@@ -5456,7 +5252,7 @@ mono_profiler_startup (const char *desc)
 
        mono_lls_init (&profiler_thread_list, NULL);
 
-       init_thread (TRUE);
+       init_thread (prof, TRUE);
 
        mono_profiler_install (prof, log_shutdown);
        mono_profiler_install_gc (gc_event, gc_resize);