Remove the mono-profiler- prefix from all source files in the profiler subdir.
authorRodrigo Kumpera <kumpera@gmail.com>
Fri, 9 Jun 2017 20:24:20 +0000 (13:24 -0700)
committerRodrigo Kumpera <kumpera@gmail.com>
Fri, 9 Jun 2017 20:58:15 +0000 (13:58 -0700)
Additionally, rename arg-parsing.c to log-args.c so it's clear it's part of the log profiler.

17 files changed:
mono/mini/aot-compiler.c
mono/profiler/Makefile.am
mono/profiler/aot.c [new file with mode: 0644]
mono/profiler/aot.h [new file with mode: 0644]
mono/profiler/arg-parsing.c [deleted file]
mono/profiler/iomap.c [new file with mode: 0644]
mono/profiler/log-args.c [new file with mode: 0644]
mono/profiler/log.c [new file with mode: 0644]
mono/profiler/log.h [new file with mode: 0644]
mono/profiler/mono-profiler-aot.c [deleted file]
mono/profiler/mono-profiler-aot.h [deleted file]
mono/profiler/mono-profiler-iomap.c [deleted file]
mono/profiler/mono-profiler-log.c [deleted file]
mono/profiler/mono-profiler-log.h [deleted file]
mono/profiler/mono-profiler-vtune.c [deleted file]
mono/profiler/mprof-report.c
mono/profiler/vtune.c [new file with mode: 0644]

index 385a4d43dd1622f6ab21ae5f344c0fa4457ced88..626473c8d0b846c2f271127a88be0c10ad5cd1f2 100644 (file)
@@ -56,7 +56,7 @@
 #include <mono/utils/mono-rand.h>
 #include <mono/utils/json.h>
 #include <mono/utils/mono-threads-coop.h>
-#include <mono/profiler/mono-profiler-aot.h>
+#include <mono/profiler/aot.h>
 #include <mono/utils/w32api.h>
 
 #include "aot-compiler.h"
index 31812af5884fc0803b308127e3884b9ace47cbba..f962de50f3a4bce1af1b243626fe27253982edab 100644 (file)
@@ -67,30 +67,30 @@ monodir=$(top_builddir)
 # also uses eglib (e.g. the runtime). Automake doesn't support this
 # functionality, so create a separate static version of the library.
 
-libmono_profiler_aot_la_SOURCES = mono-profiler-aot.c
+libmono_profiler_aot_la_SOURCES = aot.c
 libmono_profiler_aot_la_LIBADD =  $(libmono_dep) $(GLIB_LIBS) $(LIBICONV)
 libmono_profiler_aot_la_LDFLAGS = $(prof_ldflags)
-libmono_profiler_aot_static_la_SOURCES = mono-profiler-aot.c
+libmono_profiler_aot_static_la_SOURCES = aot.c
 libmono_profiler_aot_static_la_LDFLAGS = -static
 
-libmono_profiler_iomap_la_SOURCES = mono-profiler-iomap.c
+libmono_profiler_iomap_la_SOURCES = iomap.c
 libmono_profiler_iomap_la_LIBADD = $(libmono_dep) $(GLIB_LIBS) $(LIBICONV)
 libmono_profiler_iomap_la_LDFLAGS = $(prof_ldflags)
-libmono_profiler_iomap_static_la_SOURCES = mono-profiler-iomap.c
+libmono_profiler_iomap_static_la_SOURCES = iomap.c
 libmono_profiler_iomap_static_la_LDFLAGS = -static
 
-libmono_profiler_log_la_SOURCES = mono-profiler-log.c arg-parsing.c
+libmono_profiler_log_la_SOURCES = log.c log-args.c
 libmono_profiler_log_la_LIBADD = $(libmono_dep) $(GLIB_LIBS) $(Z_LIBS)
 libmono_profiler_log_la_LDFLAGS = $(prof_ldflags)
-libmono_profiler_log_static_la_SOURCES = mono-profiler-log.c arg-parsing.c
+libmono_profiler_log_static_la_SOURCES = log.c log-args.c
 libmono_profiler_log_static_la_LDFLAGS = -static
 
 if HAVE_VTUNE
-libmono_profiler_vtune_la_SOURCES = mono-profiler-vtune.c
+libmono_profiler_vtune_la_SOURCES = vtune.c
 libmono_profiler_vtune_la_CFLAGS = $(VTUNE_CFLAGS)
 libmono_profiler_vtune_la_LIBADD = $(VTUNE_LIBS) $(LIBMONO) $(GLIB_LIBS) $(LIBICONV)
 libmono_profiler_vtune_la_LDFLAGS = $(prof_ldflags)
-libmono_profiler_vtune_static_la_SOURCES = mono-profiler-vtune.c
+libmono_profiler_vtune_static_la_SOURCES = vtune.c
 libmono_profiler_vtune_static_la_LDFLAGS = -static
 libmono_profiler_vtune_static_la_CFLAGS = $(VTUNE_CFLAGS)
 libmono_profiler_vtune_static_la_LIBADD = $(VTUNE_LIBS)
@@ -116,8 +116,8 @@ testlog: $(PLOG_TESTS)
 
 check-local: $(check_targets)
 
-EXTRA_DIST=mono-profiler-log.h \
-       mono-profiler-aot.h \
+EXTRA_DIST=log.h \
+       aot.h \
        $(PLOG_TESTS_SRC) \
        ptestrunner.pl \
        $(suppression_DATA)
diff --git a/mono/profiler/aot.c b/mono/profiler/aot.c
new file mode 100644 (file)
index 0000000..b5c2d47
--- /dev/null
@@ -0,0 +1,413 @@
+/*
+ * mono-profiler-aot.c: Ahead of Time Compiler Profiler for Mono.
+ *
+ *
+ * Copyright 2008-2009 Novell, Inc (http://www.novell.com)
+ *
+ * This profiler collects profiling information usable by the Mono AOT compiler
+ * to generate better code. It saves the information into files under ~/.mono. 
+ * The AOT compiler can load these files during compilation.
+ * Currently, only the order in which methods were compiled is saved, 
+ * allowing more efficient function ordering in the AOT files.
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
+ */
+
+#include <config.h>
+
+#include "aot.h"
+
+#include <mono/metadata/profiler.h>
+#include <mono/metadata/tokentype.h>
+#include <mono/metadata/tabledefs.h>
+#include <mono/metadata/debug-helpers.h>
+#include <mono/metadata/assembly.h>
+#include <mono/metadata/class-internals.h>
+#include <mono/utils/mono-os-mutex.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <sys/stat.h>
+
+#ifdef HOST_WIN32
+#include <direct.h>
+#endif
+
+struct _MonoProfiler {
+       GHashTable *classes;
+       GHashTable *images;
+       GPtrArray *methods;
+       FILE *outfile;
+       int id;
+       char *outfile_name;
+};
+
+static mono_mutex_t mutex;
+static gboolean verbose;
+
+static void
+prof_jit_enter (MonoProfiler *prof, MonoMethod *method)
+{
+}
+
+static void
+prof_jit_leave (MonoProfiler *prof, MonoMethod *method, int result)
+{
+       MonoImage *image = mono_class_get_image (mono_method_get_class (method));
+
+       if (!image->assembly || method->wrapper_type)
+               return;
+
+       mono_os_mutex_lock (&mutex);
+       g_ptr_array_add (prof->methods, method);
+       mono_os_mutex_unlock (&mutex);
+}
+
+static void
+prof_shutdown (MonoProfiler *prof);
+
+static void
+usage (int do_exit)
+{
+       printf ("AOT profiler.\n");
+       printf ("Usage: mono --profile=aot[:OPTION1[,OPTION2...]] program.exe\n");
+       printf ("Options:\n");
+       printf ("\thelp                 show this usage info\n");
+       printf ("\toutput=FILENAME      write the data to file FILENAME (required)\n");
+       printf ("\tverbose              print diagnostic info\n");
+       if (do_exit)
+               exit (1);
+}
+
+static const char*
+match_option (const char* p, const char *opt, char **rval)
+{
+       int len = strlen (opt);
+       if (strncmp (p, opt, len) == 0) {
+               if (rval) {
+                       if (p [len] == '=' && p [len + 1]) {
+                               const char *opt = p + len + 1;
+                               const char *end = strchr (opt, ',');
+                               char *val;
+                               int l;
+                               if (end == NULL) {
+                                       l = strlen (opt);
+                               } else {
+                                       l = end - opt;
+                               }
+                               val = (char *) g_malloc (l + 1);
+                               memcpy (val, opt, l);
+                               val [l] = 0;
+                               *rval = val;
+                               return opt + l;
+                       }
+                       if (p [len] == 0 || p [len] == ',') {
+                               *rval = NULL;
+                               return p + len + (p [len] == ',');
+                       }
+                       usage (1);
+               } else {
+                       if (p [len] == 0)
+                               return p + len;
+                       if (p [len] == ',')
+                               return p + len + 1;
+               }
+       }
+       return p;
+}
+
+void
+mono_profiler_startup (const char *desc);
+
+/**
+ * mono_profiler_startup:
+ * the entry point
+ */
+void
+mono_profiler_startup (const char *desc)
+{
+       MonoProfiler *prof;
+       const char *p;
+       const char *opt;
+       char *outfile_name;
+
+       p = desc;
+       if (strncmp (p, "aot", 3))
+               usage (1);
+       p += 3;
+       if (*p == ':')
+               p++;
+       for (; *p; p = opt) {
+               char *val;
+               if (*p == ',') {
+                       opt = p + 1;
+                       continue;
+               }
+               if ((opt = match_option (p, "help", NULL)) != p) {
+                       usage (0);
+                       continue;
+               }
+               if ((opt = match_option (p, "verbose", NULL)) != p) {
+                       verbose = TRUE;
+                       continue;
+               }
+               if ((opt = match_option (p, "output", &val)) != p) {
+                       outfile_name = val;
+                       continue;
+               }
+               fprintf (stderr, "mono-profiler-aot: Unknown option: '%s'.\n", p);
+               exit (1);
+       }
+
+       if (!outfile_name) {
+               fprintf (stderr, "mono-profiler-aot: The 'output' argument is required.\n");
+               exit (1);
+       }
+
+       prof = g_new0 (MonoProfiler, 1);
+       prof->images = g_hash_table_new (NULL, NULL);
+       prof->classes = g_hash_table_new (NULL, NULL);
+       prof->methods = g_ptr_array_new ();
+       prof->outfile_name = outfile_name;
+
+       mono_os_mutex_init (&mutex);
+
+       mono_profiler_install (prof, prof_shutdown);
+
+       mono_profiler_install_jit_compile (prof_jit_enter, prof_jit_leave);
+
+       mono_profiler_set_events (MONO_PROFILE_JIT_COMPILATION);
+}
+
+static void
+emit_byte (MonoProfiler *prof, guint8 value)
+{
+       fwrite (&value, 1, 1, prof->outfile);
+}
+
+static void
+emit_int32 (MonoProfiler *prof, int value)
+{
+       // FIXME: Endianness
+       fwrite (&value, 4, 1, prof->outfile);
+}
+
+static void
+emit_string (MonoProfiler *prof, const char *str)
+{
+       int len = strlen (str);
+
+       emit_int32 (prof, len);
+       fwrite (str, len, 1, prof->outfile);
+}
+
+static void
+emit_record (MonoProfiler *prof, AotProfRecordType type, int id)
+{
+       emit_byte (prof, type);
+       emit_int32 (prof, id);
+}
+
+static int
+add_image (MonoProfiler *prof, MonoImage *image)
+{
+       int id = GPOINTER_TO_INT (g_hash_table_lookup (prof->images, image));
+       if (id)
+               return id - 1;
+
+       id = prof->id ++;
+       emit_record (prof, AOTPROF_RECORD_IMAGE, id);
+       emit_string (prof, image->assembly->aname.name);
+       emit_string (prof, image->guid);
+       g_hash_table_insert (prof->images, image, GINT_TO_POINTER (id + 1));
+       return id;
+}
+
+static int
+add_class (MonoProfiler *prof, MonoClass *klass);
+
+static int
+add_type (MonoProfiler *prof, MonoType *type)
+{
+       switch (type->type) {
+#if 0
+       case MONO_TYPE_SZARRAY: {
+               int eid = add_type (prof, &type->data.klass->byval_arg);
+               if (eid == -1)
+                       return -1;
+               int id = prof->id ++;
+               emit_record (prof, AOTPROF_RECORD_TYPE, id);
+               emit_byte (prof, MONO_TYPE_SZARRAY);
+               emit_int32 (prof, id);
+               return id;
+       }
+#endif
+       case MONO_TYPE_BOOLEAN:
+       case MONO_TYPE_CHAR:
+       case MONO_TYPE_I1:
+       case MONO_TYPE_U1:
+       case MONO_TYPE_I2:
+       case MONO_TYPE_U2:
+       case MONO_TYPE_I4:
+       case MONO_TYPE_U4:
+       case MONO_TYPE_I8:
+       case MONO_TYPE_U8:
+       case MONO_TYPE_R4:
+       case MONO_TYPE_R8:
+       case MONO_TYPE_I:
+       case MONO_TYPE_U:
+       case MONO_TYPE_OBJECT:
+       case MONO_TYPE_STRING:
+       case MONO_TYPE_CLASS:
+       case MONO_TYPE_VALUETYPE:
+       case MONO_TYPE_GENERICINST:
+               return add_class (prof, mono_class_from_mono_type (type));
+       default:
+               return -1;
+       }
+}
+
+static int
+add_ginst (MonoProfiler *prof, MonoGenericInst *inst)
+{
+       int i, id;
+       int *ids;
+
+       // FIXME: Cache
+       ids = g_malloc0 (inst->type_argc * sizeof (int));
+       for (i = 0; i < inst->type_argc; ++i) {
+               MonoType *t = inst->type_argv [i];
+               ids [i] = add_type (prof, t);
+               if (ids [i] == -1) {
+                       g_free (ids);
+                       return -1;
+               }
+       }
+       id = prof->id ++;
+       emit_record (prof, AOTPROF_RECORD_GINST, id);
+       emit_int32 (prof, inst->type_argc);
+       for (i = 0; i < inst->type_argc; ++i)
+               emit_int32 (prof, ids [i]);
+       g_free (ids);
+
+       return id;
+}
+
+static int
+add_class (MonoProfiler *prof, MonoClass *klass)
+{
+       int id, inst_id = -1, image_id;
+       char *name;
+
+       id = GPOINTER_TO_INT (g_hash_table_lookup (prof->classes, klass));
+       if (id)
+               return id - 1;
+
+       image_id = add_image (prof, klass->image);
+
+       if (mono_class_is_ginst (klass)) {
+               MonoGenericContext *ctx = mono_class_get_context (klass);
+               inst_id = add_ginst (prof, ctx->class_inst);
+               if (inst_id == -1)
+                       return -1;
+       }
+
+       if (klass->nested_in)
+               name = g_strdup_printf ("%s.%s/%s", klass->nested_in->name_space, klass->nested_in->name, klass->name);
+       else
+               name = g_strdup_printf ("%s.%s", klass->name_space, klass->name);
+
+       id = prof->id ++;
+       emit_record (prof, AOTPROF_RECORD_TYPE, id);
+       emit_byte (prof, MONO_TYPE_CLASS);
+       emit_int32 (prof, image_id);
+       emit_int32 (prof, inst_id);
+       emit_string (prof, name);
+       g_free (name);
+       g_hash_table_insert (prof->classes, klass, GINT_TO_POINTER (id + 1));
+       return id;
+}
+
+static void
+add_method (MonoProfiler *prof, MonoMethod *m)
+{
+       MonoError error;
+       MonoMethodSignature *sig;
+       char *s;
+
+       sig = mono_method_signature_checked (m, &error);
+       g_assert (mono_error_ok (&error));
+
+       int class_id = add_class (prof, m->klass);
+       if (class_id == -1)
+               return;
+       int inst_id = -1;
+
+       if (m->is_inflated) {
+               MonoGenericContext *ctx = mono_method_get_context (m);
+               if (ctx->method_inst)
+                       inst_id = add_ginst (prof, ctx->method_inst);
+       }
+       int id = prof->id ++;
+       emit_record (prof, AOTPROF_RECORD_METHOD, id);
+       emit_int32 (prof, class_id);
+       emit_int32 (prof, inst_id);
+       emit_int32 (prof, sig->param_count);
+       emit_string (prof, m->name);
+       s = mono_signature_full_name (sig);
+       emit_string (prof, s);
+       g_free (s);
+       if (verbose)
+               printf ("%s %d\n", mono_method_full_name (m, 1), id);
+}
+
+/* called at the end of the program */
+static void
+prof_shutdown (MonoProfiler *prof)
+{
+       FILE *outfile;
+       int mindex;
+       char magic [32];
+
+       printf ("Creating output file: %s\n", prof->outfile_name);
+
+       if (prof->outfile_name [0] == '#') {
+               int fd = strtol (prof->outfile_name + 1, NULL, 10);
+               outfile = fdopen (fd, "a");
+       } else {
+               outfile = fopen (prof->outfile_name, "w+");
+       }
+       if (!outfile) {
+               fprintf (stderr, "Unable to create output file '%s': %s.\n", prof->outfile_name, strerror (errno));
+               return;
+       }
+       prof->outfile = outfile;
+
+       gint32 version = (AOT_PROFILER_MAJOR_VERSION << 16) | AOT_PROFILER_MINOR_VERSION;
+       sprintf (magic, AOT_PROFILER_MAGIC);
+       fwrite (magic, strlen (magic), 1, outfile);
+       emit_int32 (prof, version);
+
+       GHashTable *all_methods = g_hash_table_new (NULL, NULL);
+       for (mindex = 0; mindex < prof->methods->len; ++mindex) {
+           MonoMethod *m = (MonoMethod*)g_ptr_array_index (prof->methods, mindex);
+
+               if (!mono_method_get_token (m))
+                       continue;
+
+               if (g_hash_table_lookup (all_methods, m))
+                       continue;
+               g_hash_table_insert (all_methods, m, m);
+
+               add_method (prof, m);
+       }
+       emit_record (prof, AOTPROF_RECORD_NONE, 0);
+
+       fclose (outfile);
+
+       g_hash_table_destroy (all_methods);
+       g_hash_table_destroy (prof->classes);
+       g_hash_table_destroy (prof->images);
+       g_ptr_array_free (prof->methods, TRUE);
+       g_free (prof->outfile_name);
+}
diff --git a/mono/profiler/aot.h b/mono/profiler/aot.h
new file mode 100644 (file)
index 0000000..7663cab
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef __MONO_PROFILER_AOT_H__
+#define __MONO_PROFILER_AOT_H__
+
+#include <config.h>
+
+/*
+ * File format:
+ * - magic
+ * - major/minor version as an int, i.e. 0x00010001
+ * - sequence of records terminated by a record with type TYPE_NONE
+ * Record format:
+ * - 1 byte record type (AotProfRecordType)
+ * - 1 int record id
+ * - record specific data
+ * Encoding rules:
+ * - int - 4 bytes little endian
+ * - string - int length followed by data
+ */
+
+typedef enum {
+       AOTPROF_RECORD_NONE,
+       AOTPROF_RECORD_IMAGE,
+       AOTPROF_RECORD_TYPE,
+       AOTPROF_RECORD_GINST,
+       AOTPROF_RECORD_METHOD
+} AotProfRecordType;
+
+#define AOT_PROFILER_MAGIC "AOTPROFILE"
+
+#define AOT_PROFILER_MAJOR_VERSION 1
+#define AOT_PROFILER_MINOR_VERSION 0
+
+#endif /* __MONO_PROFILER_AOT_H__ */
diff --git a/mono/profiler/arg-parsing.c b/mono/profiler/arg-parsing.c
deleted file mode 100644 (file)
index 688e9cf..0000000
+++ /dev/null
@@ -1,407 +0,0 @@
-#include <config.h>
-
-#include "mono-profiler-log.h"
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#ifdef HAVE_SCHED_GETAFFINITY
-#include <sched.h>
-
-#  ifndef GLIBC_HAS_CPU_COUNT
-static int
-CPU_COUNT(cpu_set_t *set)
-{
-       int i, count = 0;
-
-       for (int i = 0; i < CPU_SETSIZE; i++)
-               if (CPU_ISSET(i, set))
-                       count++;
-       return count;
-}
-#  endif
-#endif
-
-typedef struct {
-       const char *event_name;
-       const int mask;
-} NameAndMask;
-
-static NameAndMask event_list[] = {
-       { "domain", PROFLOG_DOMAIN_EVENTS },
-       { "assembly", PROFLOG_ASSEMBLY_EVENTS },
-       { "module", PROFLOG_MODULE_EVENTS },
-       { "class", PROFLOG_CLASS_EVENTS },
-       { "jit", PROFLOG_JIT_COMPILATION_EVENTS },
-       { "exception", PROFLOG_EXCEPTION_EVENTS },
-       { "gcalloc", PROFLOG_ALLOCATION_EVENTS },
-       { "gc", PROFLOG_GC_EVENTS },
-       { "thread", PROFLOG_THREAD_EVENTS },
-       { "calls", PROFLOG_CALL_EVENTS },
-       //{ "inscov", PROFLOG_INS_COVERAGE_EVENTS }, //this is a profiler API event, but there's no actual event for us to emit here
-       //{ "sampling", PROFLOG_SAMPLING_EVENTS }, //it makes no sense to enable/disable this event by itself
-       { "monitor", PROFLOG_MONITOR_EVENTS },
-       { "gcmove", PROFLOG_GC_MOVES_EVENTS },
-       { "gcroot", PROFLOG_GC_ROOT_EVENTS },
-       { "context", PROFLOG_CONTEXT_EVENTS },
-       { "finalization", PROFLOG_FINALIZATION_EVENTS },
-       { "counter", PROFLOG_COUNTER_EVENTS },
-       { "gchandle", PROFLOG_GC_HANDLE_EVENTS },
-
-       { "typesystem", PROFLOG_TYPELOADING_ALIAS },
-       { "coverage", PROFLOG_CODECOV_ALIAS },
-       //{ "sample", PROFLOG_PERF_SAMPLING_ALIAS }, //takes args, explicitly handles
-       { "alloc", PROFLOG_GC_ALLOC_ALIAS },
-       //{ "heapshot", PROFLOG_HEAPSHOT_ALIAS }, //takes args, explicitly handled
-       { "legacy", PROFLOG_LEGACY_ALIAS },
-};
-
-static void usage (void);
-static void set_hsmode (ProfilerConfig *config, const char* val);
-static void set_sample_freq (ProfilerConfig *config, const char *val);
-static int mono_cpu_count (void);
-
-
-static gboolean
-match_option (const char *arg, const char *opt_name, const char **rval)
-{
-       if (rval) {
-               const char *end = strchr (arg, '=');
-
-               *rval = NULL;
-               if (!end)
-                       return !strcmp (arg, opt_name);
-
-               if (strncmp (arg, opt_name, strlen (opt_name)) || (end - arg) > strlen (opt_name) + 1)
-                       return FALSE;
-               *rval = end + 1;
-               return TRUE;
-       } else {
-               //FIXME how should we handle passing a value to an arg that doesn't expect it?
-               return !strcmp (arg, opt_name);
-       }
-}
-
-static void
-parse_arg (const char *arg, ProfilerConfig *config)
-{
-       const char *val;
-
-       if (match_option (arg, "help", NULL)) {
-               usage ();
-       } else if (match_option (arg, "report", NULL)) {
-               config->do_report = TRUE;
-       } else if (match_option (arg, "debug", NULL)) {
-               config->do_debug = TRUE;
-       } else if (match_option (arg, "debug-coverage", NULL)) {
-               config->debug_coverage = TRUE;
-       } else if (match_option (arg, "sampling-real", NULL)) {
-               config->sampling_mode = MONO_PROFILER_STAT_MODE_REAL;
-       } else if (match_option (arg, "sampling-process", NULL)) {
-               config->sampling_mode = MONO_PROFILER_STAT_MODE_PROCESS;
-       } else if (match_option (arg, "heapshot", &val)) {
-               config->enable_mask |= PROFLOG_HEAPSHOT_ALIAS;
-               set_hsmode (config, val);
-       } else if (match_option (arg, "sample", &val)) {
-               config->enable_mask |= PROFLOG_PERF_SAMPLING_ALIAS;
-               set_sample_freq (config, val);
-       } else if (match_option (arg, "zip", NULL)) {
-               config->use_zip = TRUE;
-       } else if (match_option (arg, "output", &val)) {
-               config->output_filename = g_strdup (val);
-       } else if (match_option (arg, "port", &val)) {
-               char *end;
-               config->command_port = strtoul (val, &end, 10);
-       } else if (match_option (arg, "maxframes", &val)) {
-               char *end;
-               int num_frames = strtoul (val, &end, 10);
-               if (num_frames > MAX_FRAMES)
-                       num_frames = MAX_FRAMES;
-               config->notraces = num_frames == 0;
-               config->num_frames = num_frames;
-       } else if (match_option (arg, "maxsamples", &val)) {
-               char *end;
-               int max_samples = strtoul (val, &end, 10);
-               if (max_samples)
-                       config->max_allocated_sample_hits = max_samples;
-       } else if (match_option (arg, "calldepth", &val)) {
-               char *end;
-               config->max_call_depth = strtoul (val, &end, 10);
-       } else if (match_option (arg, "covfilter-file", &val)) {
-               if (config->cov_filter_files == NULL)
-                       config->cov_filter_files = g_ptr_array_new ();
-               g_ptr_array_add (config->cov_filter_files, g_strdup (val));
-       } else if (match_option (arg, "onlycoverage", NULL)) {
-               config->only_coverage = TRUE;
-       } else {
-               int i;
-
-               for (i = 0; i < G_N_ELEMENTS (event_list); ++i){
-                       if (!strcmp (arg, event_list [i].event_name)) {
-                               config->enable_mask |= event_list [i].mask;
-                               break;
-                       } else if (arg [0] == 'n' && arg [1] == 'o' && !strcmp (arg + 2, event_list [i].event_name)) {
-                               config->disable_mask |= event_list [i].mask;
-                       }
-               }
-               if (i == G_N_ELEMENTS (event_list)) {
-                       printf ("Could not parse argument %s\n", arg);
-               }
-       }
-}
-
-static void
-load_args_from_env_or_default (ProfilerConfig *config)
-{
-       //XXX change this to header constants
-
-       config->max_allocated_sample_hits = mono_cpu_count () * 1000;
-       config->sample_freq = 100;
-       config->max_call_depth = 100;
-       config->num_frames = MAX_FRAMES;
-}
-
-
-void
-proflog_parse_args (ProfilerConfig *config, const char *desc)
-{
-       const char *p;
-       gboolean in_quotes = FALSE;
-       char quote_char = '\0';
-       char *buffer = malloc (strlen (desc));
-       int buffer_pos = 0;
-
-       load_args_from_env_or_default (config);
-
-       for (p = desc; *p; p++){
-               switch (*p){
-               case ',':
-                       if (!in_quotes) {
-                               if (buffer_pos != 0){
-                                       buffer [buffer_pos] = 0;
-                                       parse_arg (buffer, config);
-                                       buffer_pos = 0;
-                               }
-                       } else {
-                               buffer [buffer_pos++] = *p;
-                       }
-                       break;
-
-               case '\\':
-                       if (p [1]) {
-                               buffer [buffer_pos++] = p[1];
-                               p++;
-                       }
-                       break;
-               case '\'':
-               case '"':
-                       if (in_quotes) {
-                               if (quote_char == *p)
-                                       in_quotes = FALSE;
-                               else
-                                       buffer [buffer_pos++] = *p;
-                       } else {
-                               in_quotes = TRUE;
-                               quote_char = *p;
-                       }
-                       break;
-               default:
-                       buffer [buffer_pos++] = *p;
-                       break;
-               }
-       }
-               
-       if (buffer_pos != 0) {
-               buffer [buffer_pos] = 0;
-               parse_arg (buffer, config);
-       }
-
-       g_free (buffer);
-
-       //Compure config effective mask
-       config->effective_mask = config->enable_mask & ~config->disable_mask;
-}
-
-static void
-set_hsmode (ProfilerConfig *config, const char* val)
-{
-       char *end;
-       unsigned int count;
-       if (!val)
-               return;
-       if (strcmp (val, "ondemand") == 0) {
-               config->hs_mode_ondemand = TRUE;
-               return;
-       }
-
-       count = strtoul (val, &end, 10);
-       if (val == end) {
-               usage ();
-               return;
-       }
-
-       if (strcmp (end, "ms") == 0)
-               config->hs_mode_ms = count;
-       else if (strcmp (end, "gc") == 0)
-               config->hs_mode_gc = count;
-       else
-               usage ();
-}
-
-/*
-Sampling frequency allows for one undocumented, hidden and ignored argument. The sampling kind.
-Back in the day when this was done using perf, we could specify one of: cycles,instr,cacherefs,cachemiss,branches,branchmiss
-With us moving ot userland sampling, those options are now meaningless.
-*/
-static void
-set_sample_freq (ProfilerConfig *config, const char *val)
-{
-       if (!val)
-               return;
-
-       const char *p = val;
-
-       // Is it only the frequency (new option style)?
-       if (isdigit (*p))
-               goto parse;
-
-       // Skip the sample type for backwards compatibility.
-       while (isalpha (*p))
-               p++;
-
-       // Skip the forward slash only if we got a sample type.
-       if (p != val && *p == '/') {
-               p++;
-
-               char *end;
-
-       parse:
-               config->sample_freq = strtoul (p, &end, 10);
-
-               if (p == end) {
-                       usage ();
-                       return; 
-               }
-
-               p = end;
-       }
-
-       if (*p)
-               usage ();
-}
-
-static void
-usage (void)
-{
-       printf ("Log profiler version %d.%d (format: %d)\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR, LOG_DATA_VERSION);
-       printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
-       printf ("Options:\n");
-       printf ("\thelp                 show this usage info\n");
-       printf ("\t[no]'event'          enable/disable a profiling event. Valid values: domain, assembly, module, class, jit, exception, gcalloc, gc, thread, monitor, gcmove, gcroot, context, finalization, counter, gchandle\n");
-       printf ("\t[no]typesystem       enable/disable typesystem related events such as class and assembly loading\n");
-       printf ("\t[no]alloc            enable/disable recording allocation info\n");
-       printf ("\t[no]calls            enable/disable recording enter/leave method events\n");
-       printf ("\t[no]legacy           enable/disable pre mono 5.4 default profiler events\n");
-       printf ("\tsample[=frequency]   enable/disable statistical sampling of threads (frequency in Hz, 100 by default)\n");
-       printf ("\theapshot[=MODE]      record heap shot info (by default at each major collection)\n");
-       printf ("\t                     MODE: every XXms milliseconds, every YYgc collections, ondemand\n");
-       printf ("\t[no]coverage         enable collection of code coverage data\n");
-       printf ("\tcovfilter=ASSEMBLY   add an assembly to the code coverage filters\n");
-       printf ("\t                     add a + to include the assembly or a - to exclude it\n");
-       printf ("\t                     covfilter=-mscorlib\n");
-       printf ("\tcovfilter-file=FILE  use FILE to generate the list of assemblies to be filtered\n");
-       printf ("\tmaxframes=NUM        collect up to NUM stack frames\n");
-       printf ("\tcalldepth=NUM        ignore method events for call chain depth bigger than NUM\n");
-       printf ("\toutput=FILENAME      write the data to file FILENAME (The file is always overwriten)\n");
-       printf ("\toutput=|PROGRAM      write the data to the stdin of PROGRAM\n");
-       printf ("\t                     %%t is subtituted with date and time, %%p with the pid\n");
-       printf ("\treport               create a report instead of writing the raw data to a file\n");
-       printf ("\tzip                  compress the output data\n");
-       printf ("\tport=PORTNUM         use PORTNUM for the listening command server\n");
-}
-
-static int
-mono_cpu_count (void)
-{
-#ifdef PLATFORM_ANDROID
-       /* Android tries really hard to save power by powering off CPUs on SMP phones which
-        * means the normal way to query cpu count returns a wrong value with userspace API.
-        * Instead we use /sys entries to query the actual hardware CPU count.
-        */
-       int count = 0;
-       char buffer[8] = {'\0'};
-       int present = open ("/sys/devices/system/cpu/present", O_RDONLY);
-       /* Format of the /sys entry is a cpulist of indexes which in the case
-        * of present is always of the form "0-(n-1)" when there is more than
-        * 1 core, n being the number of CPU cores in the system. Otherwise
-        * the value is simply 0
-        */
-       if (present != -1 && read (present, (char*)buffer, sizeof (buffer)) > 3)
-               count = strtol (((char*)buffer) + 2, NULL, 10);
-       if (present != -1)
-               close (present);
-       if (count > 0)
-               return count + 1;
-#endif
-
-#if defined(HOST_ARM) || defined (HOST_ARM64)
-
-       /* ARM platforms tries really hard to save power by powering off CPUs on SMP phones which
-        * means the normal way to query cpu count returns a wrong value with userspace API. */
-
-#ifdef _SC_NPROCESSORS_CONF
-       {
-               int count = sysconf (_SC_NPROCESSORS_CONF);
-               if (count > 0)
-                       return count;
-       }
-#endif
-
-#else
-
-#ifdef HAVE_SCHED_GETAFFINITY
-       {
-               cpu_set_t set;
-               if (sched_getaffinity (getpid (), sizeof (set), &set) == 0)
-                       return CPU_COUNT (&set);
-       }
-#endif
-#ifdef _SC_NPROCESSORS_ONLN
-       {
-               int count = sysconf (_SC_NPROCESSORS_ONLN);
-               if (count > 0)
-                       return count;
-       }
-#endif
-
-#endif /* defined(HOST_ARM) || defined (HOST_ARM64) */
-
-#ifdef USE_SYSCTL
-       {
-               int count;
-               int mib [2];
-               size_t len = sizeof (int);
-               mib [0] = CTL_HW;
-               mib [1] = HW_NCPU;
-               if (sysctl (mib, 2, &count, &len, NULL, 0) == 0)
-                       return count;
-       }
-#endif
-#ifdef HOST_WIN32
-       {
-               SYSTEM_INFO info;
-               GetSystemInfo (&info);
-               return info.dwNumberOfProcessors;
-       }
-#endif
-
-       static gboolean warned;
-
-       if (!warned) {
-               g_warning ("Don't know how to determine CPU count on this platform; assuming 1");
-               warned = TRUE;
-       }
-
-       return 1;
-}
\ No newline at end of file
diff --git a/mono/profiler/iomap.c b/mono/profiler/iomap.c
new file mode 100644 (file)
index 0000000..15baec0
--- /dev/null
@@ -0,0 +1,549 @@
+/*
+ * mono-profiler-iomap.c: IOMAP string profiler for Mono.
+ *
+ * Authors:
+ *   Marek Habersack <mhabersack@novell.com>
+ *
+ * Copyright (c) 2009 Novell, Inc (http://novell.com)
+ *
+ * Note: this profiler is completely unsafe wrt handling managed objects,
+ * don't use and don't copy code from here.
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
+ */
+#include "config.h"
+
+#include <string.h>
+#include <mono/utils/mono-io-portability.h>
+#include <mono/metadata/metadata.h>
+#include <mono/metadata/metadata-internals.h>
+#include <mono/metadata/class.h>
+#include <mono/metadata/class-internals.h>
+#include <mono/metadata/object-internals.h>
+#include <mono/metadata/image.h>
+#include <mono/metadata/mono-debug.h>
+#include <mono/metadata/debug-helpers.h>
+#include <mono/metadata/threads.h>
+#include <mono/metadata/profiler.h>
+#include <mono/metadata/loader.h>
+#include <mono/utils/mono-os-mutex.h>
+
+#define LOCATION_INDENT "        "
+#define BACKTRACE_SIZE 64
+
+typedef struct _MonoStackBacktraceInfo 
+{
+       MonoMethod *method;
+       gint native_offset;
+} MonoStackBacktraceInfo;
+
+typedef struct 
+{
+       guint32 count;
+       gchar *requestedName;
+       gchar *actualName;
+} MismatchedFilesStats;
+
+typedef struct _SavedString
+{
+       MonoString *string;
+       MonoDomain *domain;
+       void *stack [BACKTRACE_SIZE];
+       gint stack_entries;
+       struct _SavedString *next;
+} SavedString;
+
+typedef struct _SavedStringFindInfo
+{
+       guint32 hash;
+       size_t len;
+} SavedStringFindInfo;
+
+typedef struct _StringLocation
+{
+       gchar *hint;
+       struct _StringLocation *next;
+} StringLocation;
+
+struct _MonoProfiler
+{
+       GHashTable *mismatched_files_hash;
+       GHashTable *saved_strings_hash;
+       GHashTable *string_locations_hash;
+       gboolean may_have_locations;
+};
+
+typedef struct _StackWalkData
+{
+       MonoProfiler *prof;
+       void **stack;
+       int stack_size;
+       int frame_count;
+} StackWalkData;
+
+static mono_mutex_t mismatched_files_section;
+static gboolean runtime_initialized = FALSE;
+
+static inline void append_report (GString **report, const gchar *format, ...);
+static inline void print_report (const gchar *format, ...);
+static inline guint32 do_calc_string_hash (guint32 hash, const gchar *str);
+static inline guint32 calc_strings_hash (const gchar *str1, const gchar *str2, guint32 *str1hash);
+static void print_mismatched_stats (MonoProfiler *prof);
+static inline gchar *build_hint (SavedString *head);
+static inline gchar *build_hint_from_stack (MonoDomain *domain, void **stack, gint stack_entries);
+static inline void store_string_location (MonoProfiler *prof, const gchar *string, guint32 hash, size_t len);
+static void mono_portability_remember_string (MonoProfiler *prof, MonoDomain *domain, MonoString *str);
+void mono_profiler_startup (const char *desc);
+
+static void mismatched_stats_foreach_func (gpointer key, gpointer value, gpointer user_data)
+{
+       MismatchedFilesStats *stats = (MismatchedFilesStats*)value;
+       StringLocation *location;
+       MonoProfiler *prof = (MonoProfiler*)user_data;
+       guint32 hash;
+       gboolean bannerShown = FALSE;
+
+       hash = do_calc_string_hash (0, stats->requestedName);
+       fprintf (stdout,
+                "    Count: %u\n"
+                "Requested: %s\n"
+                "   Actual: %s\n",
+                stats->count, stats->requestedName, stats->actualName);
+
+       if (!prof->may_have_locations) {
+               fprintf (stdout, "\n");
+               return;
+       }
+
+       location = (StringLocation *)g_hash_table_lookup (prof->string_locations_hash, &hash);
+       while (location) {
+               if (location->hint && strlen (location->hint) > 0) {
+                       if (!bannerShown) {
+                               fprintf (stdout, "Locations:\n");
+                               bannerShown = TRUE;
+                       }
+                       fprintf (stdout, "%s", location->hint);
+               }
+               location = location->next;
+               if (location)
+                       fprintf (stdout, LOCATION_INDENT "--\n");
+       }
+
+       fprintf (stdout, "\n");
+}
+
+static void print_mismatched_stats (MonoProfiler *prof)
+{
+       if (!prof->mismatched_files_hash || g_hash_table_size (prof->mismatched_files_hash) == 0)
+               return;
+
+       prof->may_have_locations = g_hash_table_size (prof->string_locations_hash) > 0;
+
+       fprintf (stdout, "\n-=-=-=-=-=-=-= MONO_IOMAP Stats -=-=-=-=-=-=-=\n");
+       g_hash_table_foreach (prof->mismatched_files_hash, mismatched_stats_foreach_func, (gpointer)prof);
+       fflush (stdout);
+}
+
+static guint mismatched_files_guint32_hash (gconstpointer key)
+{
+       if (!key)
+               return 0;
+
+       return *((guint32*)key);
+}
+
+static gboolean mismatched_files_guint32_equal (gconstpointer key1, gconstpointer key2)
+{
+       if (!key1 || !key2)
+               return FALSE;
+
+       return (gboolean)(*((guint32*)key1) == *((guint32*)key2));
+}
+
+static inline guint32 do_calc_string_hash (guint32 hash, const gchar *str)
+{
+       guint32 ret = hash;
+       gchar *cc = (gchar*)str;
+       gchar *end = (gchar*)(str + strlen (str) - 1);
+
+       for (; cc < end; cc += 2) {
+               ret = (ret << 5) - ret + *cc;
+               ret = (ret << 5) - ret + cc [1];
+       }
+       end++;
+       if (cc < end)
+               ret = (ret << 5) - ret + *cc;
+
+       return ret;
+}
+
+static inline guint32 calc_strings_hash (const gchar *str1, const gchar *str2, guint32 *str1hash)
+{
+       guint32 hash = do_calc_string_hash (0, str1);
+       if (str1hash)
+               *str1hash = hash;
+       return do_calc_string_hash (hash, str2);
+}
+
+static inline void print_report (const gchar *format, ...)
+{
+       MonoError error;
+       MonoClass *klass;
+       MonoProperty *prop;
+       MonoString *str;
+       char *stack_trace;
+       va_list ap;
+
+       fprintf (stdout, "-=-=-=-=-=-=- MONO_IOMAP REPORT -=-=-=-=-=-=-\n");
+       va_start (ap, format);
+       vfprintf (stdout, format, ap);
+       fprintf (stdout, "\n");
+       va_end (ap);
+       klass = mono_class_load_from_name (mono_get_corlib (), "System", "Environment");
+       mono_class_init (klass);
+       prop = mono_class_get_property_from_name (klass, "StackTrace");
+       str = (MonoString*)mono_property_get_value_checked (prop, NULL, NULL, &error);
+       mono_error_assert_ok (&error);
+       stack_trace = mono_string_to_utf8_checked (str, &error);
+       mono_error_assert_ok (&error);
+
+       fprintf (stdout, "-= Stack Trace =-\n%s\n\n", stack_trace);
+       g_free (stack_trace);
+       fflush (stdout);
+}
+
+static inline void append_report (GString **report, const gchar *format, ...)
+{
+       va_list ap;
+       if (!*report)
+               *report = g_string_new ("");
+
+       va_start (ap, format);
+       g_string_append_vprintf (*report, format, ap);
+       va_end (ap);
+}
+
+static gboolean saved_strings_find_func (gpointer key, gpointer value, gpointer user_data)
+{
+       MonoError error;
+       SavedStringFindInfo *info = (SavedStringFindInfo*)user_data;
+       SavedString *saved = (SavedString*)value;
+       gchar *utf_str;
+       guint32 hash;
+
+       if (!info || !saved || mono_string_length (saved->string) != info->len)
+               return FALSE;
+
+       utf_str = mono_string_to_utf8_checked (saved->string, &error);
+       mono_error_assert_ok (&error);
+       hash = do_calc_string_hash (0, utf_str);
+       g_free (utf_str);
+
+       if (hash != info->hash)
+               return FALSE;
+
+       return TRUE;
+}
+
+static inline void store_string_location (MonoProfiler *prof, const gchar *string, guint32 hash, size_t len)
+{
+       StringLocation *location = (StringLocation *)g_hash_table_lookup (prof->string_locations_hash, &hash);
+       SavedString *saved;
+       SavedStringFindInfo info;
+       guint32 *hashptr;
+
+       if (location)
+               return;
+
+       info.hash = hash;
+       info.len = len;
+
+       /* Expensive but unavoidable... */
+       saved = (SavedString*)g_hash_table_find (prof->saved_strings_hash, saved_strings_find_func, &info);
+       hashptr = (guint32*)g_malloc (sizeof (guint32));
+       *hashptr = hash;
+       location = (StringLocation*)g_malloc0 (sizeof (location));
+
+       g_hash_table_insert (prof->string_locations_hash, hashptr, location);
+       if (!saved)
+               return;
+
+       g_hash_table_remove (prof->saved_strings_hash, saved->string);
+       location->hint = build_hint (saved);
+}
+
+static gboolean ignore_frame (MonoMethod *method)
+{
+       MonoClass *klass = method->klass;
+
+       if (method->wrapper_type != MONO_WRAPPER_NONE)
+               return TRUE;
+
+       /* Now ignore the assemblies we know shouldn't contain mixed-case names (only the most frequent cases) */
+       if (klass->image ) {
+               if (strcmp (klass->image->assembly_name, "mscorlib") == 0)
+                       return TRUE;
+               else if (strcmp (klass->image->assembly_name, "System") == 0)
+                       return TRUE;
+               else if (strncmp (klass->image->assembly_name, "Mono.", 5) == 0)
+                       return TRUE;
+               else if (strncmp (klass->image->assembly_name, "System.", 7) == 0)
+                       return TRUE;
+               else if (strcmp (klass->image->assembly_name, "PEAPI") == 0)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static inline gchar *build_hint_from_stack (MonoDomain *domain, void **stack, gint stack_entries)
+{
+       gchar *hint;
+       MonoMethod *method, *selectedMethod;
+       MonoAssembly *assembly;
+       MonoImage *image;
+       MonoDebugSourceLocation *location;
+       MonoStackBacktraceInfo *info;
+       gboolean use_full_trace;
+       char *methodName;
+       gint i, native_offset, firstAvailable;
+
+       selectedMethod = NULL;
+       firstAvailable = -1;
+       use_full_trace = FALSE;
+       native_offset = -1;
+       for (i = 0; i < stack_entries; i++) {
+               info = (MonoStackBacktraceInfo*) stack [i];
+               method = info ? info->method : NULL;
+
+               if (!method || method->wrapper_type != MONO_WRAPPER_NONE)
+                       continue;
+
+               if (firstAvailable == -1)
+                       firstAvailable = i;
+
+               image = method->klass->image;
+               assembly = image->assembly;
+
+               if ((assembly && assembly->in_gac) || ignore_frame (method))
+                       continue;
+               selectedMethod = method;
+               native_offset = info->native_offset;
+               break;
+       }
+
+       if (!selectedMethod) {
+               /* All the frames were from assemblies installed in GAC. Find first frame that is
+                * not in the ignore list */
+               for (i = 0; i < stack_entries; i++) {
+                       info = (MonoStackBacktraceInfo*) stack [i];
+                       method = info ? info->method : NULL;
+
+                       if (!method || ignore_frame (method))
+                               continue;
+                       selectedMethod = method;
+                       native_offset = info->native_offset;
+                       break;
+               }
+
+               if (!selectedMethod)
+                       use_full_trace = TRUE;
+       }
+
+       hint = NULL;
+       if (use_full_trace) {
+               GString *trace = g_string_new ("Full trace:\n");
+               for (i = firstAvailable; i < stack_entries; i++) {
+                       info = (MonoStackBacktraceInfo*) stack [i];
+                       method = info ? info->method : NULL;
+                       if (!method || method->wrapper_type != MONO_WRAPPER_NONE)
+                               continue;
+
+                       location = mono_debug_lookup_source_location (method, info->native_offset, domain);
+                       methodName = mono_method_full_name (method, TRUE);
+
+                       if (location) {
+                               append_report (&trace, LOCATION_INDENT "%s in %s:%u\n", methodName, location->source_file, location->row);
+                               mono_debug_free_source_location (location);
+                       } else
+                               append_report (&trace, LOCATION_INDENT "%s\n", methodName);
+                       g_free (methodName);
+               }
+
+               if (trace) {
+                       if (trace->len)
+                               hint = g_string_free (trace, FALSE);
+                       else
+                               g_string_free (trace, TRUE);
+               }
+       } else {
+               location = mono_debug_lookup_source_location (selectedMethod, native_offset, domain);
+               methodName = mono_method_full_name (selectedMethod, TRUE);
+
+               if (location) {
+                       hint = g_strdup_printf (LOCATION_INDENT "%s in %s:%u\n", methodName, location->source_file, location->row);
+                       mono_debug_free_source_location (location);
+               } else
+                       hint = g_strdup_printf (LOCATION_INDENT "%s\n", methodName);
+               g_free (methodName);
+       }
+
+       return hint;
+}
+
+static inline gchar *build_hint (SavedString *head)
+{
+       SavedString *current;
+       gchar *tmp;
+       GString *hint = NULL;
+
+       current = head;
+       while (current) {
+               tmp = build_hint_from_stack (current->domain, current->stack, current->stack_entries);
+               current = current->next;
+               if (!tmp)
+                       continue;
+
+               append_report (&hint, tmp);
+       }
+
+       if (hint) {
+               if (hint->len)
+                       return g_string_free (hint, FALSE);
+               else {
+                       g_string_free (hint, FALSE);
+                       return NULL;
+               }
+       }
+
+       return NULL;
+}
+
+static gboolean stack_walk_func (MonoMethod *method, gint32 native_offset, gint32 il_offset, gboolean managed, gpointer data)
+{
+       StackWalkData *swdata = (StackWalkData*)data;
+       MonoStackBacktraceInfo *info;
+
+       if (swdata->frame_count >= swdata->stack_size)
+               return TRUE;
+
+       info = (MonoStackBacktraceInfo*)g_malloc (sizeof (*info));
+       info->method = method;
+       info->native_offset = native_offset;
+
+       swdata->stack [swdata->frame_count++] = info;
+       return FALSE;
+}
+
+static inline int mono_stack_backtrace (MonoProfiler *prof, MonoDomain *domain, void **stack, int size)
+{
+       StackWalkData data;
+
+       data.prof = prof;
+       data.stack = stack;
+       data.stack_size = size;
+       data.frame_count = 0;
+
+       mono_stack_walk_no_il (stack_walk_func, (gpointer)&data);
+
+       return data.frame_count;
+}
+
+static void mono_portability_remember_string (MonoProfiler *prof, MonoDomain *domain, MonoString *str)
+{
+       SavedString *head, *entry;
+
+       if (!str || !domain || !runtime_initialized)
+               return;
+
+       entry = (SavedString*)g_malloc0 (sizeof (SavedString));
+       entry->string = str;
+       entry->domain = domain;
+       entry->stack_entries = mono_stack_backtrace (prof, domain, entry->stack, BACKTRACE_SIZE);
+       if (entry->stack_entries == 0) {
+               g_free (entry);
+               return;
+       }
+
+       mono_os_mutex_lock (&mismatched_files_section);
+       head = (SavedString*)g_hash_table_lookup (prof->saved_strings_hash, (gpointer)str);
+       if (head) {
+               while (head->next)
+                       head = head->next;
+               head->next = entry;
+       } else
+               g_hash_table_insert (prof->saved_strings_hash, (gpointer)str, (gpointer)entry);
+       mono_os_mutex_unlock (&mismatched_files_section);
+}
+
+static MonoClass *string_class = NULL;
+
+static void mono_portability_remember_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
+{
+       if (klass != string_class)
+               return;
+       mono_portability_remember_string (prof, mono_object_get_domain (obj), (MonoString*)obj);
+}
+
+static void mono_portability_iomap_event (MonoProfiler *prof, const char *report, const char *pathname, const char *new_pathname)
+{
+       guint32 hash, pathnameHash;
+       MismatchedFilesStats *stats;
+
+       if (!runtime_initialized)
+               return;
+
+       mono_os_mutex_lock (&mismatched_files_section);
+       hash = calc_strings_hash (pathname, new_pathname, &pathnameHash);
+       stats = (MismatchedFilesStats*)g_hash_table_lookup (prof->mismatched_files_hash, &hash);
+       if (stats == NULL) {
+               guint32 *hashptr;
+
+               stats = (MismatchedFilesStats*) g_malloc (sizeof (MismatchedFilesStats));
+               stats->count = 1;
+               stats->requestedName = g_strdup (pathname);
+               stats->actualName = g_strdup (new_pathname);
+               hashptr = (guint32*)g_malloc (sizeof (guint32));
+               if (hashptr) {
+                       *hashptr = hash;
+                       g_hash_table_insert (prof->mismatched_files_hash, (gpointer)hashptr, stats);
+               } else
+                       g_error ("Out of memory allocating integer pointer for mismatched files hash table.");
+
+               store_string_location (prof, (const gchar*)stats->requestedName, pathnameHash, strlen (stats->requestedName));
+               mono_os_mutex_unlock (&mismatched_files_section);
+
+               print_report ("%s -     Found file path: '%s'\n", report, new_pathname);
+       } else {
+               mono_os_mutex_unlock (&mismatched_files_section);
+               stats->count++;
+       }
+}
+
+static void runtime_initialized_cb (MonoProfiler *prof)
+{
+       runtime_initialized = TRUE;
+       string_class = mono_get_string_class ();
+}
+
+static void profiler_shutdown (MonoProfiler *prof)
+{
+       print_mismatched_stats (prof);
+       mono_os_mutex_destroy (&mismatched_files_section);
+}
+
+void mono_profiler_startup (const char *desc)
+{
+       MonoProfiler *prof = g_new0 (MonoProfiler, 1);
+
+       mono_os_mutex_init (&mismatched_files_section);
+       prof->mismatched_files_hash = g_hash_table_new (mismatched_files_guint32_hash, mismatched_files_guint32_equal);
+       prof->saved_strings_hash = g_hash_table_new (NULL, NULL);
+       prof->string_locations_hash = g_hash_table_new (mismatched_files_guint32_hash, mismatched_files_guint32_equal);
+
+       mono_profiler_install (prof, profiler_shutdown);
+       mono_profiler_install_runtime_initialized (runtime_initialized_cb);
+       mono_profiler_install_iomap (mono_portability_iomap_event);
+       mono_profiler_install_allocation (mono_portability_remember_alloc);
+
+       mono_profiler_set_events ((MonoProfileFlags)(MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_IOMAP_EVENTS));
+}
diff --git a/mono/profiler/log-args.c b/mono/profiler/log-args.c
new file mode 100644 (file)
index 0000000..af647a0
--- /dev/null
@@ -0,0 +1,407 @@
+#include <config.h>
+
+#include "log.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SCHED_GETAFFINITY
+#include <sched.h>
+
+#  ifndef GLIBC_HAS_CPU_COUNT
+static int
+CPU_COUNT(cpu_set_t *set)
+{
+       int i, count = 0;
+
+       for (int i = 0; i < CPU_SETSIZE; i++)
+               if (CPU_ISSET(i, set))
+                       count++;
+       return count;
+}
+#  endif
+#endif
+
+typedef struct {
+       const char *event_name;
+       const int mask;
+} NameAndMask;
+
+static NameAndMask event_list[] = {
+       { "domain", PROFLOG_DOMAIN_EVENTS },
+       { "assembly", PROFLOG_ASSEMBLY_EVENTS },
+       { "module", PROFLOG_MODULE_EVENTS },
+       { "class", PROFLOG_CLASS_EVENTS },
+       { "jit", PROFLOG_JIT_COMPILATION_EVENTS },
+       { "exception", PROFLOG_EXCEPTION_EVENTS },
+       { "gcalloc", PROFLOG_ALLOCATION_EVENTS },
+       { "gc", PROFLOG_GC_EVENTS },
+       { "thread", PROFLOG_THREAD_EVENTS },
+       { "calls", PROFLOG_CALL_EVENTS },
+       //{ "inscov", PROFLOG_INS_COVERAGE_EVENTS }, //this is a profiler API event, but there's no actual event for us to emit here
+       //{ "sampling", PROFLOG_SAMPLING_EVENTS }, //it makes no sense to enable/disable this event by itself
+       { "monitor", PROFLOG_MONITOR_EVENTS },
+       { "gcmove", PROFLOG_GC_MOVES_EVENTS },
+       { "gcroot", PROFLOG_GC_ROOT_EVENTS },
+       { "context", PROFLOG_CONTEXT_EVENTS },
+       { "finalization", PROFLOG_FINALIZATION_EVENTS },
+       { "counter", PROFLOG_COUNTER_EVENTS },
+       { "gchandle", PROFLOG_GC_HANDLE_EVENTS },
+
+       { "typesystem", PROFLOG_TYPELOADING_ALIAS },
+       { "coverage", PROFLOG_CODECOV_ALIAS },
+       //{ "sample", PROFLOG_PERF_SAMPLING_ALIAS }, //takes args, explicitly handles
+       { "alloc", PROFLOG_GC_ALLOC_ALIAS },
+       //{ "heapshot", PROFLOG_HEAPSHOT_ALIAS }, //takes args, explicitly handled
+       { "legacy", PROFLOG_LEGACY_ALIAS },
+};
+
+static void usage (void);
+static void set_hsmode (ProfilerConfig *config, const char* val);
+static void set_sample_freq (ProfilerConfig *config, const char *val);
+static int mono_cpu_count (void);
+
+
+static gboolean
+match_option (const char *arg, const char *opt_name, const char **rval)
+{
+       if (rval) {
+               const char *end = strchr (arg, '=');
+
+               *rval = NULL;
+               if (!end)
+                       return !strcmp (arg, opt_name);
+
+               if (strncmp (arg, opt_name, strlen (opt_name)) || (end - arg) > strlen (opt_name) + 1)
+                       return FALSE;
+               *rval = end + 1;
+               return TRUE;
+       } else {
+               //FIXME how should we handle passing a value to an arg that doesn't expect it?
+               return !strcmp (arg, opt_name);
+       }
+}
+
+static void
+parse_arg (const char *arg, ProfilerConfig *config)
+{
+       const char *val;
+
+       if (match_option (arg, "help", NULL)) {
+               usage ();
+       } else if (match_option (arg, "report", NULL)) {
+               config->do_report = TRUE;
+       } else if (match_option (arg, "debug", NULL)) {
+               config->do_debug = TRUE;
+       } else if (match_option (arg, "debug-coverage", NULL)) {
+               config->debug_coverage = TRUE;
+       } else if (match_option (arg, "sampling-real", NULL)) {
+               config->sampling_mode = MONO_PROFILER_STAT_MODE_REAL;
+       } else if (match_option (arg, "sampling-process", NULL)) {
+               config->sampling_mode = MONO_PROFILER_STAT_MODE_PROCESS;
+       } else if (match_option (arg, "heapshot", &val)) {
+               config->enable_mask |= PROFLOG_HEAPSHOT_ALIAS;
+               set_hsmode (config, val);
+       } else if (match_option (arg, "sample", &val)) {
+               config->enable_mask |= PROFLOG_PERF_SAMPLING_ALIAS;
+               set_sample_freq (config, val);
+       } else if (match_option (arg, "zip", NULL)) {
+               config->use_zip = TRUE;
+       } else if (match_option (arg, "output", &val)) {
+               config->output_filename = g_strdup (val);
+       } else if (match_option (arg, "port", &val)) {
+               char *end;
+               config->command_port = strtoul (val, &end, 10);
+       } else if (match_option (arg, "maxframes", &val)) {
+               char *end;
+               int num_frames = strtoul (val, &end, 10);
+               if (num_frames > MAX_FRAMES)
+                       num_frames = MAX_FRAMES;
+               config->notraces = num_frames == 0;
+               config->num_frames = num_frames;
+       } else if (match_option (arg, "maxsamples", &val)) {
+               char *end;
+               int max_samples = strtoul (val, &end, 10);
+               if (max_samples)
+                       config->max_allocated_sample_hits = max_samples;
+       } else if (match_option (arg, "calldepth", &val)) {
+               char *end;
+               config->max_call_depth = strtoul (val, &end, 10);
+       } else if (match_option (arg, "covfilter-file", &val)) {
+               if (config->cov_filter_files == NULL)
+                       config->cov_filter_files = g_ptr_array_new ();
+               g_ptr_array_add (config->cov_filter_files, g_strdup (val));
+       } else if (match_option (arg, "onlycoverage", NULL)) {
+               config->only_coverage = TRUE;
+       } else {
+               int i;
+
+               for (i = 0; i < G_N_ELEMENTS (event_list); ++i){
+                       if (!strcmp (arg, event_list [i].event_name)) {
+                               config->enable_mask |= event_list [i].mask;
+                               break;
+                       } else if (arg [0] == 'n' && arg [1] == 'o' && !strcmp (arg + 2, event_list [i].event_name)) {
+                               config->disable_mask |= event_list [i].mask;
+                       }
+               }
+               if (i == G_N_ELEMENTS (event_list)) {
+                       printf ("Could not parse argument %s\n", arg);
+               }
+       }
+}
+
+static void
+load_args_from_env_or_default (ProfilerConfig *config)
+{
+       //XXX change this to header constants
+
+       config->max_allocated_sample_hits = mono_cpu_count () * 1000;
+       config->sample_freq = 100;
+       config->max_call_depth = 100;
+       config->num_frames = MAX_FRAMES;
+}
+
+
+void
+proflog_parse_args (ProfilerConfig *config, const char *desc)
+{
+       const char *p;
+       gboolean in_quotes = FALSE;
+       char quote_char = '\0';
+       char *buffer = malloc (strlen (desc));
+       int buffer_pos = 0;
+
+       load_args_from_env_or_default (config);
+
+       for (p = desc; *p; p++){
+               switch (*p){
+               case ',':
+                       if (!in_quotes) {
+                               if (buffer_pos != 0){
+                                       buffer [buffer_pos] = 0;
+                                       parse_arg (buffer, config);
+                                       buffer_pos = 0;
+                               }
+                       } else {
+                               buffer [buffer_pos++] = *p;
+                       }
+                       break;
+
+               case '\\':
+                       if (p [1]) {
+                               buffer [buffer_pos++] = p[1];
+                               p++;
+                       }
+                       break;
+               case '\'':
+               case '"':
+                       if (in_quotes) {
+                               if (quote_char == *p)
+                                       in_quotes = FALSE;
+                               else
+                                       buffer [buffer_pos++] = *p;
+                       } else {
+                               in_quotes = TRUE;
+                               quote_char = *p;
+                       }
+                       break;
+               default:
+                       buffer [buffer_pos++] = *p;
+                       break;
+               }
+       }
+               
+       if (buffer_pos != 0) {
+               buffer [buffer_pos] = 0;
+               parse_arg (buffer, config);
+       }
+
+       g_free (buffer);
+
+       //Compure config effective mask
+       config->effective_mask = config->enable_mask & ~config->disable_mask;
+}
+
+static void
+set_hsmode (ProfilerConfig *config, const char* val)
+{
+       char *end;
+       unsigned int count;
+       if (!val)
+               return;
+       if (strcmp (val, "ondemand") == 0) {
+               config->hs_mode_ondemand = TRUE;
+               return;
+       }
+
+       count = strtoul (val, &end, 10);
+       if (val == end) {
+               usage ();
+               return;
+       }
+
+       if (strcmp (end, "ms") == 0)
+               config->hs_mode_ms = count;
+       else if (strcmp (end, "gc") == 0)
+               config->hs_mode_gc = count;
+       else
+               usage ();
+}
+
+/*
+Sampling frequency allows for one undocumented, hidden and ignored argument. The sampling kind.
+Back in the day when this was done using perf, we could specify one of: cycles,instr,cacherefs,cachemiss,branches,branchmiss
+With us moving ot userland sampling, those options are now meaningless.
+*/
+static void
+set_sample_freq (ProfilerConfig *config, const char *val)
+{
+       if (!val)
+               return;
+
+       const char *p = val;
+
+       // Is it only the frequency (new option style)?
+       if (isdigit (*p))
+               goto parse;
+
+       // Skip the sample type for backwards compatibility.
+       while (isalpha (*p))
+               p++;
+
+       // Skip the forward slash only if we got a sample type.
+       if (p != val && *p == '/') {
+               p++;
+
+               char *end;
+
+       parse:
+               config->sample_freq = strtoul (p, &end, 10);
+
+               if (p == end) {
+                       usage ();
+                       return; 
+               }
+
+               p = end;
+       }
+
+       if (*p)
+               usage ();
+}
+
+static void
+usage (void)
+{
+       printf ("Log profiler version %d.%d (format: %d)\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR, LOG_DATA_VERSION);
+       printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
+       printf ("Options:\n");
+       printf ("\thelp                 show this usage info\n");
+       printf ("\t[no]'event'          enable/disable a profiling event. Valid values: domain, assembly, module, class, jit, exception, gcalloc, gc, thread, monitor, gcmove, gcroot, context, finalization, counter, gchandle\n");
+       printf ("\t[no]typesystem       enable/disable typesystem related events such as class and assembly loading\n");
+       printf ("\t[no]alloc            enable/disable recording allocation info\n");
+       printf ("\t[no]calls            enable/disable recording enter/leave method events\n");
+       printf ("\t[no]legacy           enable/disable pre mono 5.4 default profiler events\n");
+       printf ("\tsample[=frequency]   enable/disable statistical sampling of threads (frequency in Hz, 100 by default)\n");
+       printf ("\theapshot[=MODE]      record heap shot info (by default at each major collection)\n");
+       printf ("\t                     MODE: every XXms milliseconds, every YYgc collections, ondemand\n");
+       printf ("\t[no]coverage         enable collection of code coverage data\n");
+       printf ("\tcovfilter=ASSEMBLY   add an assembly to the code coverage filters\n");
+       printf ("\t                     add a + to include the assembly or a - to exclude it\n");
+       printf ("\t                     covfilter=-mscorlib\n");
+       printf ("\tcovfilter-file=FILE  use FILE to generate the list of assemblies to be filtered\n");
+       printf ("\tmaxframes=NUM        collect up to NUM stack frames\n");
+       printf ("\tcalldepth=NUM        ignore method events for call chain depth bigger than NUM\n");
+       printf ("\toutput=FILENAME      write the data to file FILENAME (The file is always overwriten)\n");
+       printf ("\toutput=|PROGRAM      write the data to the stdin of PROGRAM\n");
+       printf ("\t                     %%t is subtituted with date and time, %%p with the pid\n");
+       printf ("\treport               create a report instead of writing the raw data to a file\n");
+       printf ("\tzip                  compress the output data\n");
+       printf ("\tport=PORTNUM         use PORTNUM for the listening command server\n");
+}
+
+static int
+mono_cpu_count (void)
+{
+#ifdef PLATFORM_ANDROID
+       /* Android tries really hard to save power by powering off CPUs on SMP phones which
+        * means the normal way to query cpu count returns a wrong value with userspace API.
+        * Instead we use /sys entries to query the actual hardware CPU count.
+        */
+       int count = 0;
+       char buffer[8] = {'\0'};
+       int present = open ("/sys/devices/system/cpu/present", O_RDONLY);
+       /* Format of the /sys entry is a cpulist of indexes which in the case
+        * of present is always of the form "0-(n-1)" when there is more than
+        * 1 core, n being the number of CPU cores in the system. Otherwise
+        * the value is simply 0
+        */
+       if (present != -1 && read (present, (char*)buffer, sizeof (buffer)) > 3)
+               count = strtol (((char*)buffer) + 2, NULL, 10);
+       if (present != -1)
+               close (present);
+       if (count > 0)
+               return count + 1;
+#endif
+
+#if defined(HOST_ARM) || defined (HOST_ARM64)
+
+       /* ARM platforms tries really hard to save power by powering off CPUs on SMP phones which
+        * means the normal way to query cpu count returns a wrong value with userspace API. */
+
+#ifdef _SC_NPROCESSORS_CONF
+       {
+               int count = sysconf (_SC_NPROCESSORS_CONF);
+               if (count > 0)
+                       return count;
+       }
+#endif
+
+#else
+
+#ifdef HAVE_SCHED_GETAFFINITY
+       {
+               cpu_set_t set;
+               if (sched_getaffinity (getpid (), sizeof (set), &set) == 0)
+                       return CPU_COUNT (&set);
+       }
+#endif
+#ifdef _SC_NPROCESSORS_ONLN
+       {
+               int count = sysconf (_SC_NPROCESSORS_ONLN);
+               if (count > 0)
+                       return count;
+       }
+#endif
+
+#endif /* defined(HOST_ARM) || defined (HOST_ARM64) */
+
+#ifdef USE_SYSCTL
+       {
+               int count;
+               int mib [2];
+               size_t len = sizeof (int);
+               mib [0] = CTL_HW;
+               mib [1] = HW_NCPU;
+               if (sysctl (mib, 2, &count, &len, NULL, 0) == 0)
+                       return count;
+       }
+#endif
+#ifdef HOST_WIN32
+       {
+               SYSTEM_INFO info;
+               GetSystemInfo (&info);
+               return info.dwNumberOfProcessors;
+       }
+#endif
+
+       static gboolean warned;
+
+       if (!warned) {
+               g_warning ("Don't know how to determine CPU count on this platform; assuming 1");
+               warned = TRUE;
+       }
+
+       return 1;
+}
\ No newline at end of file
diff --git a/mono/profiler/log.c b/mono/profiler/log.c
new file mode 100644 (file)
index 0000000..c1f56da
--- /dev/null
@@ -0,0 +1,4735 @@
+/*
+ * mono-profiler-log.c: mono log profiler
+ *
+ * Authors:
+ *   Paolo Molaro (lupus@ximian.com)
+ *   Alex Rønne Petersen (alexrp@xamarin.com)
+ *
+ * Copyright 2010 Novell, Inc (http://www.novell.com)
+ * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
+ */
+
+#include <config.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/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-os-mutex.h>
+#include <mono/utils/mono-os-semaphore.h>
+#include <mono/utils/mono-threads.h>
+#include <mono/utils/mono-threads-api.h>
+#include "log.h"
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_LINK_H
+#include <link.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if defined(__APPLE__)
+#include <mach/mach_time.h>
+#endif
+#include <netinet/in.h>
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#include <sys/socket.h>
+#if defined (HAVE_SYS_ZLIB)
+#include <zlib.h>
+#endif
+
+#define BUFFER_SIZE (4096 * 16)
+
+/* Worst-case size in bytes of a 64-bit value encoded with LEB128. */
+#define LEB128_SIZE 10
+
+/* Size of a value encoded as a single byte. */
+#undef BYTE_SIZE // mach/i386/vm_param.h on OS X defines this to 8, but it isn't used for anything.
+#define BYTE_SIZE 1
+
+/* Size in bytes of the event prefix (ID + time). */
+#define EVENT_SIZE (BYTE_SIZE + LEB128_SIZE)
+
+static volatile gint32 runtime_inited;
+static volatile gint32 in_shutdown;
+
+static int nocalls = 0;
+static int notraces = 0;
+static int use_zip = 0;
+static int do_report = 0;
+static int do_heap_shot = 0;
+static int max_call_depth = 0;
+static int command_port = 0;
+static int heapshot_requested = 0;
+static int do_mono_sample = 0;
+static int do_debug = 0;
+static int do_coverage = 0;
+static gboolean no_counters = FALSE;
+static gboolean only_coverage = FALSE;
+static gboolean debug_coverage = FALSE;
+static int max_allocated_sample_hits;
+
+// Statistics for internal profiler data structures.
+static gint32 sample_allocations_ctr,
+              buffer_allocations_ctr;
+
+// Statistics for profiler events.
+static gint32 sync_points_ctr,
+              heap_objects_ctr,
+              heap_starts_ctr,
+              heap_ends_ctr,
+              heap_roots_ctr,
+              gc_events_ctr,
+              gc_resizes_ctr,
+              gc_allocs_ctr,
+              gc_moves_ctr,
+              gc_handle_creations_ctr,
+              gc_handle_deletions_ctr,
+              finalize_begins_ctr,
+              finalize_ends_ctr,
+              finalize_object_begins_ctr,
+              finalize_object_ends_ctr,
+              image_loads_ctr,
+              image_unloads_ctr,
+              assembly_loads_ctr,
+              assembly_unloads_ctr,
+              class_loads_ctr,
+              class_unloads_ctr,
+              method_entries_ctr,
+              method_exits_ctr,
+              method_exception_exits_ctr,
+              method_jits_ctr,
+              code_buffers_ctr,
+              exception_throws_ctr,
+              exception_clauses_ctr,
+              monitor_contentions_ctr,
+              monitor_acquisitions_ctr,
+              monitor_failures_ctr,
+              thread_starts_ctr,
+              thread_ends_ctr,
+              thread_names_ctr,
+              domain_loads_ctr,
+              domain_unloads_ctr,
+              domain_names_ctr,
+              context_loads_ctr,
+              context_unloads_ctr,
+              sample_ubins_ctr,
+              sample_usyms_ctr,
+              sample_hits_ctr,
+              counter_descriptors_ctr,
+              counter_samples_ctr,
+              perfcounter_descriptors_ctr,
+              perfcounter_samples_ctr,
+              coverage_methods_ctr,
+              coverage_statements_ctr,
+              coverage_classes_ctr,
+              coverage_assemblies_ctr;
+
+static MonoLinkedListSet profiler_thread_list;
+
+/*
+ * file format:
+ * [header] [buffer]*
+ *
+ * The file is composed by a header followed by 0 or more buffers.
+ * Each buffer contains events that happened on a thread: for a given thread
+ * buffers that appear later in the file are guaranteed to contain events
+ * that happened later in time. Buffers from separate threads could be interleaved,
+ * though.
+ * Buffers are not required to be aligned.
+ *
+ * header format:
+ * [id: 4 bytes] constant value: LOG_HEADER_ID
+ * [major: 1 byte] [minor: 1 byte] major and minor version of the log profiler
+ * [format: 1 byte] version of the data format for the rest of the file
+ * [ptrsize: 1 byte] size in bytes of a pointer in the profiled program
+ * [startup time: 8 bytes] time in milliseconds since the unix epoch when the program started
+ * [timer overhead: 4 bytes] approximate overhead in nanoseconds of the timer
+ * [flags: 4 bytes] file format flags, should be 0 for now
+ * [pid: 4 bytes] pid of the profiled process
+ * [port: 2 bytes] tcp port for server if != 0
+ * [args size: 4 bytes] size of args
+ * [args: string] arguments passed to the profiler
+ * [arch size: 4 bytes] size of arch
+ * [arch: string] architecture the profiler is running on
+ * [os size: 4 bytes] size of os
+ * [os: string] operating system the profiler is running on
+ *
+ * The multiple byte integers are in little-endian format.
+ *
+ * buffer format:
+ * [buffer header] [event]*
+ * Buffers have a fixed-size header followed by 0 or more bytes of event data.
+ * Timing information and other values in the event data are usually stored
+ * as uleb128 or sleb128 integers. To save space, as noted for each item below,
+ * some data is represented as a difference between the actual value and
+ * either the last value of the same type (like for timing information) or
+ * as the difference from a value stored in a buffer header.
+ *
+ * For timing information the data is stored as uleb128, since timing
+ * increases in a monotonic way in each thread: the value is the number of
+ * nanoseconds to add to the last seen timing data in a buffer. The first value
+ * in a buffer will be calculated from the time_base field in the buffer head.
+ *
+ * Object or heap sizes are stored as uleb128.
+ * Pointer differences are stored as sleb128, instead.
+ *
+ * If an unexpected value is found, the rest of the buffer should be ignored,
+ * as generally the later values need the former to be interpreted correctly.
+ *
+ * buffer header format:
+ * [bufid: 4 bytes] constant value: BUF_ID
+ * [len: 4 bytes] size of the data following the buffer header
+ * [time_base: 8 bytes] time base in nanoseconds since an unspecified epoch
+ * [ptr_base: 8 bytes] base value for pointers
+ * [obj_base: 8 bytes] base value for object addresses
+ * [thread id: 8 bytes] system-specific thread ID (pthread_t for example)
+ * [method_base: 8 bytes] base value for MonoMethod pointers
+ *
+ * event format:
+ * [extended info: upper 4 bits] [type: lower 4 bits]
+ * [time diff: uleb128] nanoseconds since last timing
+ * [data]*
+ * The data that follows depends on type and the extended info.
+ * Type is one of the enum values in mono-profiler-log.h: TYPE_ALLOC, TYPE_GC,
+ * TYPE_METADATA, TYPE_METHOD, TYPE_EXCEPTION, TYPE_MONITOR, TYPE_HEAP.
+ * The extended info bits are interpreted based on type, see
+ * each individual event description below.
+ * strings are represented as a 0-terminated utf8 sequence.
+ *
+ * backtrace format:
+ * [num: uleb128] number of frames following
+ * [frame: sleb128]* mum MonoMethod* as a pointer difference from the last such
+ * pointer or the buffer method_base
+ *
+ * type alloc format:
+ * type: TYPE_ALLOC
+ * exinfo: flags: TYPE_ALLOC_BT
+ * [ptr: sleb128] class as a byte difference from ptr_base
+ * [obj: sleb128] object address as a byte difference from obj_base
+ * [size: uleb128] size of the object in the heap
+ * If the TYPE_ALLOC_BT flag is set, a backtrace follows.
+ *
+ * type GC format:
+ * type: TYPE_GC
+ * exinfo: one of TYPE_GC_EVENT, TYPE_GC_RESIZE, TYPE_GC_MOVE, TYPE_GC_HANDLE_CREATED[_BT],
+ * TYPE_GC_HANDLE_DESTROYED[_BT], TYPE_GC_FINALIZE_START, TYPE_GC_FINALIZE_END,
+ * TYPE_GC_FINALIZE_OBJECT_START, TYPE_GC_FINALIZE_OBJECT_END
+ * if exinfo == TYPE_GC_RESIZE
+ *     [heap_size: uleb128] new heap size
+ * if exinfo == TYPE_GC_EVENT
+ *     [event type: byte] GC event (MONO_GC_EVENT_* from profiler.h)
+ *     [generation: byte] GC generation event refers to
+ * if exinfo == TYPE_GC_MOVE
+ *     [num_objects: uleb128] number of object moves that follow
+ *     [objaddr: sleb128]+ num_objects object pointer differences from obj_base
+ *     num is always an even number: the even items are the old
+ *     addresses, the odd numbers are the respective new object addresses
+ * if exinfo == TYPE_GC_HANDLE_CREATED[_BT]
+ *     [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
+ *     upper bits reserved as flags
+ *     [handle: uleb128] GC handle value
+ *     [objaddr: sleb128] object pointer differences from obj_base
+ *     If exinfo == TYPE_GC_HANDLE_CREATED_BT, a backtrace follows.
+ * if exinfo == TYPE_GC_HANDLE_DESTROYED[_BT]
+ *     [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
+ *     upper bits reserved as flags
+ *     [handle: uleb128] GC handle value
+ *     If exinfo == TYPE_GC_HANDLE_DESTROYED_BT, a backtrace follows.
+ * if exinfo == TYPE_GC_FINALIZE_OBJECT_{START,END}
+ *     [object: sleb128] the object as a difference from obj_base
+ *
+ * type metadata format:
+ * type: TYPE_METADATA
+ * exinfo: one of: TYPE_END_LOAD, TYPE_END_UNLOAD (optional for TYPE_THREAD and TYPE_DOMAIN)
+ * [mtype: byte] metadata type, one of: TYPE_CLASS, TYPE_IMAGE, TYPE_ASSEMBLY, TYPE_DOMAIN,
+ * TYPE_THREAD, TYPE_CONTEXT
+ * [pointer: sleb128] pointer of the metadata type depending on mtype
+ * if mtype == TYPE_CLASS
+ *     [image: sleb128] MonoImage* as a pointer difference from ptr_base
+ *     [name: string] full class name
+ * if mtype == TYPE_IMAGE
+ *     [name: string] image file name
+ * if mtype == TYPE_ASSEMBLY
+ *     [name: string] assembly name
+ * if mtype == TYPE_DOMAIN && exinfo == 0
+ *     [name: string] domain friendly name
+ * if mtype == TYPE_CONTEXT
+ *     [domain: sleb128] domain id as pointer
+ * if mtype == TYPE_THREAD && exinfo == 0
+ *     [name: string] thread name
+ *
+ * type method format:
+ * type: TYPE_METHOD
+ * exinfo: one of: TYPE_LEAVE, TYPE_ENTER, TYPE_EXC_LEAVE, TYPE_JIT
+ * [method: sleb128] MonoMethod* as a pointer difference from the last such
+ * pointer or the buffer method_base
+ * if exinfo == TYPE_JIT
+ *     [code address: sleb128] pointer to the native code as a diff from ptr_base
+ *     [code size: uleb128] size of the generated code
+ *     [name: string] full method name
+ *
+ * type exception format:
+ * type: TYPE_EXCEPTION
+ * exinfo: TYPE_THROW_BT flag or one of: TYPE_CLAUSE
+ * if exinfo == TYPE_CLAUSE
+ *     [clause type: byte] MonoExceptionEnum enum value
+ *     [clause index: uleb128] index of the current clause
+ *     [method: sleb128] MonoMethod* as a pointer difference from the last such
+ *     pointer or the buffer method_base
+ * else
+ *     [object: sleb128] the exception object as a difference from obj_base
+ *     if exinfo has TYPE_THROW_BT set, a backtrace follows.
+ *
+ * type runtime format:
+ * type: TYPE_RUNTIME
+ * exinfo: one of: TYPE_JITHELPER
+ * if exinfo == TYPE_JITHELPER
+ *     [type: byte] MonoProfilerCodeBufferType enum value
+ *     [buffer address: sleb128] pointer to the native code as a diff from ptr_base
+ *     [buffer size: uleb128] size of the generated code
+ *     if type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
+ *             [name: string] buffer description name
+ *
+ * type monitor format:
+ * type: TYPE_MONITOR
+ * exinfo: TYPE_MONITOR_BT flag and one of: MONO_PROFILER_MONITOR_(CONTENTION|FAIL|DONE)
+ * [object: sleb128] the lock object as a difference from obj_base
+ * if exinfo.low3bits == MONO_PROFILER_MONITOR_CONTENTION
+ *     If the TYPE_MONITOR_BT flag is set, a backtrace follows.
+ *
+ * type heap format
+ * type: TYPE_HEAP
+ * exinfo: one of TYPE_HEAP_START, TYPE_HEAP_END, TYPE_HEAP_OBJECT, TYPE_HEAP_ROOT
+ * if exinfo == TYPE_HEAP_OBJECT
+ *     [object: sleb128] the object as a difference from obj_base
+ *     [class: sleb128] the object MonoClass* as a difference from ptr_base
+ *     [size: uleb128] size of the object on the heap
+ *     [num_refs: uleb128] number of object references
+ *     each referenced objref is preceded by a uleb128 encoded offset: the
+ *     first offset is from the object address and each next offset is relative
+ *     to the previous one
+ *     [objrefs: sleb128]+ object referenced as a difference from obj_base
+ *     The same object can appear multiple times, but only the first time
+ *     with size != 0: in the other cases this data will only be used to
+ *     provide additional referenced objects.
+ * if exinfo == TYPE_HEAP_ROOT
+ *     [num_roots: uleb128] number of root references
+ *     [num_gc: uleb128] number of major gcs
+ *     [object: sleb128] the object as a difference from obj_base
+ *     [root_type: byte] the root_type: MonoProfileGCRootType (profiler.h)
+ *     [extra_info: uleb128] the extra_info value
+ *     object, root_type and extra_info are repeated num_roots times
+ *
+ * type sample format
+ * type: TYPE_SAMPLE
+ * exinfo: one of TYPE_SAMPLE_HIT, TYPE_SAMPLE_USYM, TYPE_SAMPLE_UBIN, TYPE_SAMPLE_COUNTERS_DESC, TYPE_SAMPLE_COUNTERS
+ * if exinfo == TYPE_SAMPLE_HIT
+ *     [thread: sleb128] thread id as difference from ptr_base
+ *     [count: uleb128] number of following instruction addresses
+ *     [ip: sleb128]* instruction pointer as difference from ptr_base
+ *     [mbt_count: uleb128] number of managed backtrace frames
+ *     [method: sleb128]* MonoMethod* as a pointer difference from the last such
+ *     pointer or the buffer method_base (the first such method can be also indentified by ip, but this is not neccessarily true)
+ * if exinfo == TYPE_SAMPLE_USYM
+ *     [address: sleb128] symbol address as a difference from ptr_base
+ *     [size: uleb128] symbol size (may be 0 if unknown)
+ *     [name: string] symbol name
+ * if exinfo == TYPE_SAMPLE_UBIN
+ *     [address: sleb128] address where binary has been loaded
+ *     [offset: uleb128] file offset of mapping (the same file can be mapped multiple times)
+ *     [size: uleb128] memory size
+ *     [name: string] binary name
+ * if exinfo == TYPE_SAMPLE_COUNTERS_DESC
+ *     [len: uleb128] number of counters
+ *     for i = 0 to len
+ *             [section: uleb128] section of counter
+ *             if section == MONO_COUNTER_PERFCOUNTERS:
+ *                     [section_name: string] section name of counter
+ *             [name: string] name of counter
+ *             [type: byte] type of counter
+ *             [unit: byte] unit of counter
+ *             [variance: byte] variance of counter
+ *             [index: uleb128] unique index of counter
+ * if exinfo == TYPE_SAMPLE_COUNTERS
+ *     while true:
+ *             [index: uleb128] unique index of counter
+ *             if index == 0:
+ *                     break
+ *             [type: byte] type of counter value
+ *             if type == string:
+ *                     if value == null:
+ *                             [0: uleb128] 0 -> value is null
+ *                     else:
+ *                             [1: uleb128] 1 -> value is not null
+ *                             [value: string] counter value
+ *             else:
+ *                     [value: uleb128/sleb128/double] counter value, can be sleb128, uleb128 or double (determined by using type)
+ *
+ * type coverage format
+ * type: TYPE_COVERAGE
+ * exinfo: one of TYPE_COVERAGE_METHOD, TYPE_COVERAGE_STATEMENT, TYPE_COVERAGE_ASSEMBLY, TYPE_COVERAGE_CLASS
+ * if exinfo == TYPE_COVERAGE_METHOD
+ *  [assembly: string] name of assembly
+ *  [class: string] name of the class
+ *  [name: string] name of the method
+ *  [signature: string] the signature of the method
+ *  [filename: string] the file path of the file that contains this method
+ *  [token: uleb128] the method token
+ *  [method_id: uleb128] an ID for this data to associate with the buffers of TYPE_COVERAGE_STATEMENTS
+ *  [len: uleb128] the number of TYPE_COVERAGE_BUFFERS associated with this method
+ * if exinfo == TYPE_COVERAGE_STATEMENTS
+ *  [method_id: uleb128] an the TYPE_COVERAGE_METHOD buffer to associate this with
+ *  [offset: uleb128] the il offset relative to the previous offset
+ *  [counter: uleb128] the counter for this instruction
+ *  [line: uleb128] the line of filename containing this instruction
+ *  [column: uleb128] the column containing this instruction
+ * if exinfo == TYPE_COVERAGE_ASSEMBLY
+ *  [name: string] assembly name
+ *  [guid: string] assembly GUID
+ *  [filename: string] assembly filename
+ *  [number_of_methods: uleb128] the number of methods in this assembly
+ *  [fully_covered: uleb128] the number of fully covered methods
+ *  [partially_covered: uleb128] the number of partially covered methods
+ *    currently partially_covered will always be 0, and fully_covered is the
+ *    number of methods that are fully and partially covered.
+ * if exinfo == TYPE_COVERAGE_CLASS
+ *  [name: string] assembly name
+ *  [class: string] class name
+ *  [number_of_methods: uleb128] the number of methods in this class
+ *  [fully_covered: uleb128] the number of fully covered methods
+ *  [partially_covered: uleb128] the number of partially covered methods
+ *    currently partially_covered will always be 0, and fully_covered is the
+ *    number of methods that are fully and partially covered.
+ *
+ * type meta format:
+ * type: TYPE_META
+ * exinfo: one of: TYPE_SYNC_POINT
+ * if exinfo == TYPE_SYNC_POINT
+ *     [type: byte] MonoProfilerSyncPointType enum value
+ */
+
+// Pending data to be written to the log, for a single thread.
+// Threads periodically flush their own LogBuffers by calling safe_send
+typedef struct _LogBuffer LogBuffer;
+struct _LogBuffer {
+       // Next (older) LogBuffer in processing queue
+       LogBuffer *next;
+
+       uint64_t time_base;
+       uint64_t last_time;
+       uintptr_t ptr_base;
+       uintptr_t method_base;
+       uintptr_t last_method;
+       uintptr_t obj_base;
+       uintptr_t thread_id;
+
+       // Bytes allocated for this LogBuffer
+       int size;
+
+       // Start of currently unused space in buffer
+       unsigned char* cursor;
+
+       // Pointer to start-of-structure-plus-size (for convenience)
+       unsigned char* buf_end;
+
+       // Start of data in buffer. Contents follow "buffer format" described above.
+       unsigned char buf [1];
+};
+
+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;
+
+       // Methods referenced by events in `buffer`, see `MethodInfo`.
+       GPtrArray *methods;
+
+       // Current call depth for enter/leave events.
+       int call_depth;
+
+       // Indicates whether this thread is currently writing to its `buffer`.
+       gboolean busy;
+
+       // Has this thread written a thread end event to `buffer`?
+       gboolean ended;
+} MonoProfilerThread;
+
+static uintptr_t
+thread_id (void)
+{
+       return (uintptr_t) mono_native_thread_id_get ();
+}
+
+static uintptr_t
+process_id (void)
+{
+#ifdef HOST_WIN32
+       return (uintptr_t) GetCurrentProcessId ();
+#else
+       return (uintptr_t) getpid ();
+#endif
+}
+
+#ifdef __APPLE__
+static mach_timebase_info_data_t timebase_info;
+#elif defined (HOST_WIN32)
+static LARGE_INTEGER pcounter_freq;
+#endif
+
+#define TICKS_PER_SEC 1000000000LL
+
+static uint64_t
+current_time (void)
+{
+#ifdef __APPLE__
+       uint64_t time = mach_absolute_time ();
+
+       time *= timebase_info.numer;
+       time /= timebase_info.denom;
+
+       return time;
+#elif defined (HOST_WIN32)
+       LARGE_INTEGER value;
+
+       QueryPerformanceCounter (&value);
+
+       return value.QuadPart * TICKS_PER_SEC / pcounter_freq.QuadPart;
+#elif defined (CLOCK_MONOTONIC)
+       struct timespec tspec;
+
+       clock_gettime (CLOCK_MONOTONIC, &tspec);
+
+       return ((uint64_t) tspec.tv_sec * TICKS_PER_SEC + tspec.tv_nsec);
+#else
+       struct timeval tv;
+
+       gettimeofday (&tv, NULL);
+
+       return ((uint64_t) tv.tv_sec * TICKS_PER_SEC + tv.tv_usec * 1000);
+#endif
+}
+
+static int timer_overhead;
+
+static void
+init_time (void)
+{
+#ifdef __APPLE__
+       mach_timebase_info (&timebase_info);
+#elif defined (HOST_WIN32)
+       QueryPerformanceFrequency (&pcounter_freq);
+#endif
+
+       uint64_t time_start = current_time ();
+
+       for (int i = 0; i < 256; ++i)
+               current_time ();
+
+       uint64_t time_end = current_time ();
+
+       timer_overhead = (time_end - time_start) / 256;
+}
+
+/*
+ * These macros should be used when writing an event to a log buffer. They take
+ * care of a bunch of stuff that can be repetitive and error-prone, such as
+ * acquiring/releasing the buffer lock, incrementing the event counter,
+ * expanding the log buffer, processing requests, etc. They also create a scope
+ * so that it's harder to leak the LogBuffer pointer, which can be problematic
+ * as the pointer is unstable when the buffer lock isn't acquired.
+ */
+
+#define ENTER_LOG(COUNTER, BUFFER, SIZE) \
+       do { \
+               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 (thread__, (SIZE))
+
+#define EXIT_LOG_EXPLICIT(SEND, REQUESTS) \
+               thread__->busy = FALSE; \
+               if ((SEND)) \
+                       send_log_unsafe (TRUE); \
+               if (thread__->attached) \
+                       buffer_unlock (); \
+               if ((REQUESTS)) \
+                       process_requests (); \
+       } while (0)
+
+// Pass these to EXIT_LOG_EXPLICIT () for easier reading.
+#define DO_SEND TRUE
+#define NO_SEND FALSE
+#define DO_REQUESTS TRUE
+#define NO_REQUESTS FALSE
+
+#define EXIT_LOG EXIT_LOG_EXPLICIT (DO_SEND, DO_REQUESTS)
+
+static volatile gint32 buffer_rwlock_count;
+static volatile gpointer buffer_rwlock_exclusive;
+
+// Can be used recursively.
+static void
+buffer_lock (void)
+{
+       /*
+        * If the thread holding the exclusive lock tries to modify the
+        * reader count, just make it a no-op. This way, we also avoid
+        * invoking the GC safe point macros below, which could break if
+        * done from a thread that is currently the initiator of STW.
+        *
+        * In other words, we rely on the fact that the GC thread takes
+        * the exclusive lock in the gc_event () callback when the world
+        * is about to stop.
+        */
+       if (InterlockedReadPointer (&buffer_rwlock_exclusive) != (gpointer) thread_id ()) {
+               MONO_ENTER_GC_SAFE;
+
+               while (InterlockedReadPointer (&buffer_rwlock_exclusive))
+                       mono_thread_info_yield ();
+
+               InterlockedIncrement (&buffer_rwlock_count);
+
+               MONO_EXIT_GC_SAFE;
+       }
+
+       mono_memory_barrier ();
+}
+
+static void
+buffer_unlock (void)
+{
+       mono_memory_barrier ();
+
+       // See the comment in buffer_lock ().
+       if (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id ())
+               return;
+
+       g_assert (InterlockedRead (&buffer_rwlock_count) && "Why are we trying to decrement a zero reader count?");
+
+       InterlockedDecrement (&buffer_rwlock_count);
+}
+
+// Cannot be used recursively.
+static void
+buffer_lock_excl (void)
+{
+       gpointer tid = (gpointer) thread_id ();
+
+       g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) != tid && "Why are we taking the exclusive lock twice?");
+
+       MONO_ENTER_GC_SAFE;
+
+       while (InterlockedCompareExchangePointer (&buffer_rwlock_exclusive, tid, 0))
+               mono_thread_info_yield ();
+
+       while (InterlockedRead (&buffer_rwlock_count))
+               mono_thread_info_yield ();
+
+       MONO_EXIT_GC_SAFE;
+
+       mono_memory_barrier ();
+}
+
+static void
+buffer_unlock_excl (void)
+{
+       mono_memory_barrier ();
+
+       g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) && "Why is the exclusive lock not held?");
+       g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id () && "Why does another thread hold the exclusive lock?");
+       g_assert (!InterlockedRead (&buffer_rwlock_count) && "Why are there readers when the exclusive lock is held?");
+
+       InterlockedWritePointer (&buffer_rwlock_exclusive, NULL);
+}
+
+typedef struct _BinaryObject BinaryObject;
+struct _BinaryObject {
+       BinaryObject *next;
+       void *addr;
+       char *name;
+};
+
+struct _MonoProfiler {
+       FILE* file;
+#if defined (HAVE_SYS_ZLIB)
+       gzFile gzfile;
+#endif
+       char *args;
+       uint64_t startup_time;
+       int pipe_output;
+       int command_port;
+       int server_socket;
+       int pipes [2];
+       MonoNativeThreadId helper_thread;
+       MonoNativeThreadId writer_thread;
+       MonoNativeThreadId dumper_thread;
+       volatile gint32 run_writer_thread;
+       MonoLockFreeAllocSizeClass writer_entry_size_class;
+       MonoLockFreeAllocator writer_entry_allocator;
+       MonoLockFreeQueue writer_queue;
+       MonoSemType writer_queue_sem;
+       MonoConcurrentHashTable *method_table;
+       mono_mutex_t method_table_mutex;
+       volatile gint32 run_dumper_thread;
+       MonoLockFreeQueue dumper_queue;
+       MonoSemType dumper_queue_sem;
+       MonoLockFreeAllocSizeClass sample_size_class;
+       MonoLockFreeAllocator sample_allocator;
+       MonoLockFreeQueue sample_reuse_queue;
+       BinaryObject *binary_objects;
+       GPtrArray *coverage_filters;
+};
+
+typedef struct {
+       MonoLockFreeQueueNode node;
+       GPtrArray *methods;
+       LogBuffer *buffer;
+} WriterQueueEntry;
+
+#define WRITER_ENTRY_BLOCK_SIZE (mono_pagesize ())
+
+typedef struct {
+       MonoMethod *method;
+       MonoJitInfo *ji;
+       uint64_t time;
+} MethodInfo;
+
+#ifdef HOST_WIN32
+
+#define PROF_TLS_SET(VAL) (TlsSetValue (profiler_tls, (VAL)))
+#define PROF_TLS_GET() ((MonoProfilerThread *) TlsGetValue (profiler_tls))
+#define PROF_TLS_INIT() (profiler_tls = TlsAlloc ())
+#define PROF_TLS_FREE() (TlsFree (profiler_tls))
+
+static DWORD profiler_tls;
+
+#elif HAVE_KW_THREAD
+
+#define PROF_TLS_SET(VAL) (profiler_tls = (VAL))
+#define PROF_TLS_GET() (profiler_tls)
+#define PROF_TLS_INIT()
+#define PROF_TLS_FREE()
+
+static __thread MonoProfilerThread *profiler_tls;
+
+#else
+
+#define PROF_TLS_SET(VAL) (pthread_setspecific (profiler_tls, (VAL)))
+#define PROF_TLS_GET() ((MonoProfilerThread *) pthread_getspecific (profiler_tls))
+#define PROF_TLS_INIT() (pthread_key_create (&profiler_tls, NULL))
+#define PROF_TLS_FREE() (pthread_key_delete (profiler_tls))
+
+static pthread_key_t profiler_tls;
+
+#endif
+
+static char*
+pstrdup (const char *s)
+{
+       int len = strlen (s) + 1;
+       char *p = (char *) g_malloc (len);
+       memcpy (p, s, len);
+       return p;
+}
+
+static void *
+alloc_buffer (int size)
+{
+       return mono_valloc (NULL, size, MONO_MMAP_READ | MONO_MMAP_WRITE | MONO_MMAP_ANON | MONO_MMAP_PRIVATE, MONO_MEM_ACCOUNT_PROFILER);
+}
+
+static void
+free_buffer (void *buf, int size)
+{
+       mono_vfree (buf, size, MONO_MEM_ACCOUNT_PROFILER);
+}
+
+static LogBuffer*
+create_buffer (uintptr_t tid)
+{
+       LogBuffer* buf = (LogBuffer *) alloc_buffer (BUFFER_SIZE);
+
+       InterlockedIncrement (&buffer_allocations_ctr);
+
+       buf->size = BUFFER_SIZE;
+       buf->time_base = current_time ();
+       buf->last_time = buf->time_base;
+       buf->buf_end = (unsigned char *) buf + buf->size;
+       buf->cursor = buf->buf;
+       buf->thread_id = tid;
+
+       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)
+{
+       thread->buffer = create_buffer (thread->node.key);
+       thread->methods = NULL;
+}
+
+static void
+clear_hazard_pointers (MonoThreadHazardPointers *hp)
+{
+       mono_hazard_pointer_clear (hp, 0);
+       mono_hazard_pointer_clear (hp, 1);
+       mono_hazard_pointer_clear (hp, 2);
+}
+
+static MonoProfilerThread *
+init_thread (MonoProfiler *prof, gboolean add_to_lls)
+{
+       MonoProfilerThread *thread = PROF_TLS_GET ();
+
+       /*
+        * Sometimes we may try to initialize a thread twice. One example is the
+        * main thread: We initialize it when setting up the profiler, but we will
+        * also get a thread_start () callback for it. Another example is when
+        * attaching new threads to the runtime: We may get a gc_alloc () callback
+        * for that thread's thread object (where we initialize it), soon followed
+        * by a thread_start () callback.
+        *
+        * These cases are harmless anyhow. Just return if we've already done the
+        * initialization work.
+        */
+       if (thread)
+               return thread;
+
+       thread = g_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);
+
+       /*
+        * Some internal profiler threads don't need to be cleaned up
+        * by the main thread on shutdown.
+        */
+       if (add_to_lls) {
+               MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
+               g_assert (mono_lls_insert (&profiler_thread_list, hp, &thread->node) && "Why can't we insert the thread in the LLS?");
+               clear_hazard_pointers (hp);
+       }
+
+       PROF_TLS_SET (thread);
+
+       return thread;
+}
+
+// Only valid if init_thread () was called with add_to_lls = FALSE.
+static void
+deinit_thread (MonoProfilerThread *thread)
+{
+       g_assert (!thread->attached && "Why are we manually freeing an attached thread?");
+
+       g_free (thread);
+       PROF_TLS_SET (NULL);
+}
+
+// Only valid if init_thread () was called with add_to_lls = FALSE.
+static LogBuffer *
+ensure_logbuf_unsafe (MonoProfilerThread *thread, int bytes)
+{
+       LogBuffer *old = thread->buffer;
+
+       if (old && old->cursor + bytes + 100 < old->buf_end)
+               return old;
+
+       LogBuffer *new_ = create_buffer (thread->node.key);
+       new_->next = old;
+       thread->buffer = new_;
+
+       return new_;
+}
+
+static void
+encode_uleb128 (uint64_t value, uint8_t *buf, uint8_t **endbuf)
+{
+       uint8_t *p = buf;
+
+       do {
+               uint8_t b = value & 0x7f;
+               value >>= 7;
+
+               if (value != 0) /* more bytes to come */
+                       b |= 0x80;
+
+               *p ++ = b;
+       } while (value);
+
+       *endbuf = p;
+}
+
+static void
+encode_sleb128 (intptr_t value, uint8_t *buf, uint8_t **endbuf)
+{
+       int more = 1;
+       int negative = (value < 0);
+       unsigned int size = sizeof (intptr_t) * 8;
+       uint8_t byte;
+       uint8_t *p = buf;
+
+       while (more) {
+               byte = value & 0x7f;
+               value >>= 7;
+
+               /* the following is unnecessary if the
+                * implementation of >>= uses an arithmetic rather
+                * than logical shift for a signed left operand
+                */
+               if (negative)
+                       /* sign extend */
+                       value |= - ((intptr_t) 1 <<(size - 7));
+
+               /* sign bit of byte is second high order bit (0x40) */
+               if ((value == 0 && !(byte & 0x40)) ||
+                   (value == -1 && (byte & 0x40)))
+                       more = 0;
+               else
+                       byte |= 0x80;
+
+               *p ++= byte;
+       }
+
+       *endbuf = p;
+}
+
+static void
+emit_byte (LogBuffer *logbuffer, int value)
+{
+       logbuffer->cursor [0] = value;
+       logbuffer->cursor++;
+
+       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);
+
+       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;
+       encode_uleb128 (tdiff, logbuffer->cursor, &logbuffer->cursor);
+       logbuffer->last_time = value;
+
+       g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
+}
+
+static void
+emit_event_time (LogBuffer *logbuffer, int event, uint64_t time)
+{
+       emit_byte (logbuffer, event);
+       emit_time (logbuffer, time);
+}
+
+static void
+emit_event (LogBuffer *logbuffer, int event)
+{
+       emit_event_time (logbuffer, event, current_time ());
+}
+
+static void
+emit_svalue (LogBuffer *logbuffer, int64_t value)
+{
+       encode_sleb128 (value, logbuffer->cursor, &logbuffer->cursor);
+
+       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);
+
+       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);
+
+       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;
+       }
+
+       encode_sleb128 ((intptr_t) ((char *) method - (char *) logbuffer->last_method), logbuffer->cursor, &logbuffer->cursor);
+       logbuffer->last_method = (intptr_t) method;
+
+       g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
+}
+
+static void
+register_method_local (MonoMethod *method, MonoJitInfo *ji)
+{
+       MonoProfilerThread *thread = PROF_TLS_GET ();
+
+       if (!mono_conc_hashtable_lookup (thread->profiler->method_table, method)) {
+               MethodInfo *info = (MethodInfo *) g_malloc (sizeof (MethodInfo));
+
+               info->method = method;
+               info->ji = ji;
+               info->time = current_time ();
+
+               GPtrArray *arr = thread->methods ? thread->methods : (thread->methods = g_ptr_array_new ());
+               g_ptr_array_add (arr, info);
+       }
+}
+
+static void
+emit_method (LogBuffer *logbuffer, MonoMethod *method)
+{
+       register_method_local (method, NULL);
+       emit_method_inner (logbuffer, method);
+}
+
+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);
+
+       g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
+}
+
+static void
+emit_string (LogBuffer *logbuffer, const char *str, size_t size)
+{
+       size_t i = 0;
+       if (str) {
+               for (; i < size; i++) {
+                       if (str[i] == '\0')
+                               break;
+                       emit_byte (logbuffer, str [i]);
+               }
+       }
+       emit_byte (logbuffer, '\0');
+}
+
+static void
+emit_double (LogBuffer *logbuffer, double value)
+{
+       int i;
+       unsigned char buffer[8];
+       memcpy (buffer, &value, 8);
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+       for (i = 7; i >= 0; i--)
+#else
+       for (i = 0; i < 8; i++)
+#endif
+               emit_byte (logbuffer, buffer[i]);
+}
+
+static char*
+write_int16 (char *buf, int32_t value)
+{
+       int i;
+       for (i = 0; i < 2; ++i) {
+               buf [i] = value;
+               value >>= 8;
+       }
+       return buf + 2;
+}
+
+static char*
+write_int32 (char *buf, int32_t value)
+{
+       int i;
+       for (i = 0; i < 4; ++i) {
+               buf [i] = value;
+               value >>= 8;
+       }
+       return buf + 4;
+}
+
+static char*
+write_int64 (char *buf, int64_t value)
+{
+       int i;
+       for (i = 0; i < 8; ++i) {
+               buf [i] = value;
+               value >>= 8;
+       }
+       return buf + 8;
+}
+
+static char *
+write_header_string (char *p, const char *str)
+{
+       size_t len = strlen (str) + 1;
+
+       p = write_int32 (p, len);
+       strcpy (p, str);
+
+       return p + len;
+}
+
+static void
+dump_header (MonoProfiler *profiler)
+{
+       const char *args = profiler->args;
+       const char *arch = mono_config_get_cpu ();
+       const char *os = mono_config_get_os ();
+
+       char *hbuf = g_malloc (
+               sizeof (gint32) /* header id */ +
+               sizeof (gint8) /* major version */ +
+               sizeof (gint8) /* minor version */ +
+               sizeof (gint8) /* data version */ +
+               sizeof (gint8) /* word size */ +
+               sizeof (gint64) /* startup time */ +
+               sizeof (gint32) /* timer overhead */ +
+               sizeof (gint32) /* flags */ +
+               sizeof (gint32) /* process id */ +
+               sizeof (gint16) /* command port */ +
+               sizeof (gint32) + strlen (args) + 1 /* arguments */ +
+               sizeof (gint32) + strlen (arch) + 1 /* architecture */ +
+               sizeof (gint32) + strlen (os) + 1 /* operating system */
+       );
+       char *p = hbuf;
+
+       p = write_int32 (p, LOG_HEADER_ID);
+       *p++ = LOG_VERSION_MAJOR;
+       *p++ = LOG_VERSION_MINOR;
+       *p++ = LOG_DATA_VERSION;
+       *p++ = sizeof (void *);
+       p = write_int64 (p, ((uint64_t) time (NULL)) * 1000);
+       p = write_int32 (p, timer_overhead);
+       p = write_int32 (p, 0); /* flags */
+       p = write_int32 (p, process_id ());
+       p = write_int16 (p, profiler->command_port);
+       p = write_header_string (p, args);
+       p = write_header_string (p, arch);
+       p = write_header_string (p, os);
+
+#if defined (HAVE_SYS_ZLIB)
+       if (profiler->gzfile) {
+               gzwrite (profiler->gzfile, hbuf, p - hbuf);
+       } else
+#endif
+       {
+               fwrite (hbuf, p - hbuf, 1, profiler->file);
+               fflush (profiler->file);
+       }
+
+       g_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 (MonoProfilerThread *thread)
+{
+       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 (&thread->profiler->writer_queue, &entry->node);
+       mono_os_sem_post (&thread->profiler->writer_queue_sem);
+}
+
+static void
+free_thread (gpointer p)
+{
+       MonoProfilerThread *thread = p;
+
+       if (!thread->ended) {
+               /*
+                * 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);
+
+               LogBuffer *buf = ensure_logbuf_unsafe (thread,
+                       EVENT_SIZE /* event */ +
+                       BYTE_SIZE /* type */ +
+                       LEB128_SIZE /* tid */
+               );
+
+               emit_event (buf, TYPE_END_UNLOAD | TYPE_METADATA);
+               emit_byte (buf, TYPE_THREAD);
+               emit_ptr (buf, (void *) thread->node.key);
+       }
+
+       send_buffer (thread);
+
+       g_free (thread);
+}
+
+static void
+remove_thread (MonoProfilerThread *thread)
+{
+       MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
+
+       if (mono_lls_remove (&profiler_thread_list, hp, &thread->node))
+               mono_thread_hazardous_try_free (thread, free_thread);
+
+       clear_hazard_pointers (hp);
+}
+
+static void
+dump_buffer (MonoProfiler *profiler, LogBuffer *buf)
+{
+       char hbuf [128];
+       char *p = hbuf;
+
+       if (buf->next)
+               dump_buffer (profiler, buf->next);
+
+       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
+#endif
+               {
+                       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);
+}
+
+static void
+dump_buffer_threadless (MonoProfiler *profiler, LogBuffer *buf)
+{
+       for (LogBuffer *iter = buf; iter; iter = iter->next)
+               iter->thread_id = 0;
+
+       dump_buffer (profiler, buf);
+}
+
+static void
+process_requests (void)
+{
+       if (heapshot_requested)
+               mono_gc_collect (mono_gc_max_generation ());
+}
+
+// Only valid if init_thread () was called with add_to_lls = FALSE.
+static void
+send_log_unsafe (gboolean if_needed)
+{
+       MonoProfilerThread *thread = PROF_TLS_GET ();
+
+       if (!if_needed || (if_needed && thread->buffer->next)) {
+               if (!thread->attached)
+                       for (LogBuffer *iter = thread->buffer; iter; iter = iter->next)
+                               iter->thread_id = 0;
+
+               send_buffer (thread);
+               init_buffer_state (thread);
+       }
+}
+
+// Assumes that the exclusive lock is held.
+static void
+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) {
+               g_assert (thread->attached && "Why is a thread in the LLS not attached?");
+
+               send_buffer (thread);
+               init_buffer_state (thread);
+       } MONO_LLS_FOREACH_SAFE_END
+}
+
+// Assumes that the exclusive lock is held.
+static void
+sync_point_mark (MonoProfilerSyncPointType type)
+{
+       g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id () && "Why don't we hold the exclusive lock?");
+
+       ENTER_LOG (&sync_points_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               LEB128_SIZE /* type */
+       );
+
+       emit_event (logbuffer, TYPE_META | TYPE_SYNC_POINT);
+       emit_byte (logbuffer, type);
+
+       EXIT_LOG_EXPLICIT (NO_SEND, NO_REQUESTS);
+
+       send_log_unsafe (FALSE);
+}
+
+// Assumes that the exclusive lock is held.
+static void
+sync_point (MonoProfilerSyncPointType type)
+{
+       sync_point_flush ();
+       sync_point_mark (type);
+}
+
+static int
+gc_reference (MonoObject *obj, MonoClass *klass, uintptr_t size, uintptr_t num, MonoObject **refs, uintptr_t *offsets, void *data)
+{
+       /* account for object alignment in the heap */
+       size += 7;
+       size &= ~7;
+
+       ENTER_LOG (&heap_objects_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               LEB128_SIZE /* obj */ +
+               LEB128_SIZE /* klass */ +
+               LEB128_SIZE /* size */ +
+               LEB128_SIZE /* num */ +
+               num * (
+                       LEB128_SIZE /* offset */ +
+                       LEB128_SIZE /* ref */
+               )
+       );
+
+       emit_event (logbuffer, TYPE_HEAP_OBJECT | TYPE_HEAP);
+       emit_obj (logbuffer, obj);
+       emit_ptr (logbuffer, klass);
+       emit_value (logbuffer, size);
+       emit_value (logbuffer, num);
+
+       uintptr_t last_offset = 0;
+
+       for (int i = 0; i < num; ++i) {
+               emit_value (logbuffer, offsets [i] - last_offset);
+               last_offset = offsets [i];
+               emit_obj (logbuffer, refs [i]);
+       }
+
+       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+
+       return 0;
+}
+
+static unsigned int hs_mode_ms = 0;
+static unsigned int hs_mode_gc = 0;
+static unsigned int hs_mode_ondemand = 0;
+static unsigned int gc_count = 0;
+static uint64_t last_hs_time = 0;
+static gboolean do_heap_walk = FALSE;
+
+static void
+heap_walk (MonoProfiler *profiler)
+{
+       ENTER_LOG (&heap_starts_ctr, logbuffer,
+               EVENT_SIZE /* event */
+       );
+
+       emit_event (logbuffer, TYPE_HEAP_START | TYPE_HEAP);
+
+       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+
+       mono_gc_walk_heap (0, gc_reference, NULL);
+
+       ENTER_LOG (&heap_ends_ctr, logbuffer,
+               EVENT_SIZE /* event */
+       );
+
+       emit_event (logbuffer, TYPE_HEAP_END | TYPE_HEAP);
+
+       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+}
+
+static void
+gc_roots (MonoProfiler *prof, int num, void **objects, int *root_types, uintptr_t *extra_info)
+{
+       ENTER_LOG (&heap_roots_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               LEB128_SIZE /* num */ +
+               LEB128_SIZE /* collections */ +
+               num * (
+                       LEB128_SIZE /* object */ +
+                       LEB128_SIZE /* root type */ +
+                       LEB128_SIZE /* extra info */
+               )
+       );
+
+       emit_event (logbuffer, TYPE_HEAP_ROOT | TYPE_HEAP);
+       emit_value (logbuffer, num);
+       emit_value (logbuffer, mono_gc_collection_count (mono_gc_max_generation ()));
+
+       for (int i = 0; i < num; ++i) {
+               emit_obj (logbuffer, objects [i]);
+               emit_byte (logbuffer, root_types [i]);
+               emit_value (logbuffer, extra_info [i]);
+       }
+
+       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+}
+
+static void
+gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation)
+{
+       ENTER_LOG (&gc_events_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               BYTE_SIZE /* gc event */ +
+               BYTE_SIZE /* generation */
+       );
+
+       emit_event (logbuffer, TYPE_GC_EVENT | TYPE_GC);
+       emit_byte (logbuffer, ev);
+       emit_byte (logbuffer, generation);
+
+       EXIT_LOG_EXPLICIT (NO_SEND, NO_REQUESTS);
+
+       switch (ev) {
+       case MONO_GC_EVENT_START:
+               if (generation == mono_gc_max_generation ())
+                       gc_count++;
+
+               uint64_t now = current_time ();
+
+               if (hs_mode_ms && (now - last_hs_time) / 1000 * 1000 >= hs_mode_ms)
+                       do_heap_walk = TRUE;
+               else if (hs_mode_gc && !(gc_count % hs_mode_gc))
+                       do_heap_walk = TRUE;
+               else if (hs_mode_ondemand)
+                       do_heap_walk = heapshot_requested;
+               else if (!hs_mode_ms && !hs_mode_gc && generation == mono_gc_max_generation ())
+                       do_heap_walk = TRUE;
+               break;
+       case MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED:
+               /*
+                * Ensure that no thread can be in the middle of writing to
+                * a buffer when the world stops...
+                */
+               buffer_lock_excl ();
+               break;
+       case MONO_GC_EVENT_POST_STOP_WORLD:
+               /*
+                * ... So that we now have a consistent view of all buffers.
+                * This allows us to flush them. We need to do this because
+                * they may contain object allocation events that need to be
+                * committed to the log file before any object move events
+                * that will be produced during this GC.
+                */
+               sync_point (SYNC_POINT_WORLD_STOP);
+               break;
+       case MONO_GC_EVENT_PRE_START_WORLD:
+               if (do_heap_shot && do_heap_walk) {
+                       heap_walk (profiler);
+
+                       do_heap_walk = FALSE;
+                       heapshot_requested = 0;
+                       last_hs_time = current_time ();
+               }
+               break;
+       case MONO_GC_EVENT_POST_START_WORLD_UNLOCKED:
+               /*
+                * Similarly, we must now make sure that any object moves
+                * written to the GC thread's buffer are flushed. Otherwise,
+                * object allocation events for certain addresses could come
+                * after the move events that made those addresses available.
+                */
+               sync_point_mark (SYNC_POINT_WORLD_START);
+
+               /*
+                * Finally, it is safe to allow other threads to write to
+                * their buffers again.
+                */
+               buffer_unlock_excl ();
+               break;
+       default:
+               break;
+       }
+}
+
+static void
+gc_resize (MonoProfiler *profiler, int64_t new_size)
+{
+       ENTER_LOG (&gc_resizes_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               LEB128_SIZE /* new size */
+       );
+
+       emit_event (logbuffer, TYPE_GC_RESIZE | TYPE_GC);
+       emit_value (logbuffer, new_size);
+
+       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+}
+
+typedef struct {
+       int count;
+       MonoMethod* methods [MAX_FRAMES];
+       int32_t il_offsets [MAX_FRAMES];
+       int32_t native_offsets [MAX_FRAMES];
+} FrameData;
+
+static int num_frames = MAX_FRAMES;
+
+static mono_bool
+walk_stack (MonoMethod *method, int32_t native_offset, int32_t il_offset, mono_bool managed, void* data)
+{
+       FrameData *frame = (FrameData *)data;
+       if (method && frame->count < num_frames) {
+               frame->il_offsets [frame->count] = il_offset;
+               frame->native_offsets [frame->count] = native_offset;
+               frame->methods [frame->count++] = method;
+               //printf ("In %d %s at %d (native: %d)\n", frame->count, mono_method_get_name (method), il_offset, native_offset);
+       }
+       return frame->count == num_frames;
+}
+
+/*
+ * a note about stack walks: they can cause more profiler events to fire,
+ * so we need to make sure they don't happen after we started emitting an
+ * event, hence the collect_bt/emit_bt split.
+ */
+static void
+collect_bt (FrameData *data)
+{
+       data->count = 0;
+       mono_stack_walk_no_il (walk_stack, data);
+}
+
+static void
+emit_bt (MonoProfiler *prof, LogBuffer *logbuffer, FrameData *data)
+{
+       /* FIXME: this is actually tons of data and we should
+        * just output it the first time and use an id the next
+        */
+       if (data->count > num_frames)
+               printf ("bad num frames: %d\n", data->count);
+       emit_value (logbuffer, data->count);
+       //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 (logbuffer, data->methods [--data->count]);
+       }
+}
+
+static void
+gc_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
+{
+       init_thread (prof, TRUE);
+
+       int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces) ? TYPE_ALLOC_BT : 0;
+       FrameData data;
+       uintptr_t len = mono_object_get_size (obj);
+       /* account for object alignment in the heap */
+       len += 7;
+       len &= ~7;
+
+       if (do_bt)
+               collect_bt (&data);
+
+       ENTER_LOG (&gc_allocs_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               LEB128_SIZE /* klass */ +
+               LEB128_SIZE /* obj */ +
+               LEB128_SIZE /* size */ +
+               (do_bt ? (
+                       LEB128_SIZE /* count */ +
+                       data.count * (
+                               LEB128_SIZE /* method */
+                       )
+               ) : 0)
+       );
+
+       emit_event (logbuffer, do_bt | TYPE_ALLOC);
+       emit_ptr (logbuffer, klass);
+       emit_obj (logbuffer, obj);
+       emit_value (logbuffer, len);
+
+       if (do_bt)
+               emit_bt (prof, logbuffer, &data);
+
+       EXIT_LOG;
+}
+
+static void
+gc_moves (MonoProfiler *prof, void **objects, int num)
+{
+       ENTER_LOG (&gc_moves_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               LEB128_SIZE /* num */ +
+               num * (
+                       LEB128_SIZE /* object */
+               )
+       );
+
+       emit_event (logbuffer, TYPE_GC_MOVE | TYPE_GC);
+       emit_value (logbuffer, num);
+
+       for (int i = 0; i < num; ++i)
+               emit_obj (logbuffer, objects [i]);
+
+       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+}
+
+static void
+gc_handle (MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj)
+{
+       int do_bt = nocalls && InterlockedRead (&runtime_inited) && !notraces;
+       FrameData data;
+
+       if (do_bt)
+               collect_bt (&data);
+
+       gint32 *ctr = op == MONO_PROFILER_GC_HANDLE_CREATED ? &gc_handle_creations_ctr : &gc_handle_deletions_ctr;
+
+       ENTER_LOG (ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               LEB128_SIZE /* type */ +
+               LEB128_SIZE /* handle */ +
+               (op == MONO_PROFILER_GC_HANDLE_CREATED ? (
+                       LEB128_SIZE /* obj */
+               ) : 0) +
+               (do_bt ? (
+                       LEB128_SIZE /* count */ +
+                       data.count * (
+                               LEB128_SIZE /* method */
+                       )
+               ) : 0)
+       );
+
+       if (op == MONO_PROFILER_GC_HANDLE_CREATED)
+               emit_event (logbuffer, (do_bt ? TYPE_GC_HANDLE_CREATED_BT : TYPE_GC_HANDLE_CREATED) | TYPE_GC);
+       else if (op == MONO_PROFILER_GC_HANDLE_DESTROYED)
+               emit_event (logbuffer, (do_bt ? TYPE_GC_HANDLE_DESTROYED_BT : TYPE_GC_HANDLE_DESTROYED) | TYPE_GC);
+       else
+               g_assert_not_reached ();
+
+       emit_value (logbuffer, type);
+       emit_value (logbuffer, handle);
+
+       if (op == MONO_PROFILER_GC_HANDLE_CREATED)
+               emit_obj (logbuffer, obj);
+
+       if (do_bt)
+               emit_bt (prof, logbuffer, &data);
+
+       EXIT_LOG;
+}
+
+static void
+finalize_begin (MonoProfiler *prof)
+{
+       ENTER_LOG (&finalize_begins_ctr, buf,
+               EVENT_SIZE /* event */
+       );
+
+       emit_event (buf, TYPE_GC_FINALIZE_START | TYPE_GC);
+
+       EXIT_LOG;
+}
+
+static void
+finalize_end (MonoProfiler *prof)
+{
+       ENTER_LOG (&finalize_ends_ctr, buf,
+               EVENT_SIZE /* event */
+       );
+
+       emit_event (buf, TYPE_GC_FINALIZE_END | TYPE_GC);
+
+       EXIT_LOG;
+}
+
+static void
+finalize_object_begin (MonoProfiler *prof, MonoObject *obj)
+{
+       ENTER_LOG (&finalize_object_begins_ctr, buf,
+               EVENT_SIZE /* event */ +
+               LEB128_SIZE /* obj */
+       );
+
+       emit_event (buf, TYPE_GC_FINALIZE_OBJECT_START | TYPE_GC);
+       emit_obj (buf, obj);
+
+       EXIT_LOG;
+}
+
+static void
+finalize_object_end (MonoProfiler *prof, MonoObject *obj)
+{
+       ENTER_LOG (&finalize_object_ends_ctr, buf,
+               EVENT_SIZE /* event */ +
+               LEB128_SIZE /* obj */
+       );
+
+       emit_event (buf, TYPE_GC_FINALIZE_OBJECT_END | TYPE_GC);
+       emit_obj (buf, obj);
+
+       EXIT_LOG;
+}
+
+static char*
+push_nesting (char *p, MonoClass *klass)
+{
+       MonoClass *nesting;
+       const char *name;
+       const char *nspace;
+       nesting = mono_class_get_nesting_type (klass);
+       if (nesting) {
+               p = push_nesting (p, nesting);
+               *p++ = '/';
+               *p = 0;
+       }
+       name = mono_class_get_name (klass);
+       nspace = mono_class_get_namespace (klass);
+       if (*nspace) {
+               strcpy (p, nspace);
+               p += strlen (nspace);
+               *p++ = '.';
+               *p = 0;
+       }
+       strcpy (p, name);
+       p += strlen (name);
+       return p;
+}
+
+static char*
+type_name (MonoClass *klass)
+{
+       char buf [1024];
+       char *p;
+       push_nesting (buf, klass);
+       p = (char *) g_malloc (strlen (buf) + 1);
+       strcpy (p, buf);
+       return p;
+}
+
+static void
+image_loaded (MonoProfiler *prof, MonoImage *image, int result)
+{
+       if (result != MONO_PROFILE_OK)
+               return;
+
+       const char *name = mono_image_get_filename (image);
+       int nlen = strlen (name) + 1;
+
+       ENTER_LOG (&image_loads_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               BYTE_SIZE /* type */ +
+               LEB128_SIZE /* image */ +
+               nlen /* name */
+       );
+
+       emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
+       emit_byte (logbuffer, TYPE_IMAGE);
+       emit_ptr (logbuffer, image);
+       memcpy (logbuffer->cursor, name, nlen);
+       logbuffer->cursor += nlen;
+
+       EXIT_LOG;
+}
+
+static void
+image_unloaded (MonoProfiler *prof, MonoImage *image)
+{
+       const char *name = mono_image_get_filename (image);
+       int nlen = strlen (name) + 1;
+
+       ENTER_LOG (&image_unloads_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               BYTE_SIZE /* type */ +
+               LEB128_SIZE /* image */ +
+               nlen /* name */
+       );
+
+       emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
+       emit_byte (logbuffer, TYPE_IMAGE);
+       emit_ptr (logbuffer, image);
+       memcpy (logbuffer->cursor, name, nlen);
+       logbuffer->cursor += nlen;
+
+       EXIT_LOG;
+}
+
+static void
+assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly, int result)
+{
+       if (result != MONO_PROFILE_OK)
+               return;
+
+       char *name = mono_stringify_assembly_name (mono_assembly_get_name (assembly));
+       int nlen = strlen (name) + 1;
+
+       ENTER_LOG (&assembly_loads_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               BYTE_SIZE /* type */ +
+               LEB128_SIZE /* assembly */ +
+               nlen /* name */
+       );
+
+       emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
+       emit_byte (logbuffer, TYPE_ASSEMBLY);
+       emit_ptr (logbuffer, assembly);
+       memcpy (logbuffer->cursor, name, nlen);
+       logbuffer->cursor += nlen;
+
+       EXIT_LOG;
+
+       mono_free (name);
+}
+
+static void
+assembly_unloaded (MonoProfiler *prof, MonoAssembly *assembly)
+{
+       char *name = mono_stringify_assembly_name (mono_assembly_get_name (assembly));
+       int nlen = strlen (name) + 1;
+
+       ENTER_LOG (&assembly_unloads_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               BYTE_SIZE /* type */ +
+               LEB128_SIZE /* assembly */ +
+               nlen /* name */
+       );
+
+       emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
+       emit_byte (logbuffer, TYPE_ASSEMBLY);
+       emit_ptr (logbuffer, assembly);
+       memcpy (logbuffer->cursor, name, nlen);
+       logbuffer->cursor += nlen;
+
+       EXIT_LOG;
+
+       mono_free (name);
+}
+
+static void
+class_loaded (MonoProfiler *prof, MonoClass *klass, int result)
+{
+       if (result != MONO_PROFILE_OK)
+               return;
+
+       char *name;
+
+       if (InterlockedRead (&runtime_inited))
+               name = mono_type_get_name (mono_class_get_type (klass));
+       else
+               name = type_name (klass);
+
+       int nlen = strlen (name) + 1;
+       MonoImage *image = mono_class_get_image (klass);
+
+       ENTER_LOG (&class_loads_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               BYTE_SIZE /* type */ +
+               LEB128_SIZE /* klass */ +
+               LEB128_SIZE /* image */ +
+               nlen /* name */
+       );
+
+       emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
+       emit_byte (logbuffer, TYPE_CLASS);
+       emit_ptr (logbuffer, klass);
+       emit_ptr (logbuffer, image);
+       memcpy (logbuffer->cursor, name, nlen);
+       logbuffer->cursor += nlen;
+
+       EXIT_LOG;
+
+       if (runtime_inited)
+               mono_free (name);
+       else
+               g_free (name);
+}
+
+static void
+class_unloaded (MonoProfiler *prof, MonoClass *klass)
+{
+       char *name;
+
+       if (InterlockedRead (&runtime_inited))
+               name = mono_type_get_name (mono_class_get_type (klass));
+       else
+               name = type_name (klass);
+
+       int nlen = strlen (name) + 1;
+       MonoImage *image = mono_class_get_image (klass);
+
+       ENTER_LOG (&class_unloads_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               BYTE_SIZE /* type */ +
+               LEB128_SIZE /* klass */ +
+               LEB128_SIZE /* image */ +
+               nlen /* name */
+       );
+
+       emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
+       emit_byte (logbuffer, TYPE_CLASS);
+       emit_ptr (logbuffer, klass);
+       emit_ptr (logbuffer, image);
+       memcpy (logbuffer->cursor, name, nlen);
+       logbuffer->cursor += nlen;
+
+       EXIT_LOG;
+
+       if (runtime_inited)
+               mono_free (name);
+       else
+               g_free (name);
+}
+
+static void process_method_enter_coverage (MonoProfiler *prof, MonoMethod *method);
+
+static void
+method_enter (MonoProfiler *prof, MonoMethod *method)
+{
+       process_method_enter_coverage (prof, method);
+
+       if (!only_coverage && PROF_TLS_GET ()->call_depth++ <= max_call_depth) {
+               ENTER_LOG (&method_entries_ctr, logbuffer,
+                       EVENT_SIZE /* event */ +
+                       LEB128_SIZE /* method */
+               );
+
+               emit_event (logbuffer, TYPE_ENTER | TYPE_METHOD);
+               emit_method (logbuffer, method);
+
+               EXIT_LOG;
+       }
+}
+
+static void
+method_leave (MonoProfiler *prof, MonoMethod *method)
+{
+       if (!only_coverage && --PROF_TLS_GET ()->call_depth <= max_call_depth) {
+               ENTER_LOG (&method_exits_ctr, logbuffer,
+                       EVENT_SIZE /* event */ +
+                       LEB128_SIZE /* method */
+               );
+
+               emit_event (logbuffer, TYPE_LEAVE | TYPE_METHOD);
+               emit_method (logbuffer, method);
+
+               EXIT_LOG;
+       }
+}
+
+static void
+method_exc_leave (MonoProfiler *prof, MonoMethod *method)
+{
+       if (!only_coverage && !nocalls && --PROF_TLS_GET ()->call_depth <= max_call_depth) {
+               ENTER_LOG (&method_exception_exits_ctr, logbuffer,
+                       EVENT_SIZE /* event */ +
+                       LEB128_SIZE /* method */
+               );
+
+               emit_event (logbuffer, TYPE_EXC_LEAVE | TYPE_METHOD);
+               emit_method (logbuffer, method);
+
+               EXIT_LOG;
+       }
+}
+
+static void
+method_jitted (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *ji, int result)
+{
+       if (result != MONO_PROFILE_OK)
+               return;
+
+       register_method_local (method, ji);
+
+       process_requests ();
+}
+
+static void
+code_buffer_new (MonoProfiler *prof, void *buffer, int size, MonoProfilerCodeBufferType type, void *data)
+{
+       char *name;
+       int nlen;
+
+       if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
+               name = (char *) data;
+               nlen = strlen (name) + 1;
+       } else {
+               name = NULL;
+               nlen = 0;
+       }
+
+       ENTER_LOG (&code_buffers_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               BYTE_SIZE /* type */ +
+               LEB128_SIZE /* buffer */ +
+               LEB128_SIZE /* size */ +
+               (name ? (
+                       nlen /* name */
+               ) : 0)
+       );
+
+       emit_event (logbuffer, TYPE_JITHELPER | TYPE_RUNTIME);
+       emit_byte (logbuffer, type);
+       emit_ptr (logbuffer, buffer);
+       emit_value (logbuffer, size);
+
+       if (name) {
+               memcpy (logbuffer->cursor, name, nlen);
+               logbuffer->cursor += nlen;
+       }
+
+       EXIT_LOG;
+}
+
+static void
+throw_exc (MonoProfiler *prof, MonoObject *object)
+{
+       int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces) ? TYPE_THROW_BT : 0;
+       FrameData data;
+
+       if (do_bt)
+               collect_bt (&data);
+
+       ENTER_LOG (&exception_throws_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               LEB128_SIZE /* object */ +
+               (do_bt ? (
+                       LEB128_SIZE /* count */ +
+                       data.count * (
+                               LEB128_SIZE /* method */
+                       )
+               ) : 0)
+       );
+
+       emit_event (logbuffer, do_bt | TYPE_EXCEPTION);
+       emit_obj (logbuffer, object);
+
+       if (do_bt)
+               emit_bt (prof, logbuffer, &data);
+
+       EXIT_LOG;
+}
+
+static void
+clause_exc (MonoProfiler *prof, MonoMethod *method, int clause_type, int clause_num)
+{
+       ENTER_LOG (&exception_clauses_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               BYTE_SIZE /* clause type */ +
+               LEB128_SIZE /* clause num */ +
+               LEB128_SIZE /* method */
+       );
+
+       emit_event (logbuffer, TYPE_EXCEPTION | TYPE_CLAUSE);
+       emit_byte (logbuffer, clause_type);
+       emit_value (logbuffer, clause_num);
+       emit_method (logbuffer, method);
+
+       EXIT_LOG;
+}
+
+static void
+monitor_event (MonoProfiler *profiler, MonoObject *object, MonoProfilerMonitorEvent event)
+{
+       int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces && event == MONO_PROFILER_MONITOR_CONTENTION) ? TYPE_MONITOR_BT : 0;
+       FrameData data;
+
+       if (do_bt)
+               collect_bt (&data);
+
+       gint32 *ctr;
+
+       switch (event) {
+       case MONO_PROFILER_MONITOR_CONTENTION:
+               ctr = &monitor_contentions_ctr;
+               break;
+       case MONO_PROFILER_MONITOR_DONE:
+               ctr = &monitor_acquisitions_ctr;
+               break;
+       case MONO_PROFILER_MONITOR_FAIL:
+               ctr = &monitor_failures_ctr;
+               break;
+       default:
+               g_assert_not_reached ();
+               break;
+       }
+
+       ENTER_LOG (ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               LEB128_SIZE /* object */ +
+               (do_bt ? (
+                       LEB128_SIZE /* count */ +
+                       data.count * (
+                               LEB128_SIZE /* method */
+                       )
+               ) : 0)
+       );
+
+       emit_event (logbuffer, (event << 4) | do_bt | TYPE_MONITOR);
+       emit_obj (logbuffer, object);
+
+       if (do_bt)
+               emit_bt (profiler, logbuffer, &data);
+
+       EXIT_LOG;
+}
+
+static void
+thread_start (MonoProfiler *prof, uintptr_t tid)
+{
+       init_thread (prof, TRUE);
+
+       ENTER_LOG (&thread_starts_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               BYTE_SIZE /* type */ +
+               LEB128_SIZE /* tid */
+       );
+
+       emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
+       emit_byte (logbuffer, TYPE_THREAD);
+       emit_ptr (logbuffer, (void*) tid);
+
+       EXIT_LOG;
+}
+
+static void
+thread_end (MonoProfiler *prof, uintptr_t tid)
+{
+       ENTER_LOG (&thread_ends_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               BYTE_SIZE /* type */ +
+               LEB128_SIZE /* tid */
+       );
+
+       emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
+       emit_byte (logbuffer, TYPE_THREAD);
+       emit_ptr (logbuffer, (void*) tid);
+
+       EXIT_LOG_EXPLICIT (NO_SEND, NO_REQUESTS);
+
+       MonoProfilerThread *thread = PROF_TLS_GET ();
+
+       thread->ended = TRUE;
+       remove_thread (thread);
+
+       PROF_TLS_SET (NULL);
+}
+
+static void
+thread_name (MonoProfiler *prof, uintptr_t tid, const char *name)
+{
+       int len = strlen (name) + 1;
+
+       ENTER_LOG (&thread_names_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               BYTE_SIZE /* type */ +
+               LEB128_SIZE /* tid */ +
+               len /* name */
+       );
+
+       emit_event (logbuffer, TYPE_METADATA);
+       emit_byte (logbuffer, TYPE_THREAD);
+       emit_ptr (logbuffer, (void*)tid);
+       memcpy (logbuffer->cursor, name, len);
+       logbuffer->cursor += len;
+
+       EXIT_LOG;
+}
+
+static void
+domain_loaded (MonoProfiler *prof, MonoDomain *domain, int result)
+{
+       if (result != MONO_PROFILE_OK)
+               return;
+
+       ENTER_LOG (&domain_loads_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               BYTE_SIZE /* type */ +
+               LEB128_SIZE /* domain id */
+       );
+
+       emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
+       emit_byte (logbuffer, TYPE_DOMAIN);
+       emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
+
+       EXIT_LOG;
+}
+
+static void
+domain_unloaded (MonoProfiler *prof, MonoDomain *domain)
+{
+       ENTER_LOG (&domain_unloads_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               BYTE_SIZE /* type */ +
+               LEB128_SIZE /* domain id */
+       );
+
+       emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
+       emit_byte (logbuffer, TYPE_DOMAIN);
+       emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
+
+       EXIT_LOG;
+}
+
+static void
+domain_name (MonoProfiler *prof, MonoDomain *domain, const char *name)
+{
+       int nlen = strlen (name) + 1;
+
+       ENTER_LOG (&domain_names_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               BYTE_SIZE /* type */ +
+               LEB128_SIZE /* domain id */ +
+               nlen /* name */
+       );
+
+       emit_event (logbuffer, TYPE_METADATA);
+       emit_byte (logbuffer, TYPE_DOMAIN);
+       emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
+       memcpy (logbuffer->cursor, name, nlen);
+       logbuffer->cursor += nlen;
+
+       EXIT_LOG;
+}
+
+static void
+context_loaded (MonoProfiler *prof, MonoAppContext *context)
+{
+       ENTER_LOG (&context_loads_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               BYTE_SIZE /* type */ +
+               LEB128_SIZE /* context id */ +
+               LEB128_SIZE /* domain id */
+       );
+
+       emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
+       emit_byte (logbuffer, TYPE_CONTEXT);
+       emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_id (context));
+       emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_domain_id (context));
+
+       EXIT_LOG;
+}
+
+static void
+context_unloaded (MonoProfiler *prof, MonoAppContext *context)
+{
+       ENTER_LOG (&context_unloads_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               BYTE_SIZE /* type */ +
+               LEB128_SIZE /* context id */ +
+               LEB128_SIZE /* domain id */
+       );
+
+       emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
+       emit_byte (logbuffer, TYPE_CONTEXT);
+       emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_id (context));
+       emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_domain_id (context));
+
+       EXIT_LOG;
+}
+
+typedef struct {
+       MonoMethod *method;
+       MonoDomain *domain;
+       void *base_address;
+       int offset;
+} AsyncFrameInfo;
+
+typedef struct {
+       MonoLockFreeQueueNode node;
+       MonoProfiler *prof;
+       uint64_t time;
+       uintptr_t tid;
+       void *ip;
+       int count;
+       AsyncFrameInfo frames [MONO_ZERO_LEN_ARRAY];
+} SampleHit;
+
+static mono_bool
+async_walk_stack (MonoMethod *method, MonoDomain *domain, void *base_address, int offset, void *data)
+{
+       SampleHit *sample = (SampleHit *) data;
+
+       if (sample->count < num_frames) {
+               int i = sample->count;
+
+               sample->frames [i].method = method;
+               sample->frames [i].domain = domain;
+               sample->frames [i].base_address = base_address;
+               sample->frames [i].offset = offset;
+
+               sample->count++;
+       }
+
+       return sample->count == num_frames;
+}
+
+#define SAMPLE_SLOT_SIZE(FRAMES) (sizeof (SampleHit) + sizeof (AsyncFrameInfo) * (FRAMES - MONO_ZERO_LEN_ARRAY))
+#define SAMPLE_BLOCK_SIZE (mono_pagesize ())
+
+static void
+enqueue_sample_hit (gpointer p)
+{
+       SampleHit *sample = p;
+
+       mono_lock_free_queue_node_unpoison (&sample->node);
+       mono_lock_free_queue_enqueue (&sample->prof->dumper_queue, &sample->node);
+       mono_os_sem_post (&sample->prof->dumper_queue_sem);
+}
+
+static void
+mono_sample_hit (MonoProfiler *profiler, unsigned char *ip, void *context)
+{
+       /*
+        * Please note: We rely on the runtime loading the profiler with
+        * MONO_DL_EAGER (RTLD_NOW) so that references to runtime functions within
+        * this function (and its siblings) are resolved when the profiler is
+        * loaded. Otherwise, we would potentially invoke the dynamic linker when
+        * invoking runtime functions, which is not async-signal-safe.
+        */
+
+       if (InterlockedRead (&in_shutdown))
+               return;
+
+       SampleHit *sample = (SampleHit *) mono_lock_free_queue_dequeue (&profiler->sample_reuse_queue);
+
+       if (!sample) {
+               /*
+                * If we're out of reusable sample events and we're not allowed to
+                * allocate more, we have no choice but to drop the event.
+                */
+               if (InterlockedRead (&sample_allocations_ctr) >= max_allocated_sample_hits)
+                       return;
+
+               sample = mono_lock_free_alloc (&profiler->sample_allocator);
+               sample->prof = profiler;
+               mono_lock_free_queue_node_init (&sample->node, TRUE);
+
+               InterlockedIncrement (&sample_allocations_ctr);
+       }
+
+       sample->count = 0;
+       mono_stack_walk_async_safe (&async_walk_stack, context, sample);
+
+       sample->time = current_time ();
+       sample->tid = thread_id ();
+       sample->ip = ip;
+
+       mono_thread_hazardous_try_free (sample, enqueue_sample_hit);
+}
+
+static uintptr_t *code_pages = 0;
+static int num_code_pages = 0;
+static int size_code_pages = 0;
+#define CPAGE_SHIFT (9)
+#define CPAGE_SIZE (1 << CPAGE_SHIFT)
+#define CPAGE_MASK (~(CPAGE_SIZE - 1))
+#define CPAGE_ADDR(p) ((p) & CPAGE_MASK)
+
+static uintptr_t
+add_code_page (uintptr_t *hash, uintptr_t hsize, uintptr_t page)
+{
+       uintptr_t i;
+       uintptr_t start_pos;
+       start_pos = (page >> CPAGE_SHIFT) % hsize;
+       i = start_pos;
+       do {
+               if (hash [i] && CPAGE_ADDR (hash [i]) == CPAGE_ADDR (page)) {
+                       return 0;
+               } else if (!hash [i]) {
+                       hash [i] = page;
+                       return 1;
+               }
+               /* wrap around */
+               if (++i == hsize)
+                       i = 0;
+       } while (i != start_pos);
+       /* should not happen */
+       printf ("failed code page store\n");
+       return 0;
+}
+
+static void
+add_code_pointer (uintptr_t ip)
+{
+       uintptr_t i;
+       if (num_code_pages * 2 >= size_code_pages) {
+               uintptr_t *n;
+               uintptr_t old_size = size_code_pages;
+               size_code_pages *= 2;
+               if (size_code_pages == 0)
+                       size_code_pages = 16;
+               n = (uintptr_t *) g_calloc (sizeof (uintptr_t) * size_code_pages, 1);
+               for (i = 0; i < old_size; ++i) {
+                       if (code_pages [i])
+                               add_code_page (n, size_code_pages, code_pages [i]);
+               }
+               if (code_pages)
+                       g_free (code_pages);
+               code_pages = n;
+       }
+       num_code_pages += add_code_page (code_pages, size_code_pages, ip & CPAGE_MASK);
+}
+
+/* ELF code crashes on some systems. */
+//#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
+#if 0
+static void
+dump_ubin (MonoProfiler *prof, const char *filename, uintptr_t load_addr, uint64_t offset, uintptr_t size)
+{
+       int len = strlen (filename) + 1;
+
+       ENTER_LOG (&sample_ubins_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               LEB128_SIZE /* load address */ +
+               LEB128_SIZE /* offset */ +
+               LEB128_SIZE /* size */ +
+               nlen /* file name */
+       );
+
+       emit_event (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_UBIN);
+       emit_svalue (logbuffer, load_addr);
+       emit_uvalue (logbuffer, offset);
+       emit_uvalue (logbuffer, size);
+       memcpy (logbuffer->cursor, filename, len);
+       logbuffer->cursor += len;
+
+       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+}
+#endif
+
+static void
+dump_usym (MonoProfiler *prof, const char *name, uintptr_t value, uintptr_t size)
+{
+       int len = strlen (name) + 1;
+
+       ENTER_LOG (&sample_usyms_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               LEB128_SIZE /* value */ +
+               LEB128_SIZE /* size */ +
+               len /* name */
+       );
+
+       emit_event (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_USYM);
+       emit_ptr (logbuffer, (void*)value);
+       emit_value (logbuffer, size);
+       memcpy (logbuffer->cursor, name, len);
+       logbuffer->cursor += len;
+
+       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+}
+
+/* ELF code crashes on some systems. */
+//#if defined(ELFMAG0)
+#if 0
+
+#if SIZEOF_VOID_P == 4
+#define ELF_WSIZE 32
+#else
+#define ELF_WSIZE 64
+#endif
+#ifndef ElfW
+#define ElfW(type)      _ElfW (Elf, ELF_WSIZE, type)
+#define _ElfW(e,w,t)    _ElfW_1 (e, w, _##t)
+#define _ElfW_1(e,w,t)  e##w##t
+#endif
+
+static void
+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) {
+               const char* sym;
+               sym =  strtab + symbols [i].st_name;
+               if (!symbols [i].st_name || !symbols [i].st_size || (symbols [i].st_info & 0xf) != STT_FUNC)
+                       continue;
+               //printf ("symbol %s at %d\n", sym, symbols [i].st_value);
+               dump_usym (sym, (uintptr_t)load_addr + symbols [i].st_value, symbols [i].st_size);
+       }
+}
+
+static int
+read_elf_symbols (MonoProfiler *prof, const char *filename, void *load_addr)
+{
+       int fd, i;
+       void *data;
+       struct stat statb;
+       uint64_t file_size;
+       ElfW(Ehdr) *header;
+       ElfW(Shdr) *sheader;
+       ElfW(Shdr) *shstrtabh;
+       ElfW(Shdr) *symtabh = NULL;
+       ElfW(Shdr) *strtabh = NULL;
+       ElfW(Sym) *symbols = NULL;
+       const char *strtab;
+       int num_symbols;
+
+       fd = open (filename, O_RDONLY);
+       if (fd < 0)
+               return 0;
+       if (fstat (fd, &statb) != 0) {
+               close (fd);
+               return 0;
+       }
+       file_size = statb.st_size;
+       data = mmap (NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       close (fd);
+       if (data == MAP_FAILED)
+               return 0;
+       header = data;
+       if (header->e_ident [EI_MAG0] != ELFMAG0 ||
+                       header->e_ident [EI_MAG1] != ELFMAG1 ||
+                       header->e_ident [EI_MAG2] != ELFMAG2 ||
+                       header->e_ident [EI_MAG3] != ELFMAG3 ) {
+               munmap (data, file_size);
+               return 0;
+       }
+       sheader = (void*)((char*)data + header->e_shoff);
+       shstrtabh = (void*)((char*)sheader + (header->e_shentsize * header->e_shstrndx));
+       strtab = (const char*)data + shstrtabh->sh_offset;
+       for (i = 0; i < header->e_shnum; ++i) {
+               //printf ("section header: %d\n", sheader->sh_type);
+               if (sheader->sh_type == SHT_SYMTAB) {
+                       symtabh = sheader;
+                       strtabh = (void*)((char*)data + header->e_shoff + sheader->sh_link * header->e_shentsize);
+                       /*printf ("symtab section header: %d, .strstr: %d\n", i, sheader->sh_link);*/
+                       break;
+               }
+               sheader = (void*)((char*)sheader + header->e_shentsize);
+       }
+       if (!symtabh || !strtabh) {
+               munmap (data, file_size);
+               return 0;
+       }
+       strtab = (const char*)data + strtabh->sh_offset;
+       num_symbols = symtabh->sh_size / symtabh->sh_entsize;
+       symbols = (void*)((char*)data + symtabh->sh_offset);
+       dump_elf_symbols (symbols, num_symbols, strtab, load_addr);
+       munmap (data, file_size);
+       return 1;
+}
+#endif
+
+/* ELF code crashes on some systems. */
+//#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
+#if 0
+static int
+elf_dl_callback (struct dl_phdr_info *info, size_t size, void *data)
+{
+       MonoProfiler *prof = data;
+       char buf [256];
+       const char *filename;
+       BinaryObject *obj;
+       char *a = (void*)info->dlpi_addr;
+       int i, num_sym;
+       ElfW(Dyn) *dyn = NULL;
+       ElfW(Sym) *symtab = NULL;
+       ElfW(Word) *hash_table = NULL;
+       ElfW(Ehdr) *header = NULL;
+       const char* strtab = NULL;
+       for (obj = prof->binary_objects; obj; obj = obj->next) {
+               if (obj->addr == a)
+                       return 0;
+       }
+       filename = info->dlpi_name;
+       if (!filename)
+               return 0;
+       if (!info->dlpi_addr && !filename [0]) {
+               int l = readlink ("/proc/self/exe", buf, sizeof (buf) - 1);
+               if (l > 0) {
+                       buf [l] = 0;
+                       filename = buf;
+               }
+       }
+       obj = g_calloc (sizeof (BinaryObject), 1);
+       obj->addr = (void*)info->dlpi_addr;
+       obj->name = pstrdup (filename);
+       obj->next = prof->binary_objects;
+       prof->binary_objects = obj;
+       //printf ("loaded file: %s at %p, segments: %d\n", filename, (void*)info->dlpi_addr, info->dlpi_phnum);
+       a = NULL;
+       for (i = 0; i < info->dlpi_phnum; ++i) {
+               //printf ("segment type %d file offset: %d, size: %d\n", info->dlpi_phdr[i].p_type, info->dlpi_phdr[i].p_offset, info->dlpi_phdr[i].p_memsz);
+               if (info->dlpi_phdr[i].p_type == PT_LOAD && !header) {
+                       header = (ElfW(Ehdr)*)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
+                       if (header->e_ident [EI_MAG0] != ELFMAG0 ||
+                                       header->e_ident [EI_MAG1] != ELFMAG1 ||
+                                       header->e_ident [EI_MAG2] != ELFMAG2 ||
+                                       header->e_ident [EI_MAG3] != ELFMAG3 ) {
+                               header = NULL;
+                       }
+                       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);
+               }
+       }
+       if (read_elf_symbols (prof, filename, (void*)info->dlpi_addr))
+               return 0;
+       if (!info->dlpi_name || !info->dlpi_name[0])
+               return 0;
+       if (!dyn)
+               return 0;
+       for (i = 0; dyn [i].d_tag != DT_NULL; ++i) {
+               if (dyn [i].d_tag == DT_SYMTAB) {
+                       if (symtab && do_debug)
+                               printf ("multiple symtabs: %d\n", i);
+                       symtab = (ElfW(Sym) *)(a + dyn [i].d_un.d_ptr);
+               } else if (dyn [i].d_tag == DT_HASH) {
+                       hash_table = (ElfW(Word) *)(a + dyn [i].d_un.d_ptr);
+               } else if (dyn [i].d_tag == DT_STRTAB) {
+                       strtab = (const char*)(a + dyn [i].d_un.d_ptr);
+               }
+       }
+       if (!hash_table)
+               return 0;
+       num_sym = hash_table [1];
+       dump_elf_symbols (prof, symtab, num_sym, strtab, (void*)info->dlpi_addr);
+       return 0;
+}
+
+static int
+load_binaries (MonoProfiler *prof)
+{
+       dl_iterate_phdr (elf_dl_callback, prof);
+       return 1;
+}
+#else
+static int
+load_binaries (MonoProfiler *prof)
+{
+       return 0;
+}
+#endif
+
+static const char*
+symbol_for (uintptr_t code)
+{
+#ifdef HAVE_DLADDR
+       void *ip = (void*)code;
+       Dl_info di;
+       if (dladdr (ip, &di)) {
+               if (di.dli_sname)
+                       return di.dli_sname;
+       } else {
+       /*      char **names;
+               names = backtrace_symbols (&ip, 1);
+               if (names) {
+                       const char* p = names [0];
+                       g_free (names);
+                       return p;
+               }
+               */
+       }
+#endif
+       return NULL;
+}
+
+static void
+dump_unmanaged_coderefs (MonoProfiler *prof)
+{
+       int i;
+       const char* last_symbol;
+       uintptr_t addr, page_end;
+
+       if (load_binaries (prof))
+               return;
+       for (i = 0; i < size_code_pages; ++i) {
+               const char* sym;
+               if (!code_pages [i] || code_pages [i] & 1)
+                       continue;
+               last_symbol = NULL;
+               addr = CPAGE_ADDR (code_pages [i]);
+               page_end = addr + CPAGE_SIZE;
+               code_pages [i] |= 1;
+               /* we dump the symbols for the whole page */
+               for (; addr < page_end; addr += 16) {
+                       sym = symbol_for (addr);
+                       if (sym && sym == last_symbol)
+                               continue;
+                       last_symbol = sym;
+                       if (!sym)
+                               continue;
+                       dump_usym (prof, sym, addr, 0); /* let's not guess the size */
+                       //printf ("found symbol at %p: %s\n", (void*)addr, sym);
+               }
+       }
+}
+
+typedef struct MonoCounterAgent {
+       MonoCounter *counter;
+       // MonoCounterAgent specific data :
+       void *value;
+       size_t value_size;
+       short index;
+       short emitted;
+       struct MonoCounterAgent *next;
+} MonoCounterAgent;
+
+static MonoCounterAgent* counters;
+static int counters_index = 1;
+static mono_mutex_t counters_mutex;
+
+static void
+counters_add_agent (MonoCounter *counter)
+{
+       if (InterlockedRead (&in_shutdown))
+               return;
+
+       MonoCounterAgent *agent, *item;
+
+       mono_os_mutex_lock (&counters_mutex);
+
+       for (agent = counters; agent; agent = agent->next) {
+               if (agent->counter == counter) {
+                       agent->value_size = 0;
+                       if (agent->value) {
+                               g_free (agent->value);
+                               agent->value = NULL;
+                       }
+                       goto done;
+               }
+       }
+
+       agent = (MonoCounterAgent *) g_malloc (sizeof (MonoCounterAgent));
+       agent->counter = counter;
+       agent->value = NULL;
+       agent->value_size = 0;
+       agent->index = counters_index++;
+       agent->emitted = 0;
+       agent->next = NULL;
+
+       if (!counters) {
+               counters = agent;
+       } else {
+               item = counters;
+               while (item->next)
+                       item = item->next;
+               item->next = agent;
+       }
+
+done:
+       mono_os_mutex_unlock (&counters_mutex);
+}
+
+static mono_bool
+counters_init_foreach_callback (MonoCounter *counter, gpointer data)
+{
+       counters_add_agent (counter);
+       return TRUE;
+}
+
+static void
+counters_init (MonoProfiler *profiler)
+{
+       mono_os_mutex_init (&counters_mutex);
+
+       mono_counters_on_register (&counters_add_agent);
+       mono_counters_foreach (counters_init_foreach_callback, NULL);
+}
+
+static void
+counters_emit (MonoProfiler *profiler)
+{
+       MonoCounterAgent *agent;
+       int len = 0;
+       int size =
+               EVENT_SIZE /* event */ +
+               LEB128_SIZE /* len */
+       ;
+
+       mono_os_mutex_lock (&counters_mutex);
+
+       for (agent = counters; agent; agent = agent->next) {
+               if (agent->emitted)
+                       continue;
+
+               size +=
+                       LEB128_SIZE /* section */ +
+                       strlen (mono_counter_get_name (agent->counter)) + 1 /* name */ +
+                       BYTE_SIZE /* type */ +
+                       BYTE_SIZE /* unit */ +
+                       BYTE_SIZE /* variance */ +
+                       LEB128_SIZE /* index */
+               ;
+
+               len++;
+       }
+
+       if (!len)
+               goto done;
+
+       ENTER_LOG (&counter_descriptors_ctr, logbuffer, size);
+
+       emit_event (logbuffer, TYPE_SAMPLE_COUNTERS_DESC | TYPE_SAMPLE);
+       emit_value (logbuffer, len);
+
+       for (agent = counters; agent; agent = agent->next) {
+               const char *name;
+
+               if (agent->emitted)
+                       continue;
+
+               name = mono_counter_get_name (agent->counter);
+               emit_value (logbuffer, mono_counter_get_section (agent->counter));
+               emit_string (logbuffer, name, strlen (name) + 1);
+               emit_byte (logbuffer, mono_counter_get_type (agent->counter));
+               emit_byte (logbuffer, mono_counter_get_unit (agent->counter));
+               emit_byte (logbuffer, mono_counter_get_variance (agent->counter));
+               emit_value (logbuffer, agent->index);
+
+               agent->emitted = 1;
+       }
+
+       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+
+done:
+       mono_os_mutex_unlock (&counters_mutex);
+}
+
+static void
+counters_sample (MonoProfiler *profiler, uint64_t timestamp)
+{
+       MonoCounterAgent *agent;
+       MonoCounter *counter;
+       int type;
+       int buffer_size;
+       void *buffer;
+       int size;
+
+       counters_emit (profiler);
+
+       buffer_size = 8;
+       buffer = g_calloc (1, buffer_size);
+
+       mono_os_mutex_lock (&counters_mutex);
+
+       size =
+               EVENT_SIZE /* event */
+       ;
+
+       for (agent = counters; agent; agent = agent->next) {
+               size +=
+                       LEB128_SIZE /* index */ +
+                       BYTE_SIZE /* type */ +
+                       mono_counter_get_size (agent->counter) /* value */
+               ;
+       }
+
+       size +=
+               LEB128_SIZE /* stop marker */
+       ;
+
+       ENTER_LOG (&counter_samples_ctr, logbuffer, size);
+
+       emit_event_time (logbuffer, TYPE_SAMPLE_COUNTERS | TYPE_SAMPLE, timestamp);
+
+       for (agent = counters; agent; agent = agent->next) {
+               size_t size;
+
+               counter = agent->counter;
+
+               size = mono_counter_get_size (counter);
+
+               if (size > buffer_size) {
+                       buffer_size = size;
+                       buffer = g_realloc (buffer, buffer_size);
+               }
+
+               memset (buffer, 0, buffer_size);
+
+               g_assert (mono_counters_sample (counter, buffer, size));
+
+               type = mono_counter_get_type (counter);
+
+               if (!agent->value) {
+                       agent->value = g_calloc (1, size);
+                       agent->value_size = size;
+               } else {
+                       if (type == MONO_COUNTER_STRING) {
+                               if (strcmp (agent->value, buffer) == 0)
+                                       continue;
+                       } else {
+                               if (agent->value_size == size && memcmp (agent->value, buffer, size) == 0)
+                                       continue;
+                       }
+               }
+
+               emit_uvalue (logbuffer, agent->index);
+               emit_byte (logbuffer, type);
+               switch (type) {
+               case MONO_COUNTER_INT:
+#if SIZEOF_VOID_P == 4
+               case MONO_COUNTER_WORD:
+#endif
+                       emit_svalue (logbuffer, *(int*)buffer - *(int*)agent->value);
+                       break;
+               case MONO_COUNTER_UINT:
+                       emit_uvalue (logbuffer, *(guint*)buffer - *(guint*)agent->value);
+                       break;
+               case MONO_COUNTER_TIME_INTERVAL:
+               case MONO_COUNTER_LONG:
+#if SIZEOF_VOID_P == 8
+               case MONO_COUNTER_WORD:
+#endif
+                       emit_svalue (logbuffer, *(gint64*)buffer - *(gint64*)agent->value);
+                       break;
+               case MONO_COUNTER_ULONG:
+                       emit_uvalue (logbuffer, *(guint64*)buffer - *(guint64*)agent->value);
+                       break;
+               case MONO_COUNTER_DOUBLE:
+                       emit_double (logbuffer, *(double*)buffer);
+                       break;
+               case MONO_COUNTER_STRING:
+                       if (size == 0) {
+                               emit_byte (logbuffer, 0);
+                       } else {
+                               emit_byte (logbuffer, 1);
+                               emit_string (logbuffer, (char*)buffer, size);
+                       }
+                       break;
+               default:
+                       g_assert_not_reached ();
+               }
+
+               if (type == MONO_COUNTER_STRING && size > agent->value_size) {
+                       agent->value = g_realloc (agent->value, size);
+                       agent->value_size = size;
+               }
+
+               if (size > 0)
+                       memcpy (agent->value, buffer, size);
+       }
+       g_free (buffer);
+
+       emit_value (logbuffer, 0);
+
+       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+
+       mono_os_mutex_unlock (&counters_mutex);
+}
+
+typedef struct _PerfCounterAgent PerfCounterAgent;
+struct _PerfCounterAgent {
+       PerfCounterAgent *next;
+       int index;
+       char *category_name;
+       char *name;
+       int type;
+       gint64 value;
+       guint8 emitted;
+       guint8 updated;
+       guint8 deleted;
+};
+
+static PerfCounterAgent *perfcounters = NULL;
+
+static void
+perfcounters_emit (MonoProfiler *profiler)
+{
+       PerfCounterAgent *pcagent;
+       int len = 0;
+       int size =
+               EVENT_SIZE /* event */ +
+               LEB128_SIZE /* len */
+       ;
+
+       for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
+               if (pcagent->emitted)
+                       continue;
+
+               size +=
+                       LEB128_SIZE /* section */ +
+                       strlen (pcagent->category_name) + 1 /* category name */ +
+                       strlen (pcagent->name) + 1 /* name */ +
+                       BYTE_SIZE /* type */ +
+                       BYTE_SIZE /* unit */ +
+                       BYTE_SIZE /* variance */ +
+                       LEB128_SIZE /* index */
+               ;
+
+               len++;
+       }
+
+       if (!len)
+               return;
+
+       ENTER_LOG (&perfcounter_descriptors_ctr, logbuffer, size);
+
+       emit_event (logbuffer, TYPE_SAMPLE_COUNTERS_DESC | TYPE_SAMPLE);
+       emit_value (logbuffer, len);
+
+       for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
+               if (pcagent->emitted)
+                       continue;
+
+               emit_value (logbuffer, MONO_COUNTER_PERFCOUNTERS);
+               emit_string (logbuffer, pcagent->category_name, strlen (pcagent->category_name) + 1);
+               emit_string (logbuffer, pcagent->name, strlen (pcagent->name) + 1);
+               emit_byte (logbuffer, MONO_COUNTER_LONG);
+               emit_byte (logbuffer, MONO_COUNTER_RAW);
+               emit_byte (logbuffer, MONO_COUNTER_VARIABLE);
+               emit_value (logbuffer, pcagent->index);
+
+               pcagent->emitted = 1;
+       }
+
+       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+}
+
+static gboolean
+perfcounters_foreach (char *category_name, char *name, unsigned char type, gint64 value, gpointer user_data)
+{
+       PerfCounterAgent *pcagent;
+
+       for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
+               if (strcmp (pcagent->category_name, category_name) != 0 || strcmp (pcagent->name, name) != 0)
+                       continue;
+               if (pcagent->value == value)
+                       return TRUE;
+
+               pcagent->value = value;
+               pcagent->updated = 1;
+               pcagent->deleted = 0;
+               return TRUE;
+       }
+
+       pcagent = g_new0 (PerfCounterAgent, 1);
+       pcagent->next = perfcounters;
+       pcagent->index = counters_index++;
+       pcagent->category_name = g_strdup (category_name);
+       pcagent->name = g_strdup (name);
+       pcagent->type = (int) type;
+       pcagent->value = value;
+       pcagent->emitted = 0;
+       pcagent->updated = 1;
+       pcagent->deleted = 0;
+
+       perfcounters = pcagent;
+
+       return TRUE;
+}
+
+static void
+perfcounters_sample (MonoProfiler *profiler, uint64_t timestamp)
+{
+       PerfCounterAgent *pcagent;
+       int len = 0;
+       int size;
+
+       mono_os_mutex_lock (&counters_mutex);
+
+       /* mark all perfcounters as deleted, foreach will unmark them as necessary */
+       for (pcagent = perfcounters; pcagent; pcagent = pcagent->next)
+               pcagent->deleted = 1;
+
+       mono_perfcounter_foreach (perfcounters_foreach, perfcounters);
+
+       perfcounters_emit (profiler);
+
+       size =
+               EVENT_SIZE /* event */
+       ;
+
+       for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
+               if (pcagent->deleted || !pcagent->updated)
+                       continue;
+
+               size +=
+                       LEB128_SIZE /* index */ +
+                       BYTE_SIZE /* type */ +
+                       LEB128_SIZE /* value */
+               ;
+
+               len++;
+       }
+
+       if (!len)
+               goto done;
+
+       size +=
+               LEB128_SIZE /* stop marker */
+       ;
+
+       ENTER_LOG (&perfcounter_samples_ctr, logbuffer, size);
+
+       emit_event_time (logbuffer, TYPE_SAMPLE_COUNTERS | TYPE_SAMPLE, timestamp);
+
+       for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
+               if (pcagent->deleted || !pcagent->updated)
+                       continue;
+               emit_uvalue (logbuffer, pcagent->index);
+               emit_byte (logbuffer, MONO_COUNTER_LONG);
+               emit_svalue (logbuffer, pcagent->value);
+
+               pcagent->updated = 0;
+       }
+
+       emit_value (logbuffer, 0);
+
+       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+
+done:
+       mono_os_mutex_unlock (&counters_mutex);
+}
+
+static void
+counters_and_perfcounters_sample (MonoProfiler *prof)
+{
+       uint64_t now = current_time ();
+
+       counters_sample (prof, now);
+       perfcounters_sample (prof, now);
+}
+
+#define COVERAGE_DEBUG(x) if (debug_coverage) {x}
+static mono_mutex_t coverage_mutex;
+static MonoConcurrentHashTable *coverage_methods = NULL;
+static MonoConcurrentHashTable *coverage_assemblies = NULL;
+static MonoConcurrentHashTable *coverage_classes = NULL;
+
+static MonoConcurrentHashTable *filtered_classes = NULL;
+static MonoConcurrentHashTable *entered_methods = NULL;
+static MonoConcurrentHashTable *image_to_methods = NULL;
+static MonoConcurrentHashTable *suppressed_assemblies = NULL;
+static gboolean coverage_initialized = FALSE;
+
+static GPtrArray *coverage_data = NULL;
+static int previous_offset = 0;
+
+typedef struct {
+       MonoLockFreeQueueNode node;
+       MonoMethod *method;
+} MethodNode;
+
+typedef struct {
+       int offset;
+       int counter;
+       char *filename;
+       int line;
+       int column;
+} CoverageEntry;
+
+static void
+free_coverage_entry (gpointer data, gpointer userdata)
+{
+       CoverageEntry *entry = (CoverageEntry *)data;
+       g_free (entry->filename);
+       g_free (entry);
+}
+
+static void
+obtain_coverage_for_method (MonoProfiler *prof, const MonoProfileCoverageEntry *entry)
+{
+       int offset = entry->iloffset - previous_offset;
+       CoverageEntry *e = g_new (CoverageEntry, 1);
+
+       previous_offset = entry->iloffset;
+
+       e->offset = offset;
+       e->counter = entry->counter;
+       e->filename = g_strdup(entry->filename ? entry->filename : "");
+       e->line = entry->line;
+       e->column = entry->col;
+
+       g_ptr_array_add (coverage_data, e);
+}
+
+static char *
+parse_generic_type_names(char *name)
+{
+       char *new_name, *ret;
+       int within_generic_declaration = 0, generic_members = 1;
+
+       if (name == NULL || *name == '\0')
+               return g_strdup ("");
+
+       if (!(ret = new_name = (char *) g_calloc (strlen (name) * 4 + 1, sizeof (char))))
+               return NULL;
+
+       do {
+               switch (*name) {
+                       case '<':
+                               within_generic_declaration = 1;
+                               break;
+
+                       case '>':
+                               within_generic_declaration = 0;
+
+                               if (*(name - 1) != '<') {
+                                       *new_name++ = '`';
+                                       *new_name++ = '0' + generic_members;
+                               } else {
+                                       memcpy (new_name, "&lt;&gt;", 8);
+                                       new_name += 8;
+                               }
+
+                               generic_members = 0;
+                               break;
+
+                       case ',':
+                               generic_members++;
+                               break;
+
+                       default:
+                               if (!within_generic_declaration)
+                                       *new_name++ = *name;
+
+                               break;
+               }
+       } while (*name++);
+
+       return ret;
+}
+
+static int method_id;
+static void
+build_method_buffer (gpointer key, gpointer value, gpointer userdata)
+{
+       MonoMethod *method = (MonoMethod *)value;
+       MonoProfiler *prof = (MonoProfiler *)userdata;
+       MonoClass *klass;
+       MonoImage *image;
+       char *class_name;
+       const char *image_name, *method_name, *sig, *first_filename;
+       guint i;
+
+       previous_offset = 0;
+       coverage_data = g_ptr_array_new ();
+
+       mono_profiler_coverage_get (prof, method, obtain_coverage_for_method);
+
+       klass = mono_method_get_class (method);
+       image = mono_class_get_image (klass);
+       image_name = mono_image_get_name (image);
+
+       sig = mono_signature_get_desc (mono_method_signature (method), TRUE);
+       class_name = parse_generic_type_names (mono_type_get_name (mono_class_get_type (klass)));
+       method_name = mono_method_get_name (method);
+
+       if (coverage_data->len != 0) {
+               CoverageEntry *entry = (CoverageEntry *)coverage_data->pdata[0];
+               first_filename = entry->filename ? entry->filename : "";
+       } else
+               first_filename = "";
+
+       image_name = image_name ? image_name : "";
+       sig = sig ? sig : "";
+       method_name = method_name ? method_name : "";
+
+       ENTER_LOG (&coverage_methods_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               strlen (image_name) + 1 /* image name */ +
+               strlen (class_name) + 1 /* class name */ +
+               strlen (method_name) + 1 /* method name */ +
+               strlen (sig) + 1 /* signature */ +
+               strlen (first_filename) + 1 /* first file name */ +
+               LEB128_SIZE /* token */ +
+               LEB128_SIZE /* method id */ +
+               LEB128_SIZE /* entries */
+       );
+
+       emit_event (logbuffer, TYPE_COVERAGE_METHOD | TYPE_COVERAGE);
+       emit_string (logbuffer, image_name, strlen (image_name) + 1);
+       emit_string (logbuffer, class_name, strlen (class_name) + 1);
+       emit_string (logbuffer, method_name, strlen (method_name) + 1);
+       emit_string (logbuffer, sig, strlen (sig) + 1);
+       emit_string (logbuffer, first_filename, strlen (first_filename) + 1);
+
+       emit_uvalue (logbuffer, mono_method_get_token (method));
+       emit_uvalue (logbuffer, method_id);
+       emit_value (logbuffer, coverage_data->len);
+
+       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+
+       for (i = 0; i < coverage_data->len; i++) {
+               CoverageEntry *entry = (CoverageEntry *)coverage_data->pdata[i];
+
+               ENTER_LOG (&coverage_statements_ctr, logbuffer,
+                       EVENT_SIZE /* event */ +
+                       LEB128_SIZE /* method id */ +
+                       LEB128_SIZE /* offset */ +
+                       LEB128_SIZE /* counter */ +
+                       LEB128_SIZE /* line */ +
+                       LEB128_SIZE /* column */
+               );
+
+               emit_event (logbuffer, TYPE_COVERAGE_STATEMENT | TYPE_COVERAGE);
+               emit_uvalue (logbuffer, method_id);
+               emit_uvalue (logbuffer, entry->offset);
+               emit_uvalue (logbuffer, entry->counter);
+               emit_uvalue (logbuffer, entry->line);
+               emit_uvalue (logbuffer, entry->column);
+
+               EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+       }
+
+       method_id++;
+
+       g_free (class_name);
+
+       g_ptr_array_foreach (coverage_data, free_coverage_entry, NULL);
+       g_ptr_array_free (coverage_data, TRUE);
+       coverage_data = NULL;
+}
+
+/* This empties the queue */
+static guint
+count_queue (MonoLockFreeQueue *queue)
+{
+       MonoLockFreeQueueNode *node;
+       guint count = 0;
+
+       while ((node = mono_lock_free_queue_dequeue (queue))) {
+               count++;
+               mono_thread_hazardous_try_free (node, g_free);
+       }
+
+       return count;
+}
+
+static void
+build_class_buffer (gpointer key, gpointer value, gpointer userdata)
+{
+       MonoClass *klass = (MonoClass *)key;
+       MonoLockFreeQueue *class_methods = (MonoLockFreeQueue *)value;
+       MonoImage *image;
+       char *class_name;
+       const char *assembly_name;
+       int number_of_methods, partially_covered;
+       guint fully_covered;
+
+       image = mono_class_get_image (klass);
+       assembly_name = mono_image_get_name (image);
+       class_name = mono_type_get_name (mono_class_get_type (klass));
+
+       assembly_name = assembly_name ? assembly_name : "";
+       number_of_methods = mono_class_num_methods (klass);
+       fully_covered = count_queue (class_methods);
+       /* We don't handle partial covered yet */
+       partially_covered = 0;
+
+       ENTER_LOG (&coverage_classes_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               strlen (assembly_name) + 1 /* assembly name */ +
+               strlen (class_name) + 1 /* class name */ +
+               LEB128_SIZE /* no. methods */ +
+               LEB128_SIZE /* fully covered */ +
+               LEB128_SIZE /* partially covered */
+       );
+
+       emit_event (logbuffer, TYPE_COVERAGE_CLASS | TYPE_COVERAGE);
+       emit_string (logbuffer, assembly_name, strlen (assembly_name) + 1);
+       emit_string (logbuffer, class_name, strlen (class_name) + 1);
+       emit_uvalue (logbuffer, number_of_methods);
+       emit_uvalue (logbuffer, fully_covered);
+       emit_uvalue (logbuffer, partially_covered);
+
+       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+
+       g_free (class_name);
+}
+
+static void
+get_coverage_for_image (MonoImage *image, int *number_of_methods, guint *fully_covered, int *partially_covered)
+{
+       MonoLockFreeQueue *image_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (image_to_methods, image);
+
+       *number_of_methods = mono_image_get_table_rows (image, MONO_TABLE_METHOD);
+       if (image_methods)
+               *fully_covered = count_queue (image_methods);
+       else
+               *fully_covered = 0;
+
+       // FIXME: We don't handle partially covered yet.
+       *partially_covered = 0;
+}
+
+static void
+build_assembly_buffer (gpointer key, gpointer value, gpointer userdata)
+{
+       MonoAssembly *assembly = (MonoAssembly *)value;
+       MonoImage *image = mono_assembly_get_image (assembly);
+       const char *name, *guid, *filename;
+       int number_of_methods = 0, partially_covered = 0;
+       guint fully_covered = 0;
+
+       name = mono_image_get_name (image);
+       guid = mono_image_get_guid (image);
+       filename = mono_image_get_filename (image);
+
+       name = name ? name : "";
+       guid = guid ? guid : "";
+       filename = filename ? filename : "";
+
+       get_coverage_for_image (image, &number_of_methods, &fully_covered, &partially_covered);
+
+       ENTER_LOG (&coverage_assemblies_ctr, logbuffer,
+               EVENT_SIZE /* event */ +
+               strlen (name) + 1 /* name */ +
+               strlen (guid) + 1 /* guid */ +
+               strlen (filename) + 1 /* file name */ +
+               LEB128_SIZE /* no. methods */ +
+               LEB128_SIZE /* fully covered */ +
+               LEB128_SIZE /* partially covered */
+       );
+
+       emit_event (logbuffer, TYPE_COVERAGE_ASSEMBLY | TYPE_COVERAGE);
+       emit_string (logbuffer, name, strlen (name) + 1);
+       emit_string (logbuffer, guid, strlen (guid) + 1);
+       emit_string (logbuffer, filename, strlen (filename) + 1);
+       emit_uvalue (logbuffer, number_of_methods);
+       emit_uvalue (logbuffer, fully_covered);
+       emit_uvalue (logbuffer, partially_covered);
+
+       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+}
+
+static void
+dump_coverage (MonoProfiler *prof)
+{
+       if (!coverage_initialized)
+               return;
+
+       COVERAGE_DEBUG(fprintf (stderr, "Coverage: Started dump\n");)
+       method_id = 0;
+
+       mono_os_mutex_lock (&coverage_mutex);
+       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);
+
+       COVERAGE_DEBUG(fprintf (stderr, "Coverage: Finished dump\n");)
+}
+
+static void
+process_method_enter_coverage (MonoProfiler *prof, MonoMethod *method)
+{
+       MonoClass *klass;
+       MonoImage *image;
+
+       if (!coverage_initialized)
+               return;
+
+       klass = mono_method_get_class (method);
+       image = mono_class_get_image (klass);
+
+       if (mono_conc_hashtable_lookup (suppressed_assemblies, (gpointer) mono_image_get_name (image)))
+               return;
+
+       mono_os_mutex_lock (&coverage_mutex);
+       mono_conc_hashtable_insert (entered_methods, method, method);
+       mono_os_mutex_unlock (&coverage_mutex);
+}
+
+static MonoLockFreeQueueNode *
+create_method_node (MonoMethod *method)
+{
+       MethodNode *node = (MethodNode *) g_malloc (sizeof (MethodNode));
+       mono_lock_free_queue_node_init ((MonoLockFreeQueueNode *) node, FALSE);
+       node->method = method;
+
+       return (MonoLockFreeQueueNode *) node;
+}
+
+static gboolean
+coverage_filter (MonoProfiler *prof, MonoMethod *method)
+{
+       MonoError error;
+       MonoClass *klass;
+       MonoImage *image;
+       MonoAssembly *assembly;
+       MonoMethodHeader *header;
+       guint32 iflags, flags, code_size;
+       char *fqn, *classname;
+       gboolean has_positive, found;
+       MonoLockFreeQueue *image_methods, *class_methods;
+       MonoLockFreeQueueNode *node;
+
+       g_assert (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));)
+
+       flags = mono_method_get_flags (method, &iflags);
+       if ((iflags & 0x1000 /*METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL*/) ||
+           (flags & 0x2000 /*METHOD_ATTRIBUTE_PINVOKE_IMPL*/)) {
+               COVERAGE_DEBUG(fprintf (stderr, "   Internal call or pinvoke - ignoring\n");)
+               return FALSE;
+       }
+
+       // Don't need to do anything else if we're already tracking this method
+       if (mono_conc_hashtable_lookup (coverage_methods, method)) {
+               COVERAGE_DEBUG(fprintf (stderr, "   Already tracking\n");)
+               return TRUE;
+       }
+
+       klass = mono_method_get_class (method);
+       image = mono_class_get_image (klass);
+
+       // Don't handle coverage for the core assemblies
+       if (mono_conc_hashtable_lookup (suppressed_assemblies, (gpointer) mono_image_get_name (image)) != NULL)
+               return FALSE;
+
+       if (prof->coverage_filters) {
+               /* Check already filtered classes first */
+               if (mono_conc_hashtable_lookup (filtered_classes, klass)) {
+                       COVERAGE_DEBUG(fprintf (stderr, "   Already filtered\n");)
+                       return FALSE;
+               }
+
+               classname = mono_type_get_name (mono_class_get_type (klass));
+
+               fqn = g_strdup_printf ("[%s]%s", mono_image_get_name (image), classname);
+
+               COVERAGE_DEBUG(fprintf (stderr, "   Looking for %s in filter\n", fqn);)
+               // Check positive filters first
+               has_positive = FALSE;
+               found = FALSE;
+               for (guint i = 0; i < prof->coverage_filters->len; ++i) {
+                       char *filter = (char *)g_ptr_array_index (prof->coverage_filters, i);
+
+                       if (filter [0] == '+') {
+                               filter = &filter [1];
+
+                               COVERAGE_DEBUG(fprintf (stderr, "   Checking against +%s ...", filter);)
+
+                               if (strstr (fqn, filter) != NULL) {
+                                       COVERAGE_DEBUG(fprintf (stderr, "matched\n");)
+                                       found = TRUE;
+                               } else
+                                       COVERAGE_DEBUG(fprintf (stderr, "no match\n");)
+
+                               has_positive = TRUE;
+                       }
+               }
+
+               if (has_positive && !found) {
+                       COVERAGE_DEBUG(fprintf (stderr, "   Positive match was not found\n");)
+
+                       mono_os_mutex_lock (&coverage_mutex);
+                       mono_conc_hashtable_insert (filtered_classes, klass, klass);
+                       mono_os_mutex_unlock (&coverage_mutex);
+                       g_free (fqn);
+                       g_free (classname);
+
+                       return FALSE;
+               }
+
+               for (guint i = 0; i < prof->coverage_filters->len; ++i) {
+                       // FIXME: Is substring search sufficient?
+                       char *filter = (char *)g_ptr_array_index (prof->coverage_filters, i);
+                       if (filter [0] == '+')
+                               continue;
+
+                       // Skip '-'
+                       filter = &filter [1];
+                       COVERAGE_DEBUG(fprintf (stderr, "   Checking against -%s ...", filter);)
+
+                       if (strstr (fqn, filter) != NULL) {
+                               COVERAGE_DEBUG(fprintf (stderr, "matched\n");)
+
+                               mono_os_mutex_lock (&coverage_mutex);
+                               mono_conc_hashtable_insert (filtered_classes, klass, klass);
+                               mono_os_mutex_unlock (&coverage_mutex);
+                               g_free (fqn);
+                               g_free (classname);
+
+                               return FALSE;
+                       } else
+                               COVERAGE_DEBUG(fprintf (stderr, "no match\n");)
+
+               }
+
+               g_free (fqn);
+               g_free (classname);
+       }
+
+       COVERAGE_DEBUG(fprintf (stderr, "   Handling coverage for %s\n", mono_method_get_name (method));)
+       header = mono_method_get_header_checked (method, &error);
+       mono_error_cleanup (&error);
+
+       mono_method_header_get_code (header, &code_size, NULL);
+
+       assembly = mono_image_get_assembly (image);
+
+       // Need to keep the assemblies around for as long as they are kept in the hashtable
+       // Nunit, for example, has a habit of unloading them before the coverage statistics are
+       // generated causing a crash. See https://bugzilla.xamarin.com/show_bug.cgi?id=39325
+       mono_assembly_addref (assembly);
+
+       mono_os_mutex_lock (&coverage_mutex);
+       mono_conc_hashtable_insert (coverage_methods, method, method);
+       mono_conc_hashtable_insert (coverage_assemblies, assembly, assembly);
+       mono_os_mutex_unlock (&coverage_mutex);
+
+       image_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (image_to_methods, image);
+
+       if (image_methods == NULL) {
+               image_methods = (MonoLockFreeQueue *) g_malloc (sizeof (MonoLockFreeQueue));
+               mono_lock_free_queue_init (image_methods);
+               mono_os_mutex_lock (&coverage_mutex);
+               mono_conc_hashtable_insert (image_to_methods, image, image_methods);
+               mono_os_mutex_unlock (&coverage_mutex);
+       }
+
+       node = create_method_node (method);
+       mono_lock_free_queue_enqueue (image_methods, node);
+
+       class_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (coverage_classes, klass);
+
+       if (class_methods == NULL) {
+               class_methods = (MonoLockFreeQueue *) g_malloc (sizeof (MonoLockFreeQueue));
+               mono_lock_free_queue_init (class_methods);
+               mono_os_mutex_lock (&coverage_mutex);
+               mono_conc_hashtable_insert (coverage_classes, klass, class_methods);
+               mono_os_mutex_unlock (&coverage_mutex);
+       }
+
+       node = create_method_node (method);
+       mono_lock_free_queue_enqueue (class_methods, node);
+
+       return TRUE;
+}
+
+#define LINE_BUFFER_SIZE 4096
+/* Max file limit of 128KB */
+#define MAX_FILE_SIZE 128 * 1024
+static char *
+get_file_content (FILE *stream)
+{
+       char *buffer;
+       ssize_t bytes_read;
+       long filesize;
+       int res, offset = 0;
+
+       res = fseek (stream, 0, SEEK_END);
+       if (res < 0)
+         return NULL;
+
+       filesize = ftell (stream);
+       if (filesize < 0)
+         return NULL;
+
+       res = fseek (stream, 0, SEEK_SET);
+       if (res < 0)
+         return NULL;
+
+       if (filesize > MAX_FILE_SIZE)
+         return NULL;
+
+       buffer = (char *) g_malloc ((filesize + 1) * sizeof (char));
+       while ((bytes_read = fread (buffer + offset, 1, LINE_BUFFER_SIZE, stream)) > 0)
+               offset += bytes_read;
+
+       /* NULL terminate our buffer */
+       buffer[filesize] = '\0';
+       return buffer;
+}
+
+static char *
+get_next_line (char *contents, char **next_start)
+{
+       char *p = contents;
+
+       if (p == NULL || *p == '\0') {
+               *next_start = NULL;
+               return NULL;
+       }
+
+       while (*p != '\n' && *p != '\0')
+               p++;
+
+       if (*p == '\n') {
+               *p = '\0';
+               *next_start = p + 1;
+       } else
+               *next_start = NULL;
+
+       return contents;
+}
+
+static void
+init_suppressed_assemblies (void)
+{
+       char *content;
+       char *line;
+       FILE *sa_file;
+
+       suppressed_assemblies = mono_conc_hashtable_new (g_str_hash, g_str_equal);
+       sa_file = fopen (SUPPRESSION_DIR "/mono-profiler-log.suppression", "r");
+       if (sa_file == NULL)
+               return;
+
+       /* Don't need to free @content as it is referred to by the lines stored in @suppressed_assemblies */
+       content = get_file_content (sa_file);
+       if (content == NULL) {
+               g_error ("mono-profiler-log.suppression is greater than 128kb - aborting\n");
+       }
+
+       while ((line = get_next_line (content, &content))) {
+               line = g_strchomp (g_strchug (line));
+               /* No locking needed as we're doing initialization */
+               mono_conc_hashtable_insert (suppressed_assemblies, line, line);
+       }
+
+       fclose (sa_file);
+}
+
+static void
+parse_cov_filter_file (GPtrArray *filters, const char *file)
+{
+       FILE *filter_file;
+       char *line, *content;
+
+       filter_file = fopen (file, "r");
+       if (filter_file == NULL) {
+               fprintf (stderr, "Unable to open %s\n", file);
+               return;
+       }
+
+       /* Don't need to free content as it is referred to by the lines stored in @filters */
+       content = get_file_content (filter_file);
+       if (content == NULL)
+               fprintf (stderr, "WARNING: %s is greater than 128kb - ignoring\n", file);
+
+       while ((line = get_next_line (content, &content)))
+               g_ptr_array_add (filters, g_strchug (g_strchomp (line)));
+
+       fclose (filter_file);
+}
+
+static void
+coverage_init (MonoProfiler *prof)
+{
+       g_assert (!coverage_initialized && "Why are we initializing coverage twice?");
+
+       COVERAGE_DEBUG(fprintf (stderr, "Coverage initialized\n");)
+
+       mono_os_mutex_init (&coverage_mutex);
+       coverage_methods = mono_conc_hashtable_new (NULL, NULL);
+       coverage_assemblies = mono_conc_hashtable_new (NULL, NULL);
+       coverage_classes = mono_conc_hashtable_new (NULL, NULL);
+       filtered_classes = mono_conc_hashtable_new (NULL, NULL);
+       entered_methods = mono_conc_hashtable_new (NULL, NULL);
+       image_to_methods = mono_conc_hashtable_new (NULL, NULL);
+       init_suppressed_assemblies ();
+
+       coverage_initialized = TRUE;
+}
+
+static void
+unref_coverage_assemblies (gpointer key, gpointer value, gpointer userdata)
+{
+       MonoAssembly *assembly = (MonoAssembly *)value;
+       mono_assembly_close (assembly);
+}
+
+static void
+free_sample_hit (gpointer p)
+{
+       mono_lock_free_free (p, SAMPLE_BLOCK_SIZE);
+}
+
+static void
+cleanup_reusable_samples (MonoProfiler *prof)
+{
+       SampleHit *sample;
+
+       while ((sample = (SampleHit *) mono_lock_free_queue_dequeue (&prof->sample_reuse_queue)))
+               mono_thread_hazardous_try_free (sample, free_sample_hit);
+}
+
+static void
+log_shutdown (MonoProfiler *prof)
+{
+       InterlockedWrite (&in_shutdown, 1);
+
+       if (!no_counters)
+               counters_and_perfcounters_sample (prof);
+
+       dump_coverage (prof);
+
+       char c = 1;
+
+       if (write (prof->pipes [1], &c, 1) != 1) {
+               fprintf (stderr, "Could not write to pipe: %s\n", strerror (errno));
+               exit (1);
+       }
+
+       mono_native_thread_join (prof->helper_thread);
+
+       mono_os_mutex_destroy (&counters_mutex);
+
+       MonoCounterAgent *mc_next;
+
+       for (MonoCounterAgent *cur = counters; cur; cur = mc_next) {
+               mc_next = cur->next;
+               g_free (cur);
+       }
+
+       PerfCounterAgent *pc_next;
+
+       for (PerfCounterAgent *cur = perfcounters; cur; cur = pc_next) {
+               pc_next = cur->next;
+               g_free (cur);
+       }
+
+       /*
+        * Ensure that we empty the LLS completely, even if some nodes are
+        * not immediately removed upon calling mono_lls_remove (), by
+        * iterating until the head is NULL.
+        */
+       while (profiler_thread_list.head) {
+               MONO_LLS_FOREACH_SAFE (&profiler_thread_list, MonoProfilerThread, thread) {
+                       g_assert (thread->attached && "Why is a thread in the LLS not attached?");
+
+                       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);
+       mono_native_thread_join (prof->dumper_thread);
+       mono_os_sem_destroy (&prof->dumper_queue_sem);
+
+       InterlockedWrite (&prof->run_writer_thread, 0);
+       mono_os_sem_post (&prof->writer_queue_sem);
+       mono_native_thread_join (prof->writer_thread);
+       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);
+
+       /*
+        * 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 ();
+
+       g_assert (!InterlockedRead (&buffer_rwlock_count) && "Why is the reader count still non-zero?");
+       g_assert (!InterlockedReadPointer (&buffer_rwlock_exclusive) && "Why does someone still hold the exclusive lock?");
+
+#if defined (HAVE_SYS_ZLIB)
+       if (prof->gzfile)
+               gzclose (prof->gzfile);
+#endif
+       if (prof->pipe_output)
+               pclose (prof->file);
+       else
+               fclose (prof->file);
+
+       mono_conc_hashtable_destroy (prof->method_table);
+       mono_os_mutex_destroy (&prof->method_table_mutex);
+
+       if (coverage_initialized) {
+               mono_os_mutex_lock (&coverage_mutex);
+               mono_conc_hashtable_foreach (coverage_assemblies, unref_coverage_assemblies, prof);
+               mono_os_mutex_unlock (&coverage_mutex);
+
+               mono_conc_hashtable_destroy (coverage_methods);
+               mono_conc_hashtable_destroy (coverage_assemblies);
+               mono_conc_hashtable_destroy (coverage_classes);
+               mono_conc_hashtable_destroy (filtered_classes);
+
+               mono_conc_hashtable_destroy (entered_methods);
+               mono_conc_hashtable_destroy (image_to_methods);
+               mono_conc_hashtable_destroy (suppressed_assemblies);
+               mono_os_mutex_destroy (&coverage_mutex);
+       }
+
+       PROF_TLS_FREE ();
+
+       g_free (prof->args);
+       g_free (prof);
+}
+
+static char*
+new_filename (const char* filename)
+{
+       time_t t = time (NULL);
+       int pid = process_id ();
+       char pid_buf [16];
+       char time_buf [16];
+       char *res, *d;
+       const char *p;
+       int count_dates = 0;
+       int count_pids = 0;
+       int s_date, s_pid;
+       struct tm *ts;
+       for (p = filename; *p; p++) {
+               if (*p != '%')
+                       continue;
+               p++;
+               if (*p == 't')
+                       count_dates++;
+               else if (*p == 'p')
+                       count_pids++;
+               else if (*p == 0)
+                       break;
+       }
+       if (!count_dates && !count_pids)
+               return pstrdup (filename);
+       snprintf (pid_buf, sizeof (pid_buf), "%d", pid);
+       ts = gmtime (&t);
+       snprintf (time_buf, sizeof (time_buf), "%d%02d%02d%02d%02d%02d",
+               1900 + ts->tm_year, 1 + ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec);
+       s_date = strlen (time_buf);
+       s_pid = strlen (pid_buf);
+       d = res = (char *) g_malloc (strlen (filename) + s_date * count_dates + s_pid * count_pids);
+       for (p = filename; *p; p++) {
+               if (*p != '%') {
+                       *d++ = *p;
+                       continue;
+               }
+               p++;
+               if (*p == 't') {
+                       strcpy (d, time_buf);
+                       d += s_date;
+                       continue;
+               } else if (*p == 'p') {
+                       strcpy (d, pid_buf);
+                       d += s_pid;
+                       continue;
+               } else if (*p == '%') {
+                       *d++ = '%';
+                       continue;
+               } else if (*p == 0)
+                       break;
+               *d++ = '%';
+               *d++ = *p;
+       }
+       *d = 0;
+       return res;
+}
+
+static void
+add_to_fd_set (fd_set *set, int fd, int *max_fd)
+{
+       /*
+        * This should only trigger for the basic FDs (server socket, pipes) at
+        * startup if for some mysterious reason they're too large. In this case,
+        * the profiler really can't function, and we're better off printing an
+        * error and exiting.
+        */
+       if (fd >= FD_SETSIZE) {
+               fprintf (stderr, "File descriptor is out of bounds for fd_set: %d\n", fd);
+               exit (1);
+       }
+
+       FD_SET (fd, set);
+
+       if (*max_fd < fd)
+               *max_fd = fd;
+}
+
+static void *
+helper_thread (void *arg)
+{
+       MonoProfiler *prof = (MonoProfiler *) arg;
+
+       mono_threads_attach_tools_thread ();
+       mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler helper");
+
+       MonoProfilerThread *thread = init_thread (prof, FALSE);
+
+       GArray *command_sockets = g_array_new (FALSE, FALSE, sizeof (int));
+
+       while (1) {
+               fd_set rfds;
+               int max_fd = -1;
+
+               FD_ZERO (&rfds);
+
+               add_to_fd_set (&rfds, prof->server_socket, &max_fd);
+               add_to_fd_set (&rfds, prof->pipes [0], &max_fd);
+
+               for (gint i = 0; i < command_sockets->len; i++)
+                       add_to_fd_set (&rfds, g_array_index (command_sockets, int, i), &max_fd);
+
+               struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
+
+               // Sleep for 1sec or until a file descriptor has data.
+               if (select (max_fd + 1, &rfds, NULL, NULL, &tv) == -1) {
+                       if (errno == EINTR)
+                               continue;
+
+                       fprintf (stderr, "Error in mono-profiler-log server: %s", strerror (errno));
+                       exit (1);
+               }
+
+               if (!no_counters)
+                       counters_and_perfcounters_sample (prof);
+
+               buffer_lock_excl ();
+
+               sync_point (SYNC_POINT_PERIODIC);
+
+               buffer_unlock_excl ();
+
+               // Are we shutting down?
+               if (FD_ISSET (prof->pipes [0], &rfds)) {
+                       char c;
+                       read (prof->pipes [0], &c, 1);
+                       break;
+               }
+
+               for (gint i = 0; i < command_sockets->len; i++) {
+                       int fd = g_array_index (command_sockets, int, i);
+
+                       if (!FD_ISSET (fd, &rfds))
+                               continue;
+
+                       char buf [64];
+                       int len = read (fd, buf, sizeof (buf) - 1);
+
+                       if (len == -1)
+                               continue;
+
+                       if (!len) {
+                               // The other end disconnected.
+                               g_array_remove_index (command_sockets, i);
+                               close (fd);
+
+                               continue;
+                       }
+
+                       buf [len] = 0;
+
+                       if (!strcmp (buf, "heapshot\n") && hs_mode_ondemand) {
+                               // Rely on the finalization callbacks invoking process_requests ().
+                               heapshot_requested = 1;
+                               mono_gc_finalize_notify ();
+                       }
+               }
+
+               if (FD_ISSET (prof->server_socket, &rfds)) {
+                       int fd = accept (prof->server_socket, NULL, NULL);
+
+                       if (fd != -1) {
+                               if (fd >= FD_SETSIZE)
+                                       close (fd);
+                               else
+                                       g_array_append_val (command_sockets, fd);
+                       }
+               }
+       }
+
+       for (gint i = 0; i < command_sockets->len; i++)
+               close (g_array_index (command_sockets, int, i));
+
+       g_array_free (command_sockets, TRUE);
+
+       send_log_unsafe (FALSE);
+       deinit_thread (thread);
+
+       mono_thread_info_detach ();
+
+       return NULL;
+}
+
+static void
+start_helper_thread (MonoProfiler* prof)
+{
+       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 == -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)) == -1) {
+               fprintf (stderr, "Cannot bind server socket on port %d: %s\n", prof->command_port, strerror (errno));
+               close (prof->server_socket);
+               exit (1);
+       }
+
+       if (listen (prof->server_socket, 1) == -1) {
+               fprintf (stderr, "Cannot listen on server socket: %s\n", strerror (errno));
+               close (prof->server_socket);
+               exit (1);
+       }
+
+       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);
+       }
+
+       prof->command_port = ntohs (server_address.sin_port);
+
+       if (!mono_native_thread_create (&prof->helper_thread, helper_thread, prof)) {
+               fprintf (stderr, "Could not start helper thread\n");
+               close (prof->server_socket);
+               exit (1);
+       }
+}
+
+static void
+free_writer_entry (gpointer p)
+{
+       mono_lock_free_free (p, WRITER_ENTRY_BLOCK_SIZE);
+}
+
+static gboolean
+handle_writer_queue_entry (MonoProfiler *prof)
+{
+       WriterQueueEntry *entry;
+
+       if ((entry = (WriterQueueEntry *) mono_lock_free_queue_dequeue (&prof->writer_queue))) {
+               if (!entry->methods)
+                       goto no_methods;
+
+               gboolean wrote_methods = FALSE;
+
+               /*
+                * 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.
+                *
+                * We use a 'proper' thread-local buffer for this as opposed
+                * to allocating and freeing a buffer by hand because the call
+                * to mono_method_full_name () below may trigger class load
+                * events when it retrieves the signature of the method. So a
+                * thread-local buffer needs to exist when such events occur.
+                */
+               for (guint i = 0; i < entry->methods->len; i++) {
+                       MethodInfo *info = (MethodInfo *) g_ptr_array_index (entry->methods, i);
+
+                       if (mono_conc_hashtable_lookup (prof->method_table, info->method))
+                               goto free_info; // This method already has metadata emitted.
+
+                       /*
+                        * 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_os_mutex_lock (&prof->method_table_mutex);
+                       mono_conc_hashtable_insert (prof->method_table, info->method, info->method);
+                       mono_os_mutex_unlock (&prof->method_table_mutex);
+
+                       char *name = mono_method_full_name (info->method, 1);
+                       int nlen = strlen (name) + 1;
+                       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;
+
+                       ENTER_LOG (&method_jits_ctr, logbuffer,
+                               EVENT_SIZE /* event */ +
+                               LEB128_SIZE /* method */ +
+                               LEB128_SIZE /* start */ +
+                               LEB128_SIZE /* size */ +
+                               nlen /* name */
+                       );
+
+                       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;
+
+                       EXIT_LOG_EXPLICIT (NO_SEND, NO_REQUESTS);
+
+                       mono_free (name);
+
+                       wrote_methods = TRUE;
+
+               free_info:
+                       g_free (info);
+               }
+
+               g_ptr_array_free (entry->methods, TRUE);
+
+               if (wrote_methods) {
+                       dump_buffer_threadless (prof, PROF_TLS_GET ()->buffer);
+                       init_buffer_state (PROF_TLS_GET ());
+               }
+
+       no_methods:
+               dump_buffer (prof, entry->buffer);
+
+               mono_thread_hazardous_try_free (entry, free_writer_entry);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void *
+writer_thread (void *arg)
+{
+       MonoProfiler *prof = (MonoProfiler *)arg;
+
+       mono_threads_attach_tools_thread ();
+       mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler writer");
+
+       dump_header (prof);
+
+       MonoProfilerThread *thread = init_thread (prof, FALSE);
+
+       while (InterlockedRead (&prof->run_writer_thread)) {
+               mono_os_sem_wait (&prof->writer_queue_sem, MONO_SEM_FLAGS_NONE);
+               handle_writer_queue_entry (prof);
+       }
+
+       /* Drain any remaining entries on shutdown. */
+       while (handle_writer_queue_entry (prof));
+
+       free_buffer (thread->buffer, thread->buffer->size);
+       deinit_thread (thread);
+
+       mono_thread_info_detach ();
+
+       return NULL;
+}
+
+static void
+start_writer_thread (MonoProfiler* prof)
+{
+       InterlockedWrite (&prof->run_writer_thread, 1);
+
+       if (!mono_native_thread_create (&prof->writer_thread, writer_thread, prof)) {
+               fprintf (stderr, "Could not start writer thread\n");
+               exit (1);
+       }
+}
+
+static void
+reuse_sample_hit (gpointer p)
+{
+       SampleHit *sample = p;
+
+       mono_lock_free_queue_node_unpoison (&sample->node);
+       mono_lock_free_queue_enqueue (&sample->prof->sample_reuse_queue, &sample->node);
+}
+
+static gboolean
+handle_dumper_queue_entry (MonoProfiler *prof)
+{
+       SampleHit *sample;
+
+       if ((sample = (SampleHit *) mono_lock_free_queue_dequeue (&prof->dumper_queue))) {
+               for (int i = 0; i < sample->count; ++i) {
+                       MonoMethod *method = sample->frames [i].method;
+                       MonoDomain *domain = sample->frames [i].domain;
+                       void *address = sample->frames [i].base_address;
+
+                       if (!method) {
+                               g_assert (domain && "What happened to the domain pointer?");
+                               g_assert (address && "What happened to the instruction pointer?");
+
+                               MonoJitInfo *ji = mono_jit_info_table_find (domain, (char *) address);
+
+                               if (ji)
+                                       sample->frames [i].method = mono_jit_info_get_method (ji);
+                       }
+               }
+
+               ENTER_LOG (&sample_hits_ctr, logbuffer,
+                       EVENT_SIZE /* event */ +
+                       BYTE_SIZE /* type */ +
+                       LEB128_SIZE /* tid */ +
+                       LEB128_SIZE /* count */ +
+                       1 * (
+                               LEB128_SIZE /* ip */
+                       ) +
+                       LEB128_SIZE /* managed count */ +
+                       sample->count * (
+                               LEB128_SIZE /* method */
+                       )
+               );
+
+               emit_event_time (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_HIT, sample->time);
+               emit_byte (logbuffer, SAMPLE_CYCLES);
+               emit_ptr (logbuffer, (void *) sample->tid);
+               emit_value (logbuffer, 1);
+
+               // TODO: Actual native unwinding.
+               for (int i = 0; i < 1; ++i) {
+                       emit_ptr (logbuffer, sample->ip);
+                       add_code_pointer ((uintptr_t) sample->ip);
+               }
+
+               /* new in data version 6 */
+               emit_uvalue (logbuffer, sample->count);
+
+               for (int i = 0; i < sample->count; ++i)
+                       emit_method (logbuffer, sample->frames [i].method);
+
+               EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
+
+               mono_thread_hazardous_try_free (sample, reuse_sample_hit);
+
+               dump_unmanaged_coderefs (prof);
+       }
+
+       return FALSE;
+}
+
+static void *
+dumper_thread (void *arg)
+{
+       MonoProfiler *prof = (MonoProfiler *)arg;
+
+       mono_threads_attach_tools_thread ();
+       mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler dumper");
+
+       MonoProfilerThread *thread = init_thread (prof, FALSE);
+
+       while (InterlockedRead (&prof->run_dumper_thread)) {
+               mono_os_sem_wait (&prof->dumper_queue_sem, MONO_SEM_FLAGS_NONE);
+               handle_dumper_queue_entry (prof);
+       }
+
+       /* Drain any remaining entries on shutdown. */
+       while (handle_dumper_queue_entry (prof));
+
+       send_log_unsafe (FALSE);
+       deinit_thread (thread);
+
+       mono_thread_info_detach ();
+
+       return NULL;
+}
+
+static void
+start_dumper_thread (MonoProfiler* prof)
+{
+       InterlockedWrite (&prof->run_dumper_thread, 1);
+
+       if (!mono_native_thread_create (&prof->dumper_thread, dumper_thread, prof)) {
+               fprintf (stderr, "Could not start dumper thread\n");
+               exit (1);
+       }
+}
+
+static void
+register_counter (const char *name, gint32 *counter)
+{
+       mono_counters_register (name, MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, counter);
+}
+
+static void
+runtime_initialized (MonoProfiler *profiler)
+{
+       InterlockedWrite (&runtime_inited, 1);
+
+       register_counter ("Sample events allocated", &sample_allocations_ctr);
+       register_counter ("Log buffers allocated", &buffer_allocations_ctr);
+
+       register_counter ("Event: Sync points", &sync_points_ctr);
+       register_counter ("Event: Heap objects", &heap_objects_ctr);
+       register_counter ("Event: Heap starts", &heap_starts_ctr);
+       register_counter ("Event: Heap ends", &heap_ends_ctr);
+       register_counter ("Event: Heap roots", &heap_roots_ctr);
+       register_counter ("Event: GC events", &gc_events_ctr);
+       register_counter ("Event: GC resizes", &gc_resizes_ctr);
+       register_counter ("Event: GC allocations", &gc_allocs_ctr);
+       register_counter ("Event: GC moves", &gc_moves_ctr);
+       register_counter ("Event: GC handle creations", &gc_handle_creations_ctr);
+       register_counter ("Event: GC handle deletions", &gc_handle_deletions_ctr);
+       register_counter ("Event: GC finalize starts", &finalize_begins_ctr);
+       register_counter ("Event: GC finalize ends", &finalize_ends_ctr);
+       register_counter ("Event: GC finalize object starts", &finalize_object_begins_ctr);
+       register_counter ("Event: GC finalize object ends", &finalize_object_ends_ctr);
+       register_counter ("Event: Image loads", &image_loads_ctr);
+       register_counter ("Event: Image unloads", &image_unloads_ctr);
+       register_counter ("Event: Assembly loads", &assembly_loads_ctr);
+       register_counter ("Event: Assembly unloads", &assembly_unloads_ctr);
+       register_counter ("Event: Class loads", &class_loads_ctr);
+       register_counter ("Event: Class unloads", &class_unloads_ctr);
+       register_counter ("Event: Method entries", &method_entries_ctr);
+       register_counter ("Event: Method exits", &method_exits_ctr);
+       register_counter ("Event: Method exception leaves", &method_exception_exits_ctr);
+       register_counter ("Event: Method JITs", &method_jits_ctr);
+       register_counter ("Event: Code buffers", &code_buffers_ctr);
+       register_counter ("Event: Exception throws", &exception_throws_ctr);
+       register_counter ("Event: Exception clauses", &exception_clauses_ctr);
+       register_counter ("Event: Monitor contentions", &monitor_contentions_ctr);
+       register_counter ("Event: Monitor acquisitions", &monitor_acquisitions_ctr);
+       register_counter ("Event: Monitor failures", &monitor_failures_ctr);
+       register_counter ("Event: Thread starts", &thread_starts_ctr);
+       register_counter ("Event: Thread ends", &thread_ends_ctr);
+       register_counter ("Event: Thread names", &thread_names_ctr);
+       register_counter ("Event: Domain loads", &domain_loads_ctr);
+       register_counter ("Event: Domain unloads", &domain_unloads_ctr);
+       register_counter ("Event: Domain names", &domain_names_ctr);
+       register_counter ("Event: Context loads", &context_loads_ctr);
+       register_counter ("Event: Context unloads", &context_unloads_ctr);
+       register_counter ("Event: Sample binaries", &sample_ubins_ctr);
+       register_counter ("Event: Sample symbols", &sample_usyms_ctr);
+       register_counter ("Event: Sample hits", &sample_hits_ctr);
+       register_counter ("Event: Counter descriptors", &counter_descriptors_ctr);
+       register_counter ("Event: Counter samples", &counter_samples_ctr);
+       register_counter ("Event: Performance counter descriptors", &perfcounter_descriptors_ctr);
+       register_counter ("Event: Performance counter samples", &perfcounter_samples_ctr);
+       register_counter ("Event: Coverage methods", &coverage_methods_ctr);
+       register_counter ("Event: Coverage statements", &coverage_statements_ctr);
+       register_counter ("Event: Coverage classes", &coverage_classes_ctr);
+       register_counter ("Event: Coverage assemblies", &coverage_assemblies_ctr);
+
+       counters_init (profiler);
+
+       /*
+        * We must start the helper thread before the writer thread. This is
+        * because the helper thread sets up the command port which is written to
+        * the log header by the writer thread.
+        */
+       start_helper_thread (profiler);
+       start_writer_thread (profiler);
+       start_dumper_thread (profiler);
+}
+
+static MonoProfiler*
+create_profiler (const char *args, const char *filename, GPtrArray *filters)
+{
+       MonoProfiler *prof;
+       char *nf;
+       int force_delete = 0;
+       prof = (MonoProfiler *) g_calloc (1, sizeof (MonoProfiler));
+
+       prof->args = pstrdup (args);
+       prof->command_port = command_port;
+       if (filename && *filename == '-') {
+               force_delete = 1;
+               filename++;
+               g_warning ("WARNING: the output:-FILENAME option is deprecated, the profiler now always overrides the output file\n");
+       }
+       if (!filename) {
+               if (do_report)
+                       filename = "|mprof-report -";
+               else
+                       filename = "output.mlpd";
+               nf = (char*)filename;
+       } else {
+               nf = new_filename (filename);
+               if (do_report) {
+                       int s = strlen (nf) + 32;
+                       char *p = (char *) g_malloc (s);
+                       snprintf (p, s, "|mprof-report '--out=%s' -", nf);
+                       g_free (nf);
+                       nf = p;
+               }
+       }
+       if (*nf == '|') {
+               prof->file = popen (nf + 1, "w");
+               prof->pipe_output = 1;
+       } else if (*nf == '#') {
+               int fd = strtol (nf + 1, NULL, 10);
+               prof->file = fdopen (fd, "a");
+       } else {
+               if (force_delete)
+                       unlink (nf);
+               prof->file = fopen (nf, "wb");
+       }
+       if (!prof->file) {
+               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 you hit this assert while increasing MAX_FRAMES, you need to increase
+        * SAMPLE_BLOCK_SIZE as well.
+        */
+       g_assert (SAMPLE_SLOT_SIZE (MAX_FRAMES) * 2 < LOCK_FREE_ALLOC_SB_USABLE_SIZE (SAMPLE_BLOCK_SIZE));
+
+       // FIXME: We should free this stuff too.
+       mono_lock_free_allocator_init_size_class (&prof->sample_size_class, SAMPLE_SLOT_SIZE (num_frames), SAMPLE_BLOCK_SIZE);
+       mono_lock_free_allocator_init_allocator (&prof->sample_allocator, &prof->sample_size_class, MONO_MEM_ACCOUNT_PROFILER);
+
+       mono_lock_free_queue_init (&prof->sample_reuse_queue);
+
+       g_assert (sizeof (WriterQueueEntry) * 2 < LOCK_FREE_ALLOC_SB_USABLE_SIZE (WRITER_ENTRY_BLOCK_SIZE));
+
+       // FIXME: We should free this stuff too.
+       mono_lock_free_allocator_init_size_class (&prof->writer_entry_size_class, sizeof (WriterQueueEntry), WRITER_ENTRY_BLOCK_SIZE);
+       mono_lock_free_allocator_init_allocator (&prof->writer_entry_allocator, &prof->writer_entry_size_class, MONO_MEM_ACCOUNT_PROFILER);
+
+       mono_lock_free_queue_init (&prof->writer_queue);
+       mono_os_sem_init (&prof->writer_queue_sem, 0);
+
+       mono_lock_free_queue_init (&prof->dumper_queue);
+       mono_os_sem_init (&prof->dumper_queue_sem, 0);
+
+       mono_os_mutex_init (&prof->method_table_mutex);
+       prof->method_table = mono_conc_hashtable_new (NULL, NULL);
+
+       if (do_coverage)
+               coverage_init (prof);
+       prof->coverage_filters = filters;
+
+       prof->startup_time = current_time ();
+       return prof;
+}
+
+/*
+ * declaration to silence the compiler: this is the entry point that
+ * mono will load from the shared library and call.
+ */
+extern void
+mono_profiler_startup (const char *desc);
+
+extern void
+mono_profiler_startup_log (const char *desc);
+
+/*
+ * this is the entry point that will be used when the profiler
+ * is embedded inside the main executable.
+ */
+void
+mono_profiler_startup_log (const char *desc)
+{
+       mono_profiler_startup (desc);
+}
+
+static ProfilerConfig config;
+
+void
+mono_profiler_startup (const char *desc)
+{
+       GPtrArray *filters = NULL;
+       MonoProfiler *prof;
+
+       proflog_parse_args (&config, desc [3] == ':' ? desc + 4 : "");
+
+       //XXX maybe later cleanup to use config directly
+       nocalls = !(config.effective_mask & PROFLOG_CALL_EVENTS);
+       no_counters = !(config.effective_mask & PROFLOG_COUNTER_EVENTS);
+       do_report = config.do_report;
+       do_debug = config.do_debug;
+       do_heap_shot = (config.effective_mask & PROFLOG_HEAPSHOT_FEATURE);
+       hs_mode_ondemand = config.hs_mode_ondemand;
+       hs_mode_ms = config.hs_mode_ms;
+       hs_mode_gc = config.hs_mode_gc;
+       do_mono_sample = (config.effective_mask & PROFLOG_SAMPLING_FEATURE);
+       use_zip = config.use_zip;
+       command_port = config.command_port;
+       num_frames = config.num_frames;
+       notraces = config.notraces;
+       max_allocated_sample_hits = config.max_allocated_sample_hits;
+       max_call_depth = config.max_call_depth;
+       do_coverage = (config.effective_mask & PROFLOG_CODE_COV_FEATURE);
+       debug_coverage = config.debug_coverage;
+       only_coverage = config.only_coverage;
+
+       if (config.cov_filter_files) {
+               filters = g_ptr_array_new ();
+               int i;
+               for (i = 0; i < config.cov_filter_files->len; ++i) {
+                       const char *name = config.cov_filter_files->pdata [i];
+                       parse_cov_filter_file (filters, name);
+               }
+       }
+
+       init_time ();
+
+       PROF_TLS_INIT ();
+
+       prof = create_profiler (desc, config.output_filename, filters);
+       if (!prof) {
+               PROF_TLS_FREE ();
+               return;
+       }
+
+       mono_lls_init (&profiler_thread_list, NULL);
+
+       init_thread (prof, TRUE);
+
+       //This two events are required for the profiler to work
+       int events = MONO_PROFILE_THREADS | MONO_PROFILE_GC;
+
+       //Required callbacks
+       mono_profiler_install (prof, log_shutdown);
+       mono_profiler_install_runtime_initialized (runtime_initialized);
+
+       mono_profiler_install_gc (gc_event, gc_resize);
+       mono_profiler_install_thread (thread_start, thread_end);
+
+       //It's questionable whether we actually want this to be mandatory, maybe put it behind the actual event?
+       mono_profiler_install_thread_name (thread_name);
+
+
+       if (config.effective_mask & PROFLOG_DOMAIN_EVENTS) {
+               events |= MONO_PROFILE_APPDOMAIN_EVENTS;
+               mono_profiler_install_appdomain (NULL, domain_loaded, domain_unloaded, NULL);
+               mono_profiler_install_appdomain_name (domain_name);
+       }
+
+       if (config.effective_mask & PROFLOG_ASSEMBLY_EVENTS) {
+               events |= MONO_PROFILE_ASSEMBLY_EVENTS;
+               mono_profiler_install_assembly (NULL, assembly_loaded, assembly_unloaded, NULL);
+       }
+
+       if (config.effective_mask & PROFLOG_MODULE_EVENTS) {
+               events |= MONO_PROFILE_MODULE_EVENTS;
+               mono_profiler_install_module (NULL, image_loaded, image_unloaded, NULL);
+       }
+
+       if (config.effective_mask & PROFLOG_CLASS_EVENTS) {
+               events |= MONO_PROFILE_CLASS_EVENTS;
+               mono_profiler_install_class (NULL, class_loaded, class_unloaded, NULL);
+       }
+
+       if (config.effective_mask & PROFLOG_JIT_COMPILATION_EVENTS) {
+               events |= MONO_PROFILE_JIT_COMPILATION;
+               mono_profiler_install_jit_end (method_jitted);
+               mono_profiler_install_code_buffer_new (code_buffer_new);
+       }
+
+       if (config.effective_mask & PROFLOG_EXCEPTION_EVENTS) {
+               events |= MONO_PROFILE_EXCEPTIONS;
+               mono_profiler_install_exception (throw_exc, method_exc_leave, clause_exc);
+       }
+
+       if (config.effective_mask & PROFLOG_ALLOCATION_EVENTS) {
+               events |= MONO_PROFILE_ALLOCATIONS;
+               mono_profiler_install_allocation (gc_alloc);
+       }
+
+       //PROFLOG_GC_EVENTS is mandatory
+       //PROFLOG_THREAD_EVENTS is mandatory
+
+       if (config.effective_mask & PROFLOG_CALL_EVENTS) {
+               events |= MONO_PROFILE_ENTER_LEAVE;
+               mono_profiler_install_enter_leave (method_enter, method_leave);
+       }
+
+       if (config.effective_mask & PROFLOG_INS_COVERAGE_EVENTS) {
+               events |= MONO_PROFILE_INS_COVERAGE;
+               mono_profiler_install_coverage_filter (coverage_filter);
+       }
+
+       //XXX should we check for PROFLOG_SAMPLING_FEATURE instead??
+       if (config.effective_mask & PROFLOG_SAMPLING_EVENTS) {
+               events |= MONO_PROFILE_STATISTICAL;
+               mono_profiler_set_statistical_mode (config.sampling_mode, config.sample_freq);
+               mono_profiler_install_statistical (mono_sample_hit);
+       }
+
+       if (config.effective_mask & PROFLOG_MONITOR_EVENTS) {
+               events |= MONO_PROFILE_MONITOR_EVENTS;
+               mono_profiler_install_monitor (monitor_event);
+       }
+
+       if (config.effective_mask & PROFLOG_GC_MOVES_EVENTS) {
+               events |= MONO_PROFILE_GC_MOVES;
+               mono_profiler_install_gc_moves (gc_moves);
+       }
+
+       // TODO split those in two profiler events
+       if (config.effective_mask & (PROFLOG_GC_ROOT_EVENTS | PROFLOG_GC_HANDLE_EVENTS)) {
+               events |= MONO_PROFILE_GC_ROOTS;
+               mono_profiler_install_gc_roots (
+                       config.effective_mask & (PROFLOG_GC_HANDLE_EVENTS) ? gc_handle : NULL,
+                       (config.effective_mask & PROFLOG_GC_ROOT_EVENTS) ? gc_roots : NULL);
+       }
+
+       if (config.effective_mask & PROFLOG_CONTEXT_EVENTS) {
+               events |= MONO_PROFILE_CONTEXT_EVENTS;
+               mono_profiler_install_context (context_loaded, context_unloaded);
+       }
+
+       if (config.effective_mask & PROFLOG_FINALIZATION_EVENTS) {
+               events |= MONO_PROFILE_GC_FINALIZATION;
+               mono_profiler_install_gc_finalize (finalize_begin, finalize_object_begin, finalize_object_end, finalize_end);   
+       }
+
+       //PROFLOG_COUNTER_EVENTS is a pseudo event controled by the no_counters global var
+       //PROFLOG_GC_HANDLE_EVENTS is handled together with PROFLOG_GC_ROOT_EVENTS
+
+       mono_profiler_set_events ((MonoProfileFlags)events);
+}
diff --git a/mono/profiler/log.h b/mono/profiler/log.h
new file mode 100644 (file)
index 0000000..ee1d1d3
--- /dev/null
@@ -0,0 +1,267 @@
+#ifndef __MONO_PROFLOG_H__
+#define __MONO_PROFLOG_H__
+
+#include <glib.h>
+#include <mono/metadata/profiler.h>
+
+#define BUF_ID 0x4D504C01
+#define LOG_HEADER_ID 0x4D505A01
+#define LOG_VERSION_MAJOR 1
+#define LOG_VERSION_MINOR 1
+#define LOG_DATA_VERSION 13
+
+/*
+ * Changes in major/minor versions:
+ * version 1.0: removed sysid field from header
+ *              added args, arch, os fields to header
+ *
+ * Changes in data versions:
+ * version 2: added offsets in heap walk
+ * version 3: added GC roots
+ * version 4: added sample/statistical profiling
+ * version 5: added counters sampling
+ * version 6: added optional backtrace in sampling info
+ * version 8: added TYPE_RUNTIME and JIT helpers/trampolines
+ * version 9: added MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING
+ * version 10: added TYPE_COVERAGE
+ * version 11: added thread ID to TYPE_SAMPLE_HIT
+               added more load/unload events
+                   unload for class
+                   unload for image
+                   load/unload for appdomain
+                   load/unload for contexts
+                   load/unload/name for assemblies
+               removed TYPE_LOAD_ERR flag (profiler never generated it, now removed from the format itself)
+               added TYPE_GC_HANDLE_{CREATED,DESTROYED}_BT
+               TYPE_JIT events are no longer guaranteed to have code start/size info (can be zero)
+ * version 12: added MONO_COUNTER_PROFILER
+ * version 13: added MONO_GC_EVENT_{PRE_STOP_WORLD_LOCKED,POST_START_WORLD_UNLOCKED}
+               added TYPE_META + TYPE_SYNC_POINT
+               removed il and native offset in TYPE_SAMPLE_HIT
+               methods in backtraces are now encoded as proper method pointers
+               removed flags in backtrace format
+               removed flags in metadata events
+               changed the following fields to a single byte rather than leb128
+                   TYPE_GC_EVENT: event_type, generation
+                   TYPE_HEAP_ROOT: root_type
+                   TYPE_JITHELPER: type
+                   TYPE_SAMPLE_HIT: sample_type
+                   TYPE_CLAUSE: clause_type
+                   TYPE_SAMPLE_COUNTERS_DESC: type, unit, variance
+                   TYPE_SAMPLE_COUNTERS: type
+               added time fields to all events that were missing one
+                   TYPE_HEAP_OBJECT
+                   TYPE_HEAP_ROOT
+                   TYPE_SAMPLE_USYM
+                   TYPE_SAMPLE_COUNTERS_DESC
+                   TYPE_COVERAGE_METHOD
+                   TYPE_COVERAGE_STATEMENT
+                   TYPE_COVERAGE_CLASS
+                   TYPE_COVERAGE_ASSEMBLY
+               moved the time field in TYPE_SAMPLE_HIT to right after the event byte, now encoded as a regular time field
+               changed the time field in TYPE_SAMPLE_COUNTERS to be encoded as a regular time field (in nanoseconds)
+               added TYPE_GC_FINALIZE_{START,END,OBJECT_START,OBJECT_END}
+ */
+
+enum {
+       TYPE_ALLOC,
+       TYPE_GC,
+       TYPE_METADATA,
+       TYPE_METHOD,
+       TYPE_EXCEPTION,
+       TYPE_MONITOR,
+       TYPE_HEAP,
+       TYPE_SAMPLE,
+       TYPE_RUNTIME,
+       TYPE_COVERAGE,
+       TYPE_META,
+       /* extended type for TYPE_HEAP */
+       TYPE_HEAP_START  = 0 << 4,
+       TYPE_HEAP_END    = 1 << 4,
+       TYPE_HEAP_OBJECT = 2 << 4,
+       TYPE_HEAP_ROOT   = 3 << 4,
+       /* extended type for TYPE_METADATA */
+       TYPE_END_LOAD     = 2 << 4,
+       TYPE_END_UNLOAD   = 4 << 4,
+       /* extended type for TYPE_GC */
+       TYPE_GC_EVENT  = 1 << 4,
+       TYPE_GC_RESIZE = 2 << 4,
+       TYPE_GC_MOVE   = 3 << 4,
+       TYPE_GC_HANDLE_CREATED      = 4 << 4,
+       TYPE_GC_HANDLE_DESTROYED    = 5 << 4,
+       TYPE_GC_HANDLE_CREATED_BT   = 6 << 4,
+       TYPE_GC_HANDLE_DESTROYED_BT = 7 << 4,
+       TYPE_GC_FINALIZE_START = 8 << 4,
+       TYPE_GC_FINALIZE_END = 9 << 4,
+       TYPE_GC_FINALIZE_OBJECT_START = 10 << 4,
+       TYPE_GC_FINALIZE_OBJECT_END = 11 << 4,
+       /* extended type for TYPE_METHOD */
+       TYPE_LEAVE     = 1 << 4,
+       TYPE_ENTER     = 2 << 4,
+       TYPE_EXC_LEAVE = 3 << 4,
+       TYPE_JIT       = 4 << 4,
+       /* extended type for TYPE_EXCEPTION */
+       TYPE_THROW_NO_BT = 0 << 7,
+       TYPE_THROW_BT    = 1 << 7,
+       TYPE_CLAUSE      = 1 << 4,
+       /* extended type for TYPE_ALLOC */
+       TYPE_ALLOC_NO_BT  = 0 << 4,
+       TYPE_ALLOC_BT     = 1 << 4,
+       /* extended type for TYPE_MONITOR */
+       TYPE_MONITOR_NO_BT  = 0 << 7,
+       TYPE_MONITOR_BT     = 1 << 7,
+       /* extended type for TYPE_SAMPLE */
+       TYPE_SAMPLE_HIT           = 0 << 4,
+       TYPE_SAMPLE_USYM          = 1 << 4,
+       TYPE_SAMPLE_UBIN          = 2 << 4,
+       TYPE_SAMPLE_COUNTERS_DESC = 3 << 4,
+       TYPE_SAMPLE_COUNTERS      = 4 << 4,
+       /* extended type for TYPE_RUNTIME */
+       TYPE_JITHELPER = 1 << 4,
+       /* extended type for TYPE_COVERAGE */
+       TYPE_COVERAGE_ASSEMBLY = 0 << 4,
+       TYPE_COVERAGE_METHOD   = 1 << 4,
+       TYPE_COVERAGE_STATEMENT = 2 << 4,
+       TYPE_COVERAGE_CLASS = 3 << 4,
+       /* extended type for TYPE_META */
+       TYPE_SYNC_POINT = 0 << 4,
+       TYPE_END
+};
+
+enum {
+       /* metadata type byte for TYPE_METADATA */
+       TYPE_CLASS    = 1,
+       TYPE_IMAGE    = 2,
+       TYPE_ASSEMBLY = 3,
+       TYPE_DOMAIN   = 4,
+       TYPE_THREAD   = 5,
+       TYPE_CONTEXT  = 6,
+};
+
+typedef enum {
+       SYNC_POINT_PERIODIC,
+       SYNC_POINT_WORLD_STOP,
+       SYNC_POINT_WORLD_START
+} MonoProfilerSyncPointType;
+
+// Sampling sources
+// Unless you have compiled with --enable-perf-events, only SAMPLE_CYCLES is available
+enum {
+       SAMPLE_CYCLES = 1,
+       SAMPLE_INSTRUCTIONS,
+       SAMPLE_CACHE_MISSES,
+       SAMPLE_CACHE_REFS,
+       SAMPLE_BRANCHES,
+       SAMPLE_BRANCH_MISSES,
+       SAMPLE_LAST
+};
+
+
+// If you alter MAX_FRAMES, you may need to alter SAMPLE_BLOCK_SIZE too.
+#define MAX_FRAMES 32
+
+//The following flags control emitting individual events
+#define PROFLOG_DOMAIN_EVENTS (1 << 0)
+#define PROFLOG_ASSEMBLY_EVENTS        (1 << 1)
+#define PROFLOG_MODULE_EVENTS (1 << 2)
+#define PROFLOG_CLASS_EVENTS (1 << 3)
+#define PROFLOG_JIT_COMPILATION_EVENTS (1 << 4)
+#define PROFLOG_EXCEPTION_EVENTS (1 << 5)
+#define PROFLOG_ALLOCATION_EVENTS (1 << 6)
+#define PROFLOG_GC_EVENTS (1 << 7)
+#define PROFLOG_THREAD_EVENTS (1 << 8)
+//This generate enter/leave events
+#define PROFLOG_CALL_EVENTS (1 << 9)
+#define PROFLOG_INS_COVERAGE_EVENTS (1 << 10)
+#define PROFLOG_SAMPLING_EVENTS (1 << 11)
+#define PROFLOG_MONITOR_EVENTS (1 << 12)
+#define PROFLOG_GC_MOVES_EVENTS (1 << 13)
+
+#define PROFLOG_GC_ROOT_EVENTS (1 << 14)
+#define PROFLOG_CONTEXT_EVENTS (1 << 15)
+#define PROFLOG_FINALIZATION_EVENTS (1 << 16)
+#define PROFLOG_COUNTER_EVENTS (1 << 17)
+#define PROFLOG_GC_HANDLE_EVENTS (1 << 18)
+
+//The following flags control whole subsystems
+//Enables code coverage generation
+#define PROFLOG_CODE_COV_FEATURE (1 << 19)
+//This enables sampling to be generated
+#define PROFLOG_SAMPLING_FEATURE (1 << 20)
+//This enable heap dumping during GCs and filter GCRoots and GCHandle events outside of the dumped collections
+#define PROFLOG_HEAPSHOT_FEATURE (1 << 21)
+
+
+
+//The follow flags are the common aliases we want ppl to use
+#define PROFLOG_TYPELOADING_ALIAS (PROFLOG_DOMAIN_EVENTS | PROFLOG_ASSEMBLY_EVENTS | PROFLOG_MODULE_EVENTS | PROFLOG_CLASS_EVENTS)
+#define PROFLOG_CODECOV_ALIAS (PROFLOG_GC_EVENTS | PROFLOG_THREAD_EVENTS | PROFLOG_CALL_EVENTS | PROFLOG_INS_COVERAGE_EVENTS | PROFLOG_CODE_COV_FEATURE)
+#define PROFLOG_PERF_SAMPLING_ALIAS (PROFLOG_TYPELOADING_ALIAS | PROFLOG_THREAD_EVENTS | PROFLOG_SAMPLING_EVENTS | PROFLOG_SAMPLING_FEATURE)
+#define PROFLOG_GC_ALLOC_ALIAS (PROFLOG_TYPELOADING_ALIAS | PROFLOG_THREAD_EVENTS | PROFLOG_GC_EVENTS | PROFLOG_ALLOCATION_EVENTS)
+#define PROFLOG_HEAPSHOT_ALIAS (PROFLOG_TYPELOADING_ALIAS | PROFLOG_THREAD_EVENTS | PROFLOG_GC_EVENTS | PROFLOG_GC_ROOT_EVENTS | PROFLOG_HEAPSHOT_FEATURE)
+#define PROFLOG_LEGACY_ALIAS (PROFLOG_TYPELOADING_ALIAS | PROFLOG_GC_EVENTS | PROFLOG_THREAD_EVENTS | PROFLOG_JIT_COMPILATION_EVENTS | PROFLOG_EXCEPTION_EVENTS | PROFLOG_MONITOR_EVENTS | PROFLOG_GC_ROOT_EVENTS | PROFLOG_CONTEXT_EVENTS | PROFLOG_FINALIZATION_EVENTS | PROFLOG_COUNTER_EVENTS)
+
+
+typedef struct {
+       //Events explicitly enabled
+       int enable_mask;
+       //Events explicitly disabled
+       int disable_mask;
+
+       //Actual mask the profiler should use
+       int effective_mask;
+
+       //Emit a report at the end of execution
+       gboolean do_report;
+
+       //Enable profiler internal debugging
+       gboolean do_debug;
+
+       //Enable code coverage specific debugging
+       gboolean debug_coverage;
+
+       //Where to compress the output file
+       gboolean use_zip;
+
+       //If true, don't generate stacktraces
+       gboolean notraces;
+
+       //If true, emit coverage but don't emit enter/exit events - this happens cuz they share an event
+       gboolean only_coverage;
+
+       //If true, heapshots are generated on demand only
+       gboolean hs_mode_ondemand;
+
+       //HeapShort frequency in milliseconds
+       unsigned int hs_mode_ms;
+
+       //HeapShort frequency in number of collections
+       unsigned int hs_mode_gc;
+
+       //Sample frequency in Hertz
+       int sample_freq;
+
+       //Maximum number of frames to collect
+       int num_frames;
+
+       //Max depth to record enter/leave events
+       int max_call_depth;
+
+       //Name of the generated mlpd file
+       const char *output_filename;
+
+       //Filter files used by the code coverage mode
+       GPtrArray *cov_filter_files;
+
+       //Port to listen for profiling commands
+       int command_port;
+
+       //Max size of the sample hit buffer, we'll drop frames if it's reached
+       int max_allocated_sample_hits;
+
+       MonoProfileSamplingMode sampling_mode;
+} ProfilerConfig;
+
+void proflog_parse_args (ProfilerConfig *config, const char *desc);
+
+#endif /* __MONO_PROFLOG_H__ */
diff --git a/mono/profiler/mono-profiler-aot.c b/mono/profiler/mono-profiler-aot.c
deleted file mode 100644 (file)
index 384e624..0000000
+++ /dev/null
@@ -1,413 +0,0 @@
-/*
- * mono-profiler-aot.c: Ahead of Time Compiler Profiler for Mono.
- *
- *
- * Copyright 2008-2009 Novell, Inc (http://www.novell.com)
- *
- * This profiler collects profiling information usable by the Mono AOT compiler
- * to generate better code. It saves the information into files under ~/.mono. 
- * The AOT compiler can load these files during compilation.
- * Currently, only the order in which methods were compiled is saved, 
- * allowing more efficient function ordering in the AOT files.
- * Licensed under the MIT license. See LICENSE file in the project root for full license information.
- */
-
-#include <config.h>
-
-#include "mono-profiler-aot.h"
-
-#include <mono/metadata/profiler.h>
-#include <mono/metadata/tokentype.h>
-#include <mono/metadata/tabledefs.h>
-#include <mono/metadata/debug-helpers.h>
-#include <mono/metadata/assembly.h>
-#include <mono/metadata/class-internals.h>
-#include <mono/utils/mono-os-mutex.h>
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <glib.h>
-#include <sys/stat.h>
-
-#ifdef HOST_WIN32
-#include <direct.h>
-#endif
-
-struct _MonoProfiler {
-       GHashTable *classes;
-       GHashTable *images;
-       GPtrArray *methods;
-       FILE *outfile;
-       int id;
-       char *outfile_name;
-};
-
-static mono_mutex_t mutex;
-static gboolean verbose;
-
-static void
-prof_jit_enter (MonoProfiler *prof, MonoMethod *method)
-{
-}
-
-static void
-prof_jit_leave (MonoProfiler *prof, MonoMethod *method, int result)
-{
-       MonoImage *image = mono_class_get_image (mono_method_get_class (method));
-
-       if (!image->assembly || method->wrapper_type)
-               return;
-
-       mono_os_mutex_lock (&mutex);
-       g_ptr_array_add (prof->methods, method);
-       mono_os_mutex_unlock (&mutex);
-}
-
-static void
-prof_shutdown (MonoProfiler *prof);
-
-static void
-usage (int do_exit)
-{
-       printf ("AOT profiler.\n");
-       printf ("Usage: mono --profile=aot[:OPTION1[,OPTION2...]] program.exe\n");
-       printf ("Options:\n");
-       printf ("\thelp                 show this usage info\n");
-       printf ("\toutput=FILENAME      write the data to file FILENAME (required)\n");
-       printf ("\tverbose              print diagnostic info\n");
-       if (do_exit)
-               exit (1);
-}
-
-static const char*
-match_option (const char* p, const char *opt, char **rval)
-{
-       int len = strlen (opt);
-       if (strncmp (p, opt, len) == 0) {
-               if (rval) {
-                       if (p [len] == '=' && p [len + 1]) {
-                               const char *opt = p + len + 1;
-                               const char *end = strchr (opt, ',');
-                               char *val;
-                               int l;
-                               if (end == NULL) {
-                                       l = strlen (opt);
-                               } else {
-                                       l = end - opt;
-                               }
-                               val = (char *) g_malloc (l + 1);
-                               memcpy (val, opt, l);
-                               val [l] = 0;
-                               *rval = val;
-                               return opt + l;
-                       }
-                       if (p [len] == 0 || p [len] == ',') {
-                               *rval = NULL;
-                               return p + len + (p [len] == ',');
-                       }
-                       usage (1);
-               } else {
-                       if (p [len] == 0)
-                               return p + len;
-                       if (p [len] == ',')
-                               return p + len + 1;
-               }
-       }
-       return p;
-}
-
-void
-mono_profiler_startup (const char *desc);
-
-/**
- * mono_profiler_startup:
- * the entry point
- */
-void
-mono_profiler_startup (const char *desc)
-{
-       MonoProfiler *prof;
-       const char *p;
-       const char *opt;
-       char *outfile_name;
-
-       p = desc;
-       if (strncmp (p, "aot", 3))
-               usage (1);
-       p += 3;
-       if (*p == ':')
-               p++;
-       for (; *p; p = opt) {
-               char *val;
-               if (*p == ',') {
-                       opt = p + 1;
-                       continue;
-               }
-               if ((opt = match_option (p, "help", NULL)) != p) {
-                       usage (0);
-                       continue;
-               }
-               if ((opt = match_option (p, "verbose", NULL)) != p) {
-                       verbose = TRUE;
-                       continue;
-               }
-               if ((opt = match_option (p, "output", &val)) != p) {
-                       outfile_name = val;
-                       continue;
-               }
-               fprintf (stderr, "mono-profiler-aot: Unknown option: '%s'.\n", p);
-               exit (1);
-       }
-
-       if (!outfile_name) {
-               fprintf (stderr, "mono-profiler-aot: The 'output' argument is required.\n");
-               exit (1);
-       }
-
-       prof = g_new0 (MonoProfiler, 1);
-       prof->images = g_hash_table_new (NULL, NULL);
-       prof->classes = g_hash_table_new (NULL, NULL);
-       prof->methods = g_ptr_array_new ();
-       prof->outfile_name = outfile_name;
-
-       mono_os_mutex_init (&mutex);
-
-       mono_profiler_install (prof, prof_shutdown);
-
-       mono_profiler_install_jit_compile (prof_jit_enter, prof_jit_leave);
-
-       mono_profiler_set_events (MONO_PROFILE_JIT_COMPILATION);
-}
-
-static void
-emit_byte (MonoProfiler *prof, guint8 value)
-{
-       fwrite (&value, 1, 1, prof->outfile);
-}
-
-static void
-emit_int32 (MonoProfiler *prof, int value)
-{
-       // FIXME: Endianness
-       fwrite (&value, 4, 1, prof->outfile);
-}
-
-static void
-emit_string (MonoProfiler *prof, const char *str)
-{
-       int len = strlen (str);
-
-       emit_int32 (prof, len);
-       fwrite (str, len, 1, prof->outfile);
-}
-
-static void
-emit_record (MonoProfiler *prof, AotProfRecordType type, int id)
-{
-       emit_byte (prof, type);
-       emit_int32 (prof, id);
-}
-
-static int
-add_image (MonoProfiler *prof, MonoImage *image)
-{
-       int id = GPOINTER_TO_INT (g_hash_table_lookup (prof->images, image));
-       if (id)
-               return id - 1;
-
-       id = prof->id ++;
-       emit_record (prof, AOTPROF_RECORD_IMAGE, id);
-       emit_string (prof, image->assembly->aname.name);
-       emit_string (prof, image->guid);
-       g_hash_table_insert (prof->images, image, GINT_TO_POINTER (id + 1));
-       return id;
-}
-
-static int
-add_class (MonoProfiler *prof, MonoClass *klass);
-
-static int
-add_type (MonoProfiler *prof, MonoType *type)
-{
-       switch (type->type) {
-#if 0
-       case MONO_TYPE_SZARRAY: {
-               int eid = add_type (prof, &type->data.klass->byval_arg);
-               if (eid == -1)
-                       return -1;
-               int id = prof->id ++;
-               emit_record (prof, AOTPROF_RECORD_TYPE, id);
-               emit_byte (prof, MONO_TYPE_SZARRAY);
-               emit_int32 (prof, id);
-               return id;
-       }
-#endif
-       case MONO_TYPE_BOOLEAN:
-       case MONO_TYPE_CHAR:
-       case MONO_TYPE_I1:
-       case MONO_TYPE_U1:
-       case MONO_TYPE_I2:
-       case MONO_TYPE_U2:
-       case MONO_TYPE_I4:
-       case MONO_TYPE_U4:
-       case MONO_TYPE_I8:
-       case MONO_TYPE_U8:
-       case MONO_TYPE_R4:
-       case MONO_TYPE_R8:
-       case MONO_TYPE_I:
-       case MONO_TYPE_U:
-       case MONO_TYPE_OBJECT:
-       case MONO_TYPE_STRING:
-       case MONO_TYPE_CLASS:
-       case MONO_TYPE_VALUETYPE:
-       case MONO_TYPE_GENERICINST:
-               return add_class (prof, mono_class_from_mono_type (type));
-       default:
-               return -1;
-       }
-}
-
-static int
-add_ginst (MonoProfiler *prof, MonoGenericInst *inst)
-{
-       int i, id;
-       int *ids;
-
-       // FIXME: Cache
-       ids = g_malloc0 (inst->type_argc * sizeof (int));
-       for (i = 0; i < inst->type_argc; ++i) {
-               MonoType *t = inst->type_argv [i];
-               ids [i] = add_type (prof, t);
-               if (ids [i] == -1) {
-                       g_free (ids);
-                       return -1;
-               }
-       }
-       id = prof->id ++;
-       emit_record (prof, AOTPROF_RECORD_GINST, id);
-       emit_int32 (prof, inst->type_argc);
-       for (i = 0; i < inst->type_argc; ++i)
-               emit_int32 (prof, ids [i]);
-       g_free (ids);
-
-       return id;
-}
-
-static int
-add_class (MonoProfiler *prof, MonoClass *klass)
-{
-       int id, inst_id = -1, image_id;
-       char *name;
-
-       id = GPOINTER_TO_INT (g_hash_table_lookup (prof->classes, klass));
-       if (id)
-               return id - 1;
-
-       image_id = add_image (prof, klass->image);
-
-       if (mono_class_is_ginst (klass)) {
-               MonoGenericContext *ctx = mono_class_get_context (klass);
-               inst_id = add_ginst (prof, ctx->class_inst);
-               if (inst_id == -1)
-                       return -1;
-       }
-
-       if (klass->nested_in)
-               name = g_strdup_printf ("%s.%s/%s", klass->nested_in->name_space, klass->nested_in->name, klass->name);
-       else
-               name = g_strdup_printf ("%s.%s", klass->name_space, klass->name);
-
-       id = prof->id ++;
-       emit_record (prof, AOTPROF_RECORD_TYPE, id);
-       emit_byte (prof, MONO_TYPE_CLASS);
-       emit_int32 (prof, image_id);
-       emit_int32 (prof, inst_id);
-       emit_string (prof, name);
-       g_free (name);
-       g_hash_table_insert (prof->classes, klass, GINT_TO_POINTER (id + 1));
-       return id;
-}
-
-static void
-add_method (MonoProfiler *prof, MonoMethod *m)
-{
-       MonoError error;
-       MonoMethodSignature *sig;
-       char *s;
-
-       sig = mono_method_signature_checked (m, &error);
-       g_assert (mono_error_ok (&error));
-
-       int class_id = add_class (prof, m->klass);
-       if (class_id == -1)
-               return;
-       int inst_id = -1;
-
-       if (m->is_inflated) {
-               MonoGenericContext *ctx = mono_method_get_context (m);
-               if (ctx->method_inst)
-                       inst_id = add_ginst (prof, ctx->method_inst);
-       }
-       int id = prof->id ++;
-       emit_record (prof, AOTPROF_RECORD_METHOD, id);
-       emit_int32 (prof, class_id);
-       emit_int32 (prof, inst_id);
-       emit_int32 (prof, sig->param_count);
-       emit_string (prof, m->name);
-       s = mono_signature_full_name (sig);
-       emit_string (prof, s);
-       g_free (s);
-       if (verbose)
-               printf ("%s %d\n", mono_method_full_name (m, 1), id);
-}
-
-/* called at the end of the program */
-static void
-prof_shutdown (MonoProfiler *prof)
-{
-       FILE *outfile;
-       int mindex;
-       char magic [32];
-
-       printf ("Creating output file: %s\n", prof->outfile_name);
-
-       if (prof->outfile_name [0] == '#') {
-               int fd = strtol (prof->outfile_name + 1, NULL, 10);
-               outfile = fdopen (fd, "a");
-       } else {
-               outfile = fopen (prof->outfile_name, "w+");
-       }
-       if (!outfile) {
-               fprintf (stderr, "Unable to create output file '%s': %s.\n", prof->outfile_name, strerror (errno));
-               return;
-       }
-       prof->outfile = outfile;
-
-       gint32 version = (AOT_PROFILER_MAJOR_VERSION << 16) | AOT_PROFILER_MINOR_VERSION;
-       sprintf (magic, AOT_PROFILER_MAGIC);
-       fwrite (magic, strlen (magic), 1, outfile);
-       emit_int32 (prof, version);
-
-       GHashTable *all_methods = g_hash_table_new (NULL, NULL);
-       for (mindex = 0; mindex < prof->methods->len; ++mindex) {
-           MonoMethod *m = (MonoMethod*)g_ptr_array_index (prof->methods, mindex);
-
-               if (!mono_method_get_token (m))
-                       continue;
-
-               if (g_hash_table_lookup (all_methods, m))
-                       continue;
-               g_hash_table_insert (all_methods, m, m);
-
-               add_method (prof, m);
-       }
-       emit_record (prof, AOTPROF_RECORD_NONE, 0);
-
-       fclose (outfile);
-
-       g_hash_table_destroy (all_methods);
-       g_hash_table_destroy (prof->classes);
-       g_hash_table_destroy (prof->images);
-       g_ptr_array_free (prof->methods, TRUE);
-       g_free (prof->outfile_name);
-}
diff --git a/mono/profiler/mono-profiler-aot.h b/mono/profiler/mono-profiler-aot.h
deleted file mode 100644 (file)
index 7663cab..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef __MONO_PROFILER_AOT_H__
-#define __MONO_PROFILER_AOT_H__
-
-#include <config.h>
-
-/*
- * File format:
- * - magic
- * - major/minor version as an int, i.e. 0x00010001
- * - sequence of records terminated by a record with type TYPE_NONE
- * Record format:
- * - 1 byte record type (AotProfRecordType)
- * - 1 int record id
- * - record specific data
- * Encoding rules:
- * - int - 4 bytes little endian
- * - string - int length followed by data
- */
-
-typedef enum {
-       AOTPROF_RECORD_NONE,
-       AOTPROF_RECORD_IMAGE,
-       AOTPROF_RECORD_TYPE,
-       AOTPROF_RECORD_GINST,
-       AOTPROF_RECORD_METHOD
-} AotProfRecordType;
-
-#define AOT_PROFILER_MAGIC "AOTPROFILE"
-
-#define AOT_PROFILER_MAJOR_VERSION 1
-#define AOT_PROFILER_MINOR_VERSION 0
-
-#endif /* __MONO_PROFILER_AOT_H__ */
diff --git a/mono/profiler/mono-profiler-iomap.c b/mono/profiler/mono-profiler-iomap.c
deleted file mode 100644 (file)
index 15baec0..0000000
+++ /dev/null
@@ -1,549 +0,0 @@
-/*
- * mono-profiler-iomap.c: IOMAP string profiler for Mono.
- *
- * Authors:
- *   Marek Habersack <mhabersack@novell.com>
- *
- * Copyright (c) 2009 Novell, Inc (http://novell.com)
- *
- * Note: this profiler is completely unsafe wrt handling managed objects,
- * don't use and don't copy code from here.
- * Licensed under the MIT license. See LICENSE file in the project root for full license information.
- */
-#include "config.h"
-
-#include <string.h>
-#include <mono/utils/mono-io-portability.h>
-#include <mono/metadata/metadata.h>
-#include <mono/metadata/metadata-internals.h>
-#include <mono/metadata/class.h>
-#include <mono/metadata/class-internals.h>
-#include <mono/metadata/object-internals.h>
-#include <mono/metadata/image.h>
-#include <mono/metadata/mono-debug.h>
-#include <mono/metadata/debug-helpers.h>
-#include <mono/metadata/threads.h>
-#include <mono/metadata/profiler.h>
-#include <mono/metadata/loader.h>
-#include <mono/utils/mono-os-mutex.h>
-
-#define LOCATION_INDENT "        "
-#define BACKTRACE_SIZE 64
-
-typedef struct _MonoStackBacktraceInfo 
-{
-       MonoMethod *method;
-       gint native_offset;
-} MonoStackBacktraceInfo;
-
-typedef struct 
-{
-       guint32 count;
-       gchar *requestedName;
-       gchar *actualName;
-} MismatchedFilesStats;
-
-typedef struct _SavedString
-{
-       MonoString *string;
-       MonoDomain *domain;
-       void *stack [BACKTRACE_SIZE];
-       gint stack_entries;
-       struct _SavedString *next;
-} SavedString;
-
-typedef struct _SavedStringFindInfo
-{
-       guint32 hash;
-       size_t len;
-} SavedStringFindInfo;
-
-typedef struct _StringLocation
-{
-       gchar *hint;
-       struct _StringLocation *next;
-} StringLocation;
-
-struct _MonoProfiler
-{
-       GHashTable *mismatched_files_hash;
-       GHashTable *saved_strings_hash;
-       GHashTable *string_locations_hash;
-       gboolean may_have_locations;
-};
-
-typedef struct _StackWalkData
-{
-       MonoProfiler *prof;
-       void **stack;
-       int stack_size;
-       int frame_count;
-} StackWalkData;
-
-static mono_mutex_t mismatched_files_section;
-static gboolean runtime_initialized = FALSE;
-
-static inline void append_report (GString **report, const gchar *format, ...);
-static inline void print_report (const gchar *format, ...);
-static inline guint32 do_calc_string_hash (guint32 hash, const gchar *str);
-static inline guint32 calc_strings_hash (const gchar *str1, const gchar *str2, guint32 *str1hash);
-static void print_mismatched_stats (MonoProfiler *prof);
-static inline gchar *build_hint (SavedString *head);
-static inline gchar *build_hint_from_stack (MonoDomain *domain, void **stack, gint stack_entries);
-static inline void store_string_location (MonoProfiler *prof, const gchar *string, guint32 hash, size_t len);
-static void mono_portability_remember_string (MonoProfiler *prof, MonoDomain *domain, MonoString *str);
-void mono_profiler_startup (const char *desc);
-
-static void mismatched_stats_foreach_func (gpointer key, gpointer value, gpointer user_data)
-{
-       MismatchedFilesStats *stats = (MismatchedFilesStats*)value;
-       StringLocation *location;
-       MonoProfiler *prof = (MonoProfiler*)user_data;
-       guint32 hash;
-       gboolean bannerShown = FALSE;
-
-       hash = do_calc_string_hash (0, stats->requestedName);
-       fprintf (stdout,
-                "    Count: %u\n"
-                "Requested: %s\n"
-                "   Actual: %s\n",
-                stats->count, stats->requestedName, stats->actualName);
-
-       if (!prof->may_have_locations) {
-               fprintf (stdout, "\n");
-               return;
-       }
-
-       location = (StringLocation *)g_hash_table_lookup (prof->string_locations_hash, &hash);
-       while (location) {
-               if (location->hint && strlen (location->hint) > 0) {
-                       if (!bannerShown) {
-                               fprintf (stdout, "Locations:\n");
-                               bannerShown = TRUE;
-                       }
-                       fprintf (stdout, "%s", location->hint);
-               }
-               location = location->next;
-               if (location)
-                       fprintf (stdout, LOCATION_INDENT "--\n");
-       }
-
-       fprintf (stdout, "\n");
-}
-
-static void print_mismatched_stats (MonoProfiler *prof)
-{
-       if (!prof->mismatched_files_hash || g_hash_table_size (prof->mismatched_files_hash) == 0)
-               return;
-
-       prof->may_have_locations = g_hash_table_size (prof->string_locations_hash) > 0;
-
-       fprintf (stdout, "\n-=-=-=-=-=-=-= MONO_IOMAP Stats -=-=-=-=-=-=-=\n");
-       g_hash_table_foreach (prof->mismatched_files_hash, mismatched_stats_foreach_func, (gpointer)prof);
-       fflush (stdout);
-}
-
-static guint mismatched_files_guint32_hash (gconstpointer key)
-{
-       if (!key)
-               return 0;
-
-       return *((guint32*)key);
-}
-
-static gboolean mismatched_files_guint32_equal (gconstpointer key1, gconstpointer key2)
-{
-       if (!key1 || !key2)
-               return FALSE;
-
-       return (gboolean)(*((guint32*)key1) == *((guint32*)key2));
-}
-
-static inline guint32 do_calc_string_hash (guint32 hash, const gchar *str)
-{
-       guint32 ret = hash;
-       gchar *cc = (gchar*)str;
-       gchar *end = (gchar*)(str + strlen (str) - 1);
-
-       for (; cc < end; cc += 2) {
-               ret = (ret << 5) - ret + *cc;
-               ret = (ret << 5) - ret + cc [1];
-       }
-       end++;
-       if (cc < end)
-               ret = (ret << 5) - ret + *cc;
-
-       return ret;
-}
-
-static inline guint32 calc_strings_hash (const gchar *str1, const gchar *str2, guint32 *str1hash)
-{
-       guint32 hash = do_calc_string_hash (0, str1);
-       if (str1hash)
-               *str1hash = hash;
-       return do_calc_string_hash (hash, str2);
-}
-
-static inline void print_report (const gchar *format, ...)
-{
-       MonoError error;
-       MonoClass *klass;
-       MonoProperty *prop;
-       MonoString *str;
-       char *stack_trace;
-       va_list ap;
-
-       fprintf (stdout, "-=-=-=-=-=-=- MONO_IOMAP REPORT -=-=-=-=-=-=-\n");
-       va_start (ap, format);
-       vfprintf (stdout, format, ap);
-       fprintf (stdout, "\n");
-       va_end (ap);
-       klass = mono_class_load_from_name (mono_get_corlib (), "System", "Environment");
-       mono_class_init (klass);
-       prop = mono_class_get_property_from_name (klass, "StackTrace");
-       str = (MonoString*)mono_property_get_value_checked (prop, NULL, NULL, &error);
-       mono_error_assert_ok (&error);
-       stack_trace = mono_string_to_utf8_checked (str, &error);
-       mono_error_assert_ok (&error);
-
-       fprintf (stdout, "-= Stack Trace =-\n%s\n\n", stack_trace);
-       g_free (stack_trace);
-       fflush (stdout);
-}
-
-static inline void append_report (GString **report, const gchar *format, ...)
-{
-       va_list ap;
-       if (!*report)
-               *report = g_string_new ("");
-
-       va_start (ap, format);
-       g_string_append_vprintf (*report, format, ap);
-       va_end (ap);
-}
-
-static gboolean saved_strings_find_func (gpointer key, gpointer value, gpointer user_data)
-{
-       MonoError error;
-       SavedStringFindInfo *info = (SavedStringFindInfo*)user_data;
-       SavedString *saved = (SavedString*)value;
-       gchar *utf_str;
-       guint32 hash;
-
-       if (!info || !saved || mono_string_length (saved->string) != info->len)
-               return FALSE;
-
-       utf_str = mono_string_to_utf8_checked (saved->string, &error);
-       mono_error_assert_ok (&error);
-       hash = do_calc_string_hash (0, utf_str);
-       g_free (utf_str);
-
-       if (hash != info->hash)
-               return FALSE;
-
-       return TRUE;
-}
-
-static inline void store_string_location (MonoProfiler *prof, const gchar *string, guint32 hash, size_t len)
-{
-       StringLocation *location = (StringLocation *)g_hash_table_lookup (prof->string_locations_hash, &hash);
-       SavedString *saved;
-       SavedStringFindInfo info;
-       guint32 *hashptr;
-
-       if (location)
-               return;
-
-       info.hash = hash;
-       info.len = len;
-
-       /* Expensive but unavoidable... */
-       saved = (SavedString*)g_hash_table_find (prof->saved_strings_hash, saved_strings_find_func, &info);
-       hashptr = (guint32*)g_malloc (sizeof (guint32));
-       *hashptr = hash;
-       location = (StringLocation*)g_malloc0 (sizeof (location));
-
-       g_hash_table_insert (prof->string_locations_hash, hashptr, location);
-       if (!saved)
-               return;
-
-       g_hash_table_remove (prof->saved_strings_hash, saved->string);
-       location->hint = build_hint (saved);
-}
-
-static gboolean ignore_frame (MonoMethod *method)
-{
-       MonoClass *klass = method->klass;
-
-       if (method->wrapper_type != MONO_WRAPPER_NONE)
-               return TRUE;
-
-       /* Now ignore the assemblies we know shouldn't contain mixed-case names (only the most frequent cases) */
-       if (klass->image ) {
-               if (strcmp (klass->image->assembly_name, "mscorlib") == 0)
-                       return TRUE;
-               else if (strcmp (klass->image->assembly_name, "System") == 0)
-                       return TRUE;
-               else if (strncmp (klass->image->assembly_name, "Mono.", 5) == 0)
-                       return TRUE;
-               else if (strncmp (klass->image->assembly_name, "System.", 7) == 0)
-                       return TRUE;
-               else if (strcmp (klass->image->assembly_name, "PEAPI") == 0)
-                       return TRUE;
-       }
-
-       return FALSE;
-}
-
-static inline gchar *build_hint_from_stack (MonoDomain *domain, void **stack, gint stack_entries)
-{
-       gchar *hint;
-       MonoMethod *method, *selectedMethod;
-       MonoAssembly *assembly;
-       MonoImage *image;
-       MonoDebugSourceLocation *location;
-       MonoStackBacktraceInfo *info;
-       gboolean use_full_trace;
-       char *methodName;
-       gint i, native_offset, firstAvailable;
-
-       selectedMethod = NULL;
-       firstAvailable = -1;
-       use_full_trace = FALSE;
-       native_offset = -1;
-       for (i = 0; i < stack_entries; i++) {
-               info = (MonoStackBacktraceInfo*) stack [i];
-               method = info ? info->method : NULL;
-
-               if (!method || method->wrapper_type != MONO_WRAPPER_NONE)
-                       continue;
-
-               if (firstAvailable == -1)
-                       firstAvailable = i;
-
-               image = method->klass->image;
-               assembly = image->assembly;
-
-               if ((assembly && assembly->in_gac) || ignore_frame (method))
-                       continue;
-               selectedMethod = method;
-               native_offset = info->native_offset;
-               break;
-       }
-
-       if (!selectedMethod) {
-               /* All the frames were from assemblies installed in GAC. Find first frame that is
-                * not in the ignore list */
-               for (i = 0; i < stack_entries; i++) {
-                       info = (MonoStackBacktraceInfo*) stack [i];
-                       method = info ? info->method : NULL;
-
-                       if (!method || ignore_frame (method))
-                               continue;
-                       selectedMethod = method;
-                       native_offset = info->native_offset;
-                       break;
-               }
-
-               if (!selectedMethod)
-                       use_full_trace = TRUE;
-       }
-
-       hint = NULL;
-       if (use_full_trace) {
-               GString *trace = g_string_new ("Full trace:\n");
-               for (i = firstAvailable; i < stack_entries; i++) {
-                       info = (MonoStackBacktraceInfo*) stack [i];
-                       method = info ? info->method : NULL;
-                       if (!method || method->wrapper_type != MONO_WRAPPER_NONE)
-                               continue;
-
-                       location = mono_debug_lookup_source_location (method, info->native_offset, domain);
-                       methodName = mono_method_full_name (method, TRUE);
-
-                       if (location) {
-                               append_report (&trace, LOCATION_INDENT "%s in %s:%u\n", methodName, location->source_file, location->row);
-                               mono_debug_free_source_location (location);
-                       } else
-                               append_report (&trace, LOCATION_INDENT "%s\n", methodName);
-                       g_free (methodName);
-               }
-
-               if (trace) {
-                       if (trace->len)
-                               hint = g_string_free (trace, FALSE);
-                       else
-                               g_string_free (trace, TRUE);
-               }
-       } else {
-               location = mono_debug_lookup_source_location (selectedMethod, native_offset, domain);
-               methodName = mono_method_full_name (selectedMethod, TRUE);
-
-               if (location) {
-                       hint = g_strdup_printf (LOCATION_INDENT "%s in %s:%u\n", methodName, location->source_file, location->row);
-                       mono_debug_free_source_location (location);
-               } else
-                       hint = g_strdup_printf (LOCATION_INDENT "%s\n", methodName);
-               g_free (methodName);
-       }
-
-       return hint;
-}
-
-static inline gchar *build_hint (SavedString *head)
-{
-       SavedString *current;
-       gchar *tmp;
-       GString *hint = NULL;
-
-       current = head;
-       while (current) {
-               tmp = build_hint_from_stack (current->domain, current->stack, current->stack_entries);
-               current = current->next;
-               if (!tmp)
-                       continue;
-
-               append_report (&hint, tmp);
-       }
-
-       if (hint) {
-               if (hint->len)
-                       return g_string_free (hint, FALSE);
-               else {
-                       g_string_free (hint, FALSE);
-                       return NULL;
-               }
-       }
-
-       return NULL;
-}
-
-static gboolean stack_walk_func (MonoMethod *method, gint32 native_offset, gint32 il_offset, gboolean managed, gpointer data)
-{
-       StackWalkData *swdata = (StackWalkData*)data;
-       MonoStackBacktraceInfo *info;
-
-       if (swdata->frame_count >= swdata->stack_size)
-               return TRUE;
-
-       info = (MonoStackBacktraceInfo*)g_malloc (sizeof (*info));
-       info->method = method;
-       info->native_offset = native_offset;
-
-       swdata->stack [swdata->frame_count++] = info;
-       return FALSE;
-}
-
-static inline int mono_stack_backtrace (MonoProfiler *prof, MonoDomain *domain, void **stack, int size)
-{
-       StackWalkData data;
-
-       data.prof = prof;
-       data.stack = stack;
-       data.stack_size = size;
-       data.frame_count = 0;
-
-       mono_stack_walk_no_il (stack_walk_func, (gpointer)&data);
-
-       return data.frame_count;
-}
-
-static void mono_portability_remember_string (MonoProfiler *prof, MonoDomain *domain, MonoString *str)
-{
-       SavedString *head, *entry;
-
-       if (!str || !domain || !runtime_initialized)
-               return;
-
-       entry = (SavedString*)g_malloc0 (sizeof (SavedString));
-       entry->string = str;
-       entry->domain = domain;
-       entry->stack_entries = mono_stack_backtrace (prof, domain, entry->stack, BACKTRACE_SIZE);
-       if (entry->stack_entries == 0) {
-               g_free (entry);
-               return;
-       }
-
-       mono_os_mutex_lock (&mismatched_files_section);
-       head = (SavedString*)g_hash_table_lookup (prof->saved_strings_hash, (gpointer)str);
-       if (head) {
-               while (head->next)
-                       head = head->next;
-               head->next = entry;
-       } else
-               g_hash_table_insert (prof->saved_strings_hash, (gpointer)str, (gpointer)entry);
-       mono_os_mutex_unlock (&mismatched_files_section);
-}
-
-static MonoClass *string_class = NULL;
-
-static void mono_portability_remember_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
-{
-       if (klass != string_class)
-               return;
-       mono_portability_remember_string (prof, mono_object_get_domain (obj), (MonoString*)obj);
-}
-
-static void mono_portability_iomap_event (MonoProfiler *prof, const char *report, const char *pathname, const char *new_pathname)
-{
-       guint32 hash, pathnameHash;
-       MismatchedFilesStats *stats;
-
-       if (!runtime_initialized)
-               return;
-
-       mono_os_mutex_lock (&mismatched_files_section);
-       hash = calc_strings_hash (pathname, new_pathname, &pathnameHash);
-       stats = (MismatchedFilesStats*)g_hash_table_lookup (prof->mismatched_files_hash, &hash);
-       if (stats == NULL) {
-               guint32 *hashptr;
-
-               stats = (MismatchedFilesStats*) g_malloc (sizeof (MismatchedFilesStats));
-               stats->count = 1;
-               stats->requestedName = g_strdup (pathname);
-               stats->actualName = g_strdup (new_pathname);
-               hashptr = (guint32*)g_malloc (sizeof (guint32));
-               if (hashptr) {
-                       *hashptr = hash;
-                       g_hash_table_insert (prof->mismatched_files_hash, (gpointer)hashptr, stats);
-               } else
-                       g_error ("Out of memory allocating integer pointer for mismatched files hash table.");
-
-               store_string_location (prof, (const gchar*)stats->requestedName, pathnameHash, strlen (stats->requestedName));
-               mono_os_mutex_unlock (&mismatched_files_section);
-
-               print_report ("%s -     Found file path: '%s'\n", report, new_pathname);
-       } else {
-               mono_os_mutex_unlock (&mismatched_files_section);
-               stats->count++;
-       }
-}
-
-static void runtime_initialized_cb (MonoProfiler *prof)
-{
-       runtime_initialized = TRUE;
-       string_class = mono_get_string_class ();
-}
-
-static void profiler_shutdown (MonoProfiler *prof)
-{
-       print_mismatched_stats (prof);
-       mono_os_mutex_destroy (&mismatched_files_section);
-}
-
-void mono_profiler_startup (const char *desc)
-{
-       MonoProfiler *prof = g_new0 (MonoProfiler, 1);
-
-       mono_os_mutex_init (&mismatched_files_section);
-       prof->mismatched_files_hash = g_hash_table_new (mismatched_files_guint32_hash, mismatched_files_guint32_equal);
-       prof->saved_strings_hash = g_hash_table_new (NULL, NULL);
-       prof->string_locations_hash = g_hash_table_new (mismatched_files_guint32_hash, mismatched_files_guint32_equal);
-
-       mono_profiler_install (prof, profiler_shutdown);
-       mono_profiler_install_runtime_initialized (runtime_initialized_cb);
-       mono_profiler_install_iomap (mono_portability_iomap_event);
-       mono_profiler_install_allocation (mono_portability_remember_alloc);
-
-       mono_profiler_set_events ((MonoProfileFlags)(MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_IOMAP_EVENTS));
-}
diff --git a/mono/profiler/mono-profiler-log.c b/mono/profiler/mono-profiler-log.c
deleted file mode 100644 (file)
index 0e509a4..0000000
+++ /dev/null
@@ -1,4735 +0,0 @@
-/*
- * mono-profiler-log.c: mono log profiler
- *
- * Authors:
- *   Paolo Molaro (lupus@ximian.com)
- *   Alex Rønne Petersen (alexrp@xamarin.com)
- *
- * Copyright 2010 Novell, Inc (http://www.novell.com)
- * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
- * Licensed under the MIT license. See LICENSE file in the project root for full license information.
- */
-
-#include <config.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/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-os-mutex.h>
-#include <mono/utils/mono-os-semaphore.h>
-#include <mono/utils/mono-threads.h>
-#include <mono/utils/mono-threads-api.h>
-#include "mono-profiler-log.h"
-
-#ifdef HAVE_DLFCN_H
-#include <dlfcn.h>
-#endif
-#include <fcntl.h>
-#ifdef HAVE_LINK_H
-#include <link.h>
-#endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#if defined(__APPLE__)
-#include <mach/mach_time.h>
-#endif
-#include <netinet/in.h>
-#ifdef HAVE_SYS_MMAN_H
-#include <sys/mman.h>
-#endif
-#include <sys/socket.h>
-#if defined (HAVE_SYS_ZLIB)
-#include <zlib.h>
-#endif
-
-#define BUFFER_SIZE (4096 * 16)
-
-/* Worst-case size in bytes of a 64-bit value encoded with LEB128. */
-#define LEB128_SIZE 10
-
-/* Size of a value encoded as a single byte. */
-#undef BYTE_SIZE // mach/i386/vm_param.h on OS X defines this to 8, but it isn't used for anything.
-#define BYTE_SIZE 1
-
-/* Size in bytes of the event prefix (ID + time). */
-#define EVENT_SIZE (BYTE_SIZE + LEB128_SIZE)
-
-static volatile gint32 runtime_inited;
-static volatile gint32 in_shutdown;
-
-static int nocalls = 0;
-static int notraces = 0;
-static int use_zip = 0;
-static int do_report = 0;
-static int do_heap_shot = 0;
-static int max_call_depth = 0;
-static int command_port = 0;
-static int heapshot_requested = 0;
-static int do_mono_sample = 0;
-static int do_debug = 0;
-static int do_coverage = 0;
-static gboolean no_counters = FALSE;
-static gboolean only_coverage = FALSE;
-static gboolean debug_coverage = FALSE;
-static int max_allocated_sample_hits;
-
-// Statistics for internal profiler data structures.
-static gint32 sample_allocations_ctr,
-              buffer_allocations_ctr;
-
-// Statistics for profiler events.
-static gint32 sync_points_ctr,
-              heap_objects_ctr,
-              heap_starts_ctr,
-              heap_ends_ctr,
-              heap_roots_ctr,
-              gc_events_ctr,
-              gc_resizes_ctr,
-              gc_allocs_ctr,
-              gc_moves_ctr,
-              gc_handle_creations_ctr,
-              gc_handle_deletions_ctr,
-              finalize_begins_ctr,
-              finalize_ends_ctr,
-              finalize_object_begins_ctr,
-              finalize_object_ends_ctr,
-              image_loads_ctr,
-              image_unloads_ctr,
-              assembly_loads_ctr,
-              assembly_unloads_ctr,
-              class_loads_ctr,
-              class_unloads_ctr,
-              method_entries_ctr,
-              method_exits_ctr,
-              method_exception_exits_ctr,
-              method_jits_ctr,
-              code_buffers_ctr,
-              exception_throws_ctr,
-              exception_clauses_ctr,
-              monitor_contentions_ctr,
-              monitor_acquisitions_ctr,
-              monitor_failures_ctr,
-              thread_starts_ctr,
-              thread_ends_ctr,
-              thread_names_ctr,
-              domain_loads_ctr,
-              domain_unloads_ctr,
-              domain_names_ctr,
-              context_loads_ctr,
-              context_unloads_ctr,
-              sample_ubins_ctr,
-              sample_usyms_ctr,
-              sample_hits_ctr,
-              counter_descriptors_ctr,
-              counter_samples_ctr,
-              perfcounter_descriptors_ctr,
-              perfcounter_samples_ctr,
-              coverage_methods_ctr,
-              coverage_statements_ctr,
-              coverage_classes_ctr,
-              coverage_assemblies_ctr;
-
-static MonoLinkedListSet profiler_thread_list;
-
-/*
- * file format:
- * [header] [buffer]*
- *
- * The file is composed by a header followed by 0 or more buffers.
- * Each buffer contains events that happened on a thread: for a given thread
- * buffers that appear later in the file are guaranteed to contain events
- * that happened later in time. Buffers from separate threads could be interleaved,
- * though.
- * Buffers are not required to be aligned.
- *
- * header format:
- * [id: 4 bytes] constant value: LOG_HEADER_ID
- * [major: 1 byte] [minor: 1 byte] major and minor version of the log profiler
- * [format: 1 byte] version of the data format for the rest of the file
- * [ptrsize: 1 byte] size in bytes of a pointer in the profiled program
- * [startup time: 8 bytes] time in milliseconds since the unix epoch when the program started
- * [timer overhead: 4 bytes] approximate overhead in nanoseconds of the timer
- * [flags: 4 bytes] file format flags, should be 0 for now
- * [pid: 4 bytes] pid of the profiled process
- * [port: 2 bytes] tcp port for server if != 0
- * [args size: 4 bytes] size of args
- * [args: string] arguments passed to the profiler
- * [arch size: 4 bytes] size of arch
- * [arch: string] architecture the profiler is running on
- * [os size: 4 bytes] size of os
- * [os: string] operating system the profiler is running on
- *
- * The multiple byte integers are in little-endian format.
- *
- * buffer format:
- * [buffer header] [event]*
- * Buffers have a fixed-size header followed by 0 or more bytes of event data.
- * Timing information and other values in the event data are usually stored
- * as uleb128 or sleb128 integers. To save space, as noted for each item below,
- * some data is represented as a difference between the actual value and
- * either the last value of the same type (like for timing information) or
- * as the difference from a value stored in a buffer header.
- *
- * For timing information the data is stored as uleb128, since timing
- * increases in a monotonic way in each thread: the value is the number of
- * nanoseconds to add to the last seen timing data in a buffer. The first value
- * in a buffer will be calculated from the time_base field in the buffer head.
- *
- * Object or heap sizes are stored as uleb128.
- * Pointer differences are stored as sleb128, instead.
- *
- * If an unexpected value is found, the rest of the buffer should be ignored,
- * as generally the later values need the former to be interpreted correctly.
- *
- * buffer header format:
- * [bufid: 4 bytes] constant value: BUF_ID
- * [len: 4 bytes] size of the data following the buffer header
- * [time_base: 8 bytes] time base in nanoseconds since an unspecified epoch
- * [ptr_base: 8 bytes] base value for pointers
- * [obj_base: 8 bytes] base value for object addresses
- * [thread id: 8 bytes] system-specific thread ID (pthread_t for example)
- * [method_base: 8 bytes] base value for MonoMethod pointers
- *
- * event format:
- * [extended info: upper 4 bits] [type: lower 4 bits]
- * [time diff: uleb128] nanoseconds since last timing
- * [data]*
- * The data that follows depends on type and the extended info.
- * Type is one of the enum values in mono-profiler-log.h: TYPE_ALLOC, TYPE_GC,
- * TYPE_METADATA, TYPE_METHOD, TYPE_EXCEPTION, TYPE_MONITOR, TYPE_HEAP.
- * The extended info bits are interpreted based on type, see
- * each individual event description below.
- * strings are represented as a 0-terminated utf8 sequence.
- *
- * backtrace format:
- * [num: uleb128] number of frames following
- * [frame: sleb128]* mum MonoMethod* as a pointer difference from the last such
- * pointer or the buffer method_base
- *
- * type alloc format:
- * type: TYPE_ALLOC
- * exinfo: flags: TYPE_ALLOC_BT
- * [ptr: sleb128] class as a byte difference from ptr_base
- * [obj: sleb128] object address as a byte difference from obj_base
- * [size: uleb128] size of the object in the heap
- * If the TYPE_ALLOC_BT flag is set, a backtrace follows.
- *
- * type GC format:
- * type: TYPE_GC
- * exinfo: one of TYPE_GC_EVENT, TYPE_GC_RESIZE, TYPE_GC_MOVE, TYPE_GC_HANDLE_CREATED[_BT],
- * TYPE_GC_HANDLE_DESTROYED[_BT], TYPE_GC_FINALIZE_START, TYPE_GC_FINALIZE_END,
- * TYPE_GC_FINALIZE_OBJECT_START, TYPE_GC_FINALIZE_OBJECT_END
- * if exinfo == TYPE_GC_RESIZE
- *     [heap_size: uleb128] new heap size
- * if exinfo == TYPE_GC_EVENT
- *     [event type: byte] GC event (MONO_GC_EVENT_* from profiler.h)
- *     [generation: byte] GC generation event refers to
- * if exinfo == TYPE_GC_MOVE
- *     [num_objects: uleb128] number of object moves that follow
- *     [objaddr: sleb128]+ num_objects object pointer differences from obj_base
- *     num is always an even number: the even items are the old
- *     addresses, the odd numbers are the respective new object addresses
- * if exinfo == TYPE_GC_HANDLE_CREATED[_BT]
- *     [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
- *     upper bits reserved as flags
- *     [handle: uleb128] GC handle value
- *     [objaddr: sleb128] object pointer differences from obj_base
- *     If exinfo == TYPE_GC_HANDLE_CREATED_BT, a backtrace follows.
- * if exinfo == TYPE_GC_HANDLE_DESTROYED[_BT]
- *     [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
- *     upper bits reserved as flags
- *     [handle: uleb128] GC handle value
- *     If exinfo == TYPE_GC_HANDLE_DESTROYED_BT, a backtrace follows.
- * if exinfo == TYPE_GC_FINALIZE_OBJECT_{START,END}
- *     [object: sleb128] the object as a difference from obj_base
- *
- * type metadata format:
- * type: TYPE_METADATA
- * exinfo: one of: TYPE_END_LOAD, TYPE_END_UNLOAD (optional for TYPE_THREAD and TYPE_DOMAIN)
- * [mtype: byte] metadata type, one of: TYPE_CLASS, TYPE_IMAGE, TYPE_ASSEMBLY, TYPE_DOMAIN,
- * TYPE_THREAD, TYPE_CONTEXT
- * [pointer: sleb128] pointer of the metadata type depending on mtype
- * if mtype == TYPE_CLASS
- *     [image: sleb128] MonoImage* as a pointer difference from ptr_base
- *     [name: string] full class name
- * if mtype == TYPE_IMAGE
- *     [name: string] image file name
- * if mtype == TYPE_ASSEMBLY
- *     [name: string] assembly name
- * if mtype == TYPE_DOMAIN && exinfo == 0
- *     [name: string] domain friendly name
- * if mtype == TYPE_CONTEXT
- *     [domain: sleb128] domain id as pointer
- * if mtype == TYPE_THREAD && exinfo == 0
- *     [name: string] thread name
- *
- * type method format:
- * type: TYPE_METHOD
- * exinfo: one of: TYPE_LEAVE, TYPE_ENTER, TYPE_EXC_LEAVE, TYPE_JIT
- * [method: sleb128] MonoMethod* as a pointer difference from the last such
- * pointer or the buffer method_base
- * if exinfo == TYPE_JIT
- *     [code address: sleb128] pointer to the native code as a diff from ptr_base
- *     [code size: uleb128] size of the generated code
- *     [name: string] full method name
- *
- * type exception format:
- * type: TYPE_EXCEPTION
- * exinfo: TYPE_THROW_BT flag or one of: TYPE_CLAUSE
- * if exinfo == TYPE_CLAUSE
- *     [clause type: byte] MonoExceptionEnum enum value
- *     [clause index: uleb128] index of the current clause
- *     [method: sleb128] MonoMethod* as a pointer difference from the last such
- *     pointer or the buffer method_base
- * else
- *     [object: sleb128] the exception object as a difference from obj_base
- *     if exinfo has TYPE_THROW_BT set, a backtrace follows.
- *
- * type runtime format:
- * type: TYPE_RUNTIME
- * exinfo: one of: TYPE_JITHELPER
- * if exinfo == TYPE_JITHELPER
- *     [type: byte] MonoProfilerCodeBufferType enum value
- *     [buffer address: sleb128] pointer to the native code as a diff from ptr_base
- *     [buffer size: uleb128] size of the generated code
- *     if type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
- *             [name: string] buffer description name
- *
- * type monitor format:
- * type: TYPE_MONITOR
- * exinfo: TYPE_MONITOR_BT flag and one of: MONO_PROFILER_MONITOR_(CONTENTION|FAIL|DONE)
- * [object: sleb128] the lock object as a difference from obj_base
- * if exinfo.low3bits == MONO_PROFILER_MONITOR_CONTENTION
- *     If the TYPE_MONITOR_BT flag is set, a backtrace follows.
- *
- * type heap format
- * type: TYPE_HEAP
- * exinfo: one of TYPE_HEAP_START, TYPE_HEAP_END, TYPE_HEAP_OBJECT, TYPE_HEAP_ROOT
- * if exinfo == TYPE_HEAP_OBJECT
- *     [object: sleb128] the object as a difference from obj_base
- *     [class: sleb128] the object MonoClass* as a difference from ptr_base
- *     [size: uleb128] size of the object on the heap
- *     [num_refs: uleb128] number of object references
- *     each referenced objref is preceded by a uleb128 encoded offset: the
- *     first offset is from the object address and each next offset is relative
- *     to the previous one
- *     [objrefs: sleb128]+ object referenced as a difference from obj_base
- *     The same object can appear multiple times, but only the first time
- *     with size != 0: in the other cases this data will only be used to
- *     provide additional referenced objects.
- * if exinfo == TYPE_HEAP_ROOT
- *     [num_roots: uleb128] number of root references
- *     [num_gc: uleb128] number of major gcs
- *     [object: sleb128] the object as a difference from obj_base
- *     [root_type: byte] the root_type: MonoProfileGCRootType (profiler.h)
- *     [extra_info: uleb128] the extra_info value
- *     object, root_type and extra_info are repeated num_roots times
- *
- * type sample format
- * type: TYPE_SAMPLE
- * exinfo: one of TYPE_SAMPLE_HIT, TYPE_SAMPLE_USYM, TYPE_SAMPLE_UBIN, TYPE_SAMPLE_COUNTERS_DESC, TYPE_SAMPLE_COUNTERS
- * if exinfo == TYPE_SAMPLE_HIT
- *     [thread: sleb128] thread id as difference from ptr_base
- *     [count: uleb128] number of following instruction addresses
- *     [ip: sleb128]* instruction pointer as difference from ptr_base
- *     [mbt_count: uleb128] number of managed backtrace frames
- *     [method: sleb128]* MonoMethod* as a pointer difference from the last such
- *     pointer or the buffer method_base (the first such method can be also indentified by ip, but this is not neccessarily true)
- * if exinfo == TYPE_SAMPLE_USYM
- *     [address: sleb128] symbol address as a difference from ptr_base
- *     [size: uleb128] symbol size (may be 0 if unknown)
- *     [name: string] symbol name
- * if exinfo == TYPE_SAMPLE_UBIN
- *     [address: sleb128] address where binary has been loaded
- *     [offset: uleb128] file offset of mapping (the same file can be mapped multiple times)
- *     [size: uleb128] memory size
- *     [name: string] binary name
- * if exinfo == TYPE_SAMPLE_COUNTERS_DESC
- *     [len: uleb128] number of counters
- *     for i = 0 to len
- *             [section: uleb128] section of counter
- *             if section == MONO_COUNTER_PERFCOUNTERS:
- *                     [section_name: string] section name of counter
- *             [name: string] name of counter
- *             [type: byte] type of counter
- *             [unit: byte] unit of counter
- *             [variance: byte] variance of counter
- *             [index: uleb128] unique index of counter
- * if exinfo == TYPE_SAMPLE_COUNTERS
- *     while true:
- *             [index: uleb128] unique index of counter
- *             if index == 0:
- *                     break
- *             [type: byte] type of counter value
- *             if type == string:
- *                     if value == null:
- *                             [0: uleb128] 0 -> value is null
- *                     else:
- *                             [1: uleb128] 1 -> value is not null
- *                             [value: string] counter value
- *             else:
- *                     [value: uleb128/sleb128/double] counter value, can be sleb128, uleb128 or double (determined by using type)
- *
- * type coverage format
- * type: TYPE_COVERAGE
- * exinfo: one of TYPE_COVERAGE_METHOD, TYPE_COVERAGE_STATEMENT, TYPE_COVERAGE_ASSEMBLY, TYPE_COVERAGE_CLASS
- * if exinfo == TYPE_COVERAGE_METHOD
- *  [assembly: string] name of assembly
- *  [class: string] name of the class
- *  [name: string] name of the method
- *  [signature: string] the signature of the method
- *  [filename: string] the file path of the file that contains this method
- *  [token: uleb128] the method token
- *  [method_id: uleb128] an ID for this data to associate with the buffers of TYPE_COVERAGE_STATEMENTS
- *  [len: uleb128] the number of TYPE_COVERAGE_BUFFERS associated with this method
- * if exinfo == TYPE_COVERAGE_STATEMENTS
- *  [method_id: uleb128] an the TYPE_COVERAGE_METHOD buffer to associate this with
- *  [offset: uleb128] the il offset relative to the previous offset
- *  [counter: uleb128] the counter for this instruction
- *  [line: uleb128] the line of filename containing this instruction
- *  [column: uleb128] the column containing this instruction
- * if exinfo == TYPE_COVERAGE_ASSEMBLY
- *  [name: string] assembly name
- *  [guid: string] assembly GUID
- *  [filename: string] assembly filename
- *  [number_of_methods: uleb128] the number of methods in this assembly
- *  [fully_covered: uleb128] the number of fully covered methods
- *  [partially_covered: uleb128] the number of partially covered methods
- *    currently partially_covered will always be 0, and fully_covered is the
- *    number of methods that are fully and partially covered.
- * if exinfo == TYPE_COVERAGE_CLASS
- *  [name: string] assembly name
- *  [class: string] class name
- *  [number_of_methods: uleb128] the number of methods in this class
- *  [fully_covered: uleb128] the number of fully covered methods
- *  [partially_covered: uleb128] the number of partially covered methods
- *    currently partially_covered will always be 0, and fully_covered is the
- *    number of methods that are fully and partially covered.
- *
- * type meta format:
- * type: TYPE_META
- * exinfo: one of: TYPE_SYNC_POINT
- * if exinfo == TYPE_SYNC_POINT
- *     [type: byte] MonoProfilerSyncPointType enum value
- */
-
-// Pending data to be written to the log, for a single thread.
-// Threads periodically flush their own LogBuffers by calling safe_send
-typedef struct _LogBuffer LogBuffer;
-struct _LogBuffer {
-       // Next (older) LogBuffer in processing queue
-       LogBuffer *next;
-
-       uint64_t time_base;
-       uint64_t last_time;
-       uintptr_t ptr_base;
-       uintptr_t method_base;
-       uintptr_t last_method;
-       uintptr_t obj_base;
-       uintptr_t thread_id;
-
-       // Bytes allocated for this LogBuffer
-       int size;
-
-       // Start of currently unused space in buffer
-       unsigned char* cursor;
-
-       // Pointer to start-of-structure-plus-size (for convenience)
-       unsigned char* buf_end;
-
-       // Start of data in buffer. Contents follow "buffer format" described above.
-       unsigned char buf [1];
-};
-
-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;
-
-       // Methods referenced by events in `buffer`, see `MethodInfo`.
-       GPtrArray *methods;
-
-       // Current call depth for enter/leave events.
-       int call_depth;
-
-       // Indicates whether this thread is currently writing to its `buffer`.
-       gboolean busy;
-
-       // Has this thread written a thread end event to `buffer`?
-       gboolean ended;
-} MonoProfilerThread;
-
-static uintptr_t
-thread_id (void)
-{
-       return (uintptr_t) mono_native_thread_id_get ();
-}
-
-static uintptr_t
-process_id (void)
-{
-#ifdef HOST_WIN32
-       return (uintptr_t) GetCurrentProcessId ();
-#else
-       return (uintptr_t) getpid ();
-#endif
-}
-
-#ifdef __APPLE__
-static mach_timebase_info_data_t timebase_info;
-#elif defined (HOST_WIN32)
-static LARGE_INTEGER pcounter_freq;
-#endif
-
-#define TICKS_PER_SEC 1000000000LL
-
-static uint64_t
-current_time (void)
-{
-#ifdef __APPLE__
-       uint64_t time = mach_absolute_time ();
-
-       time *= timebase_info.numer;
-       time /= timebase_info.denom;
-
-       return time;
-#elif defined (HOST_WIN32)
-       LARGE_INTEGER value;
-
-       QueryPerformanceCounter (&value);
-
-       return value.QuadPart * TICKS_PER_SEC / pcounter_freq.QuadPart;
-#elif defined (CLOCK_MONOTONIC)
-       struct timespec tspec;
-
-       clock_gettime (CLOCK_MONOTONIC, &tspec);
-
-       return ((uint64_t) tspec.tv_sec * TICKS_PER_SEC + tspec.tv_nsec);
-#else
-       struct timeval tv;
-
-       gettimeofday (&tv, NULL);
-
-       return ((uint64_t) tv.tv_sec * TICKS_PER_SEC + tv.tv_usec * 1000);
-#endif
-}
-
-static int timer_overhead;
-
-static void
-init_time (void)
-{
-#ifdef __APPLE__
-       mach_timebase_info (&timebase_info);
-#elif defined (HOST_WIN32)
-       QueryPerformanceFrequency (&pcounter_freq);
-#endif
-
-       uint64_t time_start = current_time ();
-
-       for (int i = 0; i < 256; ++i)
-               current_time ();
-
-       uint64_t time_end = current_time ();
-
-       timer_overhead = (time_end - time_start) / 256;
-}
-
-/*
- * These macros should be used when writing an event to a log buffer. They take
- * care of a bunch of stuff that can be repetitive and error-prone, such as
- * acquiring/releasing the buffer lock, incrementing the event counter,
- * expanding the log buffer, processing requests, etc. They also create a scope
- * so that it's harder to leak the LogBuffer pointer, which can be problematic
- * as the pointer is unstable when the buffer lock isn't acquired.
- */
-
-#define ENTER_LOG(COUNTER, BUFFER, SIZE) \
-       do { \
-               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 (thread__, (SIZE))
-
-#define EXIT_LOG_EXPLICIT(SEND, REQUESTS) \
-               thread__->busy = FALSE; \
-               if ((SEND)) \
-                       send_log_unsafe (TRUE); \
-               if (thread__->attached) \
-                       buffer_unlock (); \
-               if ((REQUESTS)) \
-                       process_requests (); \
-       } while (0)
-
-// Pass these to EXIT_LOG_EXPLICIT () for easier reading.
-#define DO_SEND TRUE
-#define NO_SEND FALSE
-#define DO_REQUESTS TRUE
-#define NO_REQUESTS FALSE
-
-#define EXIT_LOG EXIT_LOG_EXPLICIT (DO_SEND, DO_REQUESTS)
-
-static volatile gint32 buffer_rwlock_count;
-static volatile gpointer buffer_rwlock_exclusive;
-
-// Can be used recursively.
-static void
-buffer_lock (void)
-{
-       /*
-        * If the thread holding the exclusive lock tries to modify the
-        * reader count, just make it a no-op. This way, we also avoid
-        * invoking the GC safe point macros below, which could break if
-        * done from a thread that is currently the initiator of STW.
-        *
-        * In other words, we rely on the fact that the GC thread takes
-        * the exclusive lock in the gc_event () callback when the world
-        * is about to stop.
-        */
-       if (InterlockedReadPointer (&buffer_rwlock_exclusive) != (gpointer) thread_id ()) {
-               MONO_ENTER_GC_SAFE;
-
-               while (InterlockedReadPointer (&buffer_rwlock_exclusive))
-                       mono_thread_info_yield ();
-
-               InterlockedIncrement (&buffer_rwlock_count);
-
-               MONO_EXIT_GC_SAFE;
-       }
-
-       mono_memory_barrier ();
-}
-
-static void
-buffer_unlock (void)
-{
-       mono_memory_barrier ();
-
-       // See the comment in buffer_lock ().
-       if (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id ())
-               return;
-
-       g_assert (InterlockedRead (&buffer_rwlock_count) && "Why are we trying to decrement a zero reader count?");
-
-       InterlockedDecrement (&buffer_rwlock_count);
-}
-
-// Cannot be used recursively.
-static void
-buffer_lock_excl (void)
-{
-       gpointer tid = (gpointer) thread_id ();
-
-       g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) != tid && "Why are we taking the exclusive lock twice?");
-
-       MONO_ENTER_GC_SAFE;
-
-       while (InterlockedCompareExchangePointer (&buffer_rwlock_exclusive, tid, 0))
-               mono_thread_info_yield ();
-
-       while (InterlockedRead (&buffer_rwlock_count))
-               mono_thread_info_yield ();
-
-       MONO_EXIT_GC_SAFE;
-
-       mono_memory_barrier ();
-}
-
-static void
-buffer_unlock_excl (void)
-{
-       mono_memory_barrier ();
-
-       g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) && "Why is the exclusive lock not held?");
-       g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id () && "Why does another thread hold the exclusive lock?");
-       g_assert (!InterlockedRead (&buffer_rwlock_count) && "Why are there readers when the exclusive lock is held?");
-
-       InterlockedWritePointer (&buffer_rwlock_exclusive, NULL);
-}
-
-typedef struct _BinaryObject BinaryObject;
-struct _BinaryObject {
-       BinaryObject *next;
-       void *addr;
-       char *name;
-};
-
-struct _MonoProfiler {
-       FILE* file;
-#if defined (HAVE_SYS_ZLIB)
-       gzFile gzfile;
-#endif
-       char *args;
-       uint64_t startup_time;
-       int pipe_output;
-       int command_port;
-       int server_socket;
-       int pipes [2];
-       MonoNativeThreadId helper_thread;
-       MonoNativeThreadId writer_thread;
-       MonoNativeThreadId dumper_thread;
-       volatile gint32 run_writer_thread;
-       MonoLockFreeAllocSizeClass writer_entry_size_class;
-       MonoLockFreeAllocator writer_entry_allocator;
-       MonoLockFreeQueue writer_queue;
-       MonoSemType writer_queue_sem;
-       MonoConcurrentHashTable *method_table;
-       mono_mutex_t method_table_mutex;
-       volatile gint32 run_dumper_thread;
-       MonoLockFreeQueue dumper_queue;
-       MonoSemType dumper_queue_sem;
-       MonoLockFreeAllocSizeClass sample_size_class;
-       MonoLockFreeAllocator sample_allocator;
-       MonoLockFreeQueue sample_reuse_queue;
-       BinaryObject *binary_objects;
-       GPtrArray *coverage_filters;
-};
-
-typedef struct {
-       MonoLockFreeQueueNode node;
-       GPtrArray *methods;
-       LogBuffer *buffer;
-} WriterQueueEntry;
-
-#define WRITER_ENTRY_BLOCK_SIZE (mono_pagesize ())
-
-typedef struct {
-       MonoMethod *method;
-       MonoJitInfo *ji;
-       uint64_t time;
-} MethodInfo;
-
-#ifdef HOST_WIN32
-
-#define PROF_TLS_SET(VAL) (TlsSetValue (profiler_tls, (VAL)))
-#define PROF_TLS_GET() ((MonoProfilerThread *) TlsGetValue (profiler_tls))
-#define PROF_TLS_INIT() (profiler_tls = TlsAlloc ())
-#define PROF_TLS_FREE() (TlsFree (profiler_tls))
-
-static DWORD profiler_tls;
-
-#elif HAVE_KW_THREAD
-
-#define PROF_TLS_SET(VAL) (profiler_tls = (VAL))
-#define PROF_TLS_GET() (profiler_tls)
-#define PROF_TLS_INIT()
-#define PROF_TLS_FREE()
-
-static __thread MonoProfilerThread *profiler_tls;
-
-#else
-
-#define PROF_TLS_SET(VAL) (pthread_setspecific (profiler_tls, (VAL)))
-#define PROF_TLS_GET() ((MonoProfilerThread *) pthread_getspecific (profiler_tls))
-#define PROF_TLS_INIT() (pthread_key_create (&profiler_tls, NULL))
-#define PROF_TLS_FREE() (pthread_key_delete (profiler_tls))
-
-static pthread_key_t profiler_tls;
-
-#endif
-
-static char*
-pstrdup (const char *s)
-{
-       int len = strlen (s) + 1;
-       char *p = (char *) g_malloc (len);
-       memcpy (p, s, len);
-       return p;
-}
-
-static void *
-alloc_buffer (int size)
-{
-       return mono_valloc (NULL, size, MONO_MMAP_READ | MONO_MMAP_WRITE | MONO_MMAP_ANON | MONO_MMAP_PRIVATE, MONO_MEM_ACCOUNT_PROFILER);
-}
-
-static void
-free_buffer (void *buf, int size)
-{
-       mono_vfree (buf, size, MONO_MEM_ACCOUNT_PROFILER);
-}
-
-static LogBuffer*
-create_buffer (uintptr_t tid)
-{
-       LogBuffer* buf = (LogBuffer *) alloc_buffer (BUFFER_SIZE);
-
-       InterlockedIncrement (&buffer_allocations_ctr);
-
-       buf->size = BUFFER_SIZE;
-       buf->time_base = current_time ();
-       buf->last_time = buf->time_base;
-       buf->buf_end = (unsigned char *) buf + buf->size;
-       buf->cursor = buf->buf;
-       buf->thread_id = tid;
-
-       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)
-{
-       thread->buffer = create_buffer (thread->node.key);
-       thread->methods = NULL;
-}
-
-static void
-clear_hazard_pointers (MonoThreadHazardPointers *hp)
-{
-       mono_hazard_pointer_clear (hp, 0);
-       mono_hazard_pointer_clear (hp, 1);
-       mono_hazard_pointer_clear (hp, 2);
-}
-
-static MonoProfilerThread *
-init_thread (MonoProfiler *prof, gboolean add_to_lls)
-{
-       MonoProfilerThread *thread = PROF_TLS_GET ();
-
-       /*
-        * Sometimes we may try to initialize a thread twice. One example is the
-        * main thread: We initialize it when setting up the profiler, but we will
-        * also get a thread_start () callback for it. Another example is when
-        * attaching new threads to the runtime: We may get a gc_alloc () callback
-        * for that thread's thread object (where we initialize it), soon followed
-        * by a thread_start () callback.
-        *
-        * These cases are harmless anyhow. Just return if we've already done the
-        * initialization work.
-        */
-       if (thread)
-               return thread;
-
-       thread = g_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);
-
-       /*
-        * Some internal profiler threads don't need to be cleaned up
-        * by the main thread on shutdown.
-        */
-       if (add_to_lls) {
-               MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
-               g_assert (mono_lls_insert (&profiler_thread_list, hp, &thread->node) && "Why can't we insert the thread in the LLS?");
-               clear_hazard_pointers (hp);
-       }
-
-       PROF_TLS_SET (thread);
-
-       return thread;
-}
-
-// Only valid if init_thread () was called with add_to_lls = FALSE.
-static void
-deinit_thread (MonoProfilerThread *thread)
-{
-       g_assert (!thread->attached && "Why are we manually freeing an attached thread?");
-
-       g_free (thread);
-       PROF_TLS_SET (NULL);
-}
-
-// Only valid if init_thread () was called with add_to_lls = FALSE.
-static LogBuffer *
-ensure_logbuf_unsafe (MonoProfilerThread *thread, int bytes)
-{
-       LogBuffer *old = thread->buffer;
-
-       if (old && old->cursor + bytes + 100 < old->buf_end)
-               return old;
-
-       LogBuffer *new_ = create_buffer (thread->node.key);
-       new_->next = old;
-       thread->buffer = new_;
-
-       return new_;
-}
-
-static void
-encode_uleb128 (uint64_t value, uint8_t *buf, uint8_t **endbuf)
-{
-       uint8_t *p = buf;
-
-       do {
-               uint8_t b = value & 0x7f;
-               value >>= 7;
-
-               if (value != 0) /* more bytes to come */
-                       b |= 0x80;
-
-               *p ++ = b;
-       } while (value);
-
-       *endbuf = p;
-}
-
-static void
-encode_sleb128 (intptr_t value, uint8_t *buf, uint8_t **endbuf)
-{
-       int more = 1;
-       int negative = (value < 0);
-       unsigned int size = sizeof (intptr_t) * 8;
-       uint8_t byte;
-       uint8_t *p = buf;
-
-       while (more) {
-               byte = value & 0x7f;
-               value >>= 7;
-
-               /* the following is unnecessary if the
-                * implementation of >>= uses an arithmetic rather
-                * than logical shift for a signed left operand
-                */
-               if (negative)
-                       /* sign extend */
-                       value |= - ((intptr_t) 1 <<(size - 7));
-
-               /* sign bit of byte is second high order bit (0x40) */
-               if ((value == 0 && !(byte & 0x40)) ||
-                   (value == -1 && (byte & 0x40)))
-                       more = 0;
-               else
-                       byte |= 0x80;
-
-               *p ++= byte;
-       }
-
-       *endbuf = p;
-}
-
-static void
-emit_byte (LogBuffer *logbuffer, int value)
-{
-       logbuffer->cursor [0] = value;
-       logbuffer->cursor++;
-
-       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);
-
-       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;
-       encode_uleb128 (tdiff, logbuffer->cursor, &logbuffer->cursor);
-       logbuffer->last_time = value;
-
-       g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
-}
-
-static void
-emit_event_time (LogBuffer *logbuffer, int event, uint64_t time)
-{
-       emit_byte (logbuffer, event);
-       emit_time (logbuffer, time);
-}
-
-static void
-emit_event (LogBuffer *logbuffer, int event)
-{
-       emit_event_time (logbuffer, event, current_time ());
-}
-
-static void
-emit_svalue (LogBuffer *logbuffer, int64_t value)
-{
-       encode_sleb128 (value, logbuffer->cursor, &logbuffer->cursor);
-
-       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);
-
-       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);
-
-       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;
-       }
-
-       encode_sleb128 ((intptr_t) ((char *) method - (char *) logbuffer->last_method), logbuffer->cursor, &logbuffer->cursor);
-       logbuffer->last_method = (intptr_t) method;
-
-       g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
-}
-
-static void
-register_method_local (MonoMethod *method, MonoJitInfo *ji)
-{
-       MonoProfilerThread *thread = PROF_TLS_GET ();
-
-       if (!mono_conc_hashtable_lookup (thread->profiler->method_table, method)) {
-               MethodInfo *info = (MethodInfo *) g_malloc (sizeof (MethodInfo));
-
-               info->method = method;
-               info->ji = ji;
-               info->time = current_time ();
-
-               GPtrArray *arr = thread->methods ? thread->methods : (thread->methods = g_ptr_array_new ());
-               g_ptr_array_add (arr, info);
-       }
-}
-
-static void
-emit_method (LogBuffer *logbuffer, MonoMethod *method)
-{
-       register_method_local (method, NULL);
-       emit_method_inner (logbuffer, method);
-}
-
-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);
-
-       g_assert (logbuffer->cursor <= logbuffer->buf_end && "Why are we writing past the buffer end?");
-}
-
-static void
-emit_string (LogBuffer *logbuffer, const char *str, size_t size)
-{
-       size_t i = 0;
-       if (str) {
-               for (; i < size; i++) {
-                       if (str[i] == '\0')
-                               break;
-                       emit_byte (logbuffer, str [i]);
-               }
-       }
-       emit_byte (logbuffer, '\0');
-}
-
-static void
-emit_double (LogBuffer *logbuffer, double value)
-{
-       int i;
-       unsigned char buffer[8];
-       memcpy (buffer, &value, 8);
-#if G_BYTE_ORDER == G_BIG_ENDIAN
-       for (i = 7; i >= 0; i--)
-#else
-       for (i = 0; i < 8; i++)
-#endif
-               emit_byte (logbuffer, buffer[i]);
-}
-
-static char*
-write_int16 (char *buf, int32_t value)
-{
-       int i;
-       for (i = 0; i < 2; ++i) {
-               buf [i] = value;
-               value >>= 8;
-       }
-       return buf + 2;
-}
-
-static char*
-write_int32 (char *buf, int32_t value)
-{
-       int i;
-       for (i = 0; i < 4; ++i) {
-               buf [i] = value;
-               value >>= 8;
-       }
-       return buf + 4;
-}
-
-static char*
-write_int64 (char *buf, int64_t value)
-{
-       int i;
-       for (i = 0; i < 8; ++i) {
-               buf [i] = value;
-               value >>= 8;
-       }
-       return buf + 8;
-}
-
-static char *
-write_header_string (char *p, const char *str)
-{
-       size_t len = strlen (str) + 1;
-
-       p = write_int32 (p, len);
-       strcpy (p, str);
-
-       return p + len;
-}
-
-static void
-dump_header (MonoProfiler *profiler)
-{
-       const char *args = profiler->args;
-       const char *arch = mono_config_get_cpu ();
-       const char *os = mono_config_get_os ();
-
-       char *hbuf = g_malloc (
-               sizeof (gint32) /* header id */ +
-               sizeof (gint8) /* major version */ +
-               sizeof (gint8) /* minor version */ +
-               sizeof (gint8) /* data version */ +
-               sizeof (gint8) /* word size */ +
-               sizeof (gint64) /* startup time */ +
-               sizeof (gint32) /* timer overhead */ +
-               sizeof (gint32) /* flags */ +
-               sizeof (gint32) /* process id */ +
-               sizeof (gint16) /* command port */ +
-               sizeof (gint32) + strlen (args) + 1 /* arguments */ +
-               sizeof (gint32) + strlen (arch) + 1 /* architecture */ +
-               sizeof (gint32) + strlen (os) + 1 /* operating system */
-       );
-       char *p = hbuf;
-
-       p = write_int32 (p, LOG_HEADER_ID);
-       *p++ = LOG_VERSION_MAJOR;
-       *p++ = LOG_VERSION_MINOR;
-       *p++ = LOG_DATA_VERSION;
-       *p++ = sizeof (void *);
-       p = write_int64 (p, ((uint64_t) time (NULL)) * 1000);
-       p = write_int32 (p, timer_overhead);
-       p = write_int32 (p, 0); /* flags */
-       p = write_int32 (p, process_id ());
-       p = write_int16 (p, profiler->command_port);
-       p = write_header_string (p, args);
-       p = write_header_string (p, arch);
-       p = write_header_string (p, os);
-
-#if defined (HAVE_SYS_ZLIB)
-       if (profiler->gzfile) {
-               gzwrite (profiler->gzfile, hbuf, p - hbuf);
-       } else
-#endif
-       {
-               fwrite (hbuf, p - hbuf, 1, profiler->file);
-               fflush (profiler->file);
-       }
-
-       g_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 (MonoProfilerThread *thread)
-{
-       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 (&thread->profiler->writer_queue, &entry->node);
-       mono_os_sem_post (&thread->profiler->writer_queue_sem);
-}
-
-static void
-free_thread (gpointer p)
-{
-       MonoProfilerThread *thread = p;
-
-       if (!thread->ended) {
-               /*
-                * 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);
-
-               LogBuffer *buf = ensure_logbuf_unsafe (thread,
-                       EVENT_SIZE /* event */ +
-                       BYTE_SIZE /* type */ +
-                       LEB128_SIZE /* tid */
-               );
-
-               emit_event (buf, TYPE_END_UNLOAD | TYPE_METADATA);
-               emit_byte (buf, TYPE_THREAD);
-               emit_ptr (buf, (void *) thread->node.key);
-       }
-
-       send_buffer (thread);
-
-       g_free (thread);
-}
-
-static void
-remove_thread (MonoProfilerThread *thread)
-{
-       MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
-
-       if (mono_lls_remove (&profiler_thread_list, hp, &thread->node))
-               mono_thread_hazardous_try_free (thread, free_thread);
-
-       clear_hazard_pointers (hp);
-}
-
-static void
-dump_buffer (MonoProfiler *profiler, LogBuffer *buf)
-{
-       char hbuf [128];
-       char *p = hbuf;
-
-       if (buf->next)
-               dump_buffer (profiler, buf->next);
-
-       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
-#endif
-               {
-                       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);
-}
-
-static void
-dump_buffer_threadless (MonoProfiler *profiler, LogBuffer *buf)
-{
-       for (LogBuffer *iter = buf; iter; iter = iter->next)
-               iter->thread_id = 0;
-
-       dump_buffer (profiler, buf);
-}
-
-static void
-process_requests (void)
-{
-       if (heapshot_requested)
-               mono_gc_collect (mono_gc_max_generation ());
-}
-
-// Only valid if init_thread () was called with add_to_lls = FALSE.
-static void
-send_log_unsafe (gboolean if_needed)
-{
-       MonoProfilerThread *thread = PROF_TLS_GET ();
-
-       if (!if_needed || (if_needed && thread->buffer->next)) {
-               if (!thread->attached)
-                       for (LogBuffer *iter = thread->buffer; iter; iter = iter->next)
-                               iter->thread_id = 0;
-
-               send_buffer (thread);
-               init_buffer_state (thread);
-       }
-}
-
-// Assumes that the exclusive lock is held.
-static void
-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) {
-               g_assert (thread->attached && "Why is a thread in the LLS not attached?");
-
-               send_buffer (thread);
-               init_buffer_state (thread);
-       } MONO_LLS_FOREACH_SAFE_END
-}
-
-// Assumes that the exclusive lock is held.
-static void
-sync_point_mark (MonoProfilerSyncPointType type)
-{
-       g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id () && "Why don't we hold the exclusive lock?");
-
-       ENTER_LOG (&sync_points_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               LEB128_SIZE /* type */
-       );
-
-       emit_event (logbuffer, TYPE_META | TYPE_SYNC_POINT);
-       emit_byte (logbuffer, type);
-
-       EXIT_LOG_EXPLICIT (NO_SEND, NO_REQUESTS);
-
-       send_log_unsafe (FALSE);
-}
-
-// Assumes that the exclusive lock is held.
-static void
-sync_point (MonoProfilerSyncPointType type)
-{
-       sync_point_flush ();
-       sync_point_mark (type);
-}
-
-static int
-gc_reference (MonoObject *obj, MonoClass *klass, uintptr_t size, uintptr_t num, MonoObject **refs, uintptr_t *offsets, void *data)
-{
-       /* account for object alignment in the heap */
-       size += 7;
-       size &= ~7;
-
-       ENTER_LOG (&heap_objects_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               LEB128_SIZE /* obj */ +
-               LEB128_SIZE /* klass */ +
-               LEB128_SIZE /* size */ +
-               LEB128_SIZE /* num */ +
-               num * (
-                       LEB128_SIZE /* offset */ +
-                       LEB128_SIZE /* ref */
-               )
-       );
-
-       emit_event (logbuffer, TYPE_HEAP_OBJECT | TYPE_HEAP);
-       emit_obj (logbuffer, obj);
-       emit_ptr (logbuffer, klass);
-       emit_value (logbuffer, size);
-       emit_value (logbuffer, num);
-
-       uintptr_t last_offset = 0;
-
-       for (int i = 0; i < num; ++i) {
-               emit_value (logbuffer, offsets [i] - last_offset);
-               last_offset = offsets [i];
-               emit_obj (logbuffer, refs [i]);
-       }
-
-       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
-
-       return 0;
-}
-
-static unsigned int hs_mode_ms = 0;
-static unsigned int hs_mode_gc = 0;
-static unsigned int hs_mode_ondemand = 0;
-static unsigned int gc_count = 0;
-static uint64_t last_hs_time = 0;
-static gboolean do_heap_walk = FALSE;
-
-static void
-heap_walk (MonoProfiler *profiler)
-{
-       ENTER_LOG (&heap_starts_ctr, logbuffer,
-               EVENT_SIZE /* event */
-       );
-
-       emit_event (logbuffer, TYPE_HEAP_START | TYPE_HEAP);
-
-       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
-
-       mono_gc_walk_heap (0, gc_reference, NULL);
-
-       ENTER_LOG (&heap_ends_ctr, logbuffer,
-               EVENT_SIZE /* event */
-       );
-
-       emit_event (logbuffer, TYPE_HEAP_END | TYPE_HEAP);
-
-       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
-}
-
-static void
-gc_roots (MonoProfiler *prof, int num, void **objects, int *root_types, uintptr_t *extra_info)
-{
-       ENTER_LOG (&heap_roots_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               LEB128_SIZE /* num */ +
-               LEB128_SIZE /* collections */ +
-               num * (
-                       LEB128_SIZE /* object */ +
-                       LEB128_SIZE /* root type */ +
-                       LEB128_SIZE /* extra info */
-               )
-       );
-
-       emit_event (logbuffer, TYPE_HEAP_ROOT | TYPE_HEAP);
-       emit_value (logbuffer, num);
-       emit_value (logbuffer, mono_gc_collection_count (mono_gc_max_generation ()));
-
-       for (int i = 0; i < num; ++i) {
-               emit_obj (logbuffer, objects [i]);
-               emit_byte (logbuffer, root_types [i]);
-               emit_value (logbuffer, extra_info [i]);
-       }
-
-       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
-}
-
-static void
-gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation)
-{
-       ENTER_LOG (&gc_events_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               BYTE_SIZE /* gc event */ +
-               BYTE_SIZE /* generation */
-       );
-
-       emit_event (logbuffer, TYPE_GC_EVENT | TYPE_GC);
-       emit_byte (logbuffer, ev);
-       emit_byte (logbuffer, generation);
-
-       EXIT_LOG_EXPLICIT (NO_SEND, NO_REQUESTS);
-
-       switch (ev) {
-       case MONO_GC_EVENT_START:
-               if (generation == mono_gc_max_generation ())
-                       gc_count++;
-
-               uint64_t now = current_time ();
-
-               if (hs_mode_ms && (now - last_hs_time) / 1000 * 1000 >= hs_mode_ms)
-                       do_heap_walk = TRUE;
-               else if (hs_mode_gc && !(gc_count % hs_mode_gc))
-                       do_heap_walk = TRUE;
-               else if (hs_mode_ondemand)
-                       do_heap_walk = heapshot_requested;
-               else if (!hs_mode_ms && !hs_mode_gc && generation == mono_gc_max_generation ())
-                       do_heap_walk = TRUE;
-               break;
-       case MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED:
-               /*
-                * Ensure that no thread can be in the middle of writing to
-                * a buffer when the world stops...
-                */
-               buffer_lock_excl ();
-               break;
-       case MONO_GC_EVENT_POST_STOP_WORLD:
-               /*
-                * ... So that we now have a consistent view of all buffers.
-                * This allows us to flush them. We need to do this because
-                * they may contain object allocation events that need to be
-                * committed to the log file before any object move events
-                * that will be produced during this GC.
-                */
-               sync_point (SYNC_POINT_WORLD_STOP);
-               break;
-       case MONO_GC_EVENT_PRE_START_WORLD:
-               if (do_heap_shot && do_heap_walk) {
-                       heap_walk (profiler);
-
-                       do_heap_walk = FALSE;
-                       heapshot_requested = 0;
-                       last_hs_time = current_time ();
-               }
-               break;
-       case MONO_GC_EVENT_POST_START_WORLD_UNLOCKED:
-               /*
-                * Similarly, we must now make sure that any object moves
-                * written to the GC thread's buffer are flushed. Otherwise,
-                * object allocation events for certain addresses could come
-                * after the move events that made those addresses available.
-                */
-               sync_point_mark (SYNC_POINT_WORLD_START);
-
-               /*
-                * Finally, it is safe to allow other threads to write to
-                * their buffers again.
-                */
-               buffer_unlock_excl ();
-               break;
-       default:
-               break;
-       }
-}
-
-static void
-gc_resize (MonoProfiler *profiler, int64_t new_size)
-{
-       ENTER_LOG (&gc_resizes_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               LEB128_SIZE /* new size */
-       );
-
-       emit_event (logbuffer, TYPE_GC_RESIZE | TYPE_GC);
-       emit_value (logbuffer, new_size);
-
-       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
-}
-
-typedef struct {
-       int count;
-       MonoMethod* methods [MAX_FRAMES];
-       int32_t il_offsets [MAX_FRAMES];
-       int32_t native_offsets [MAX_FRAMES];
-} FrameData;
-
-static int num_frames = MAX_FRAMES;
-
-static mono_bool
-walk_stack (MonoMethod *method, int32_t native_offset, int32_t il_offset, mono_bool managed, void* data)
-{
-       FrameData *frame = (FrameData *)data;
-       if (method && frame->count < num_frames) {
-               frame->il_offsets [frame->count] = il_offset;
-               frame->native_offsets [frame->count] = native_offset;
-               frame->methods [frame->count++] = method;
-               //printf ("In %d %s at %d (native: %d)\n", frame->count, mono_method_get_name (method), il_offset, native_offset);
-       }
-       return frame->count == num_frames;
-}
-
-/*
- * a note about stack walks: they can cause more profiler events to fire,
- * so we need to make sure they don't happen after we started emitting an
- * event, hence the collect_bt/emit_bt split.
- */
-static void
-collect_bt (FrameData *data)
-{
-       data->count = 0;
-       mono_stack_walk_no_il (walk_stack, data);
-}
-
-static void
-emit_bt (MonoProfiler *prof, LogBuffer *logbuffer, FrameData *data)
-{
-       /* FIXME: this is actually tons of data and we should
-        * just output it the first time and use an id the next
-        */
-       if (data->count > num_frames)
-               printf ("bad num frames: %d\n", data->count);
-       emit_value (logbuffer, data->count);
-       //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 (logbuffer, data->methods [--data->count]);
-       }
-}
-
-static void
-gc_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
-{
-       init_thread (prof, TRUE);
-
-       int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces) ? TYPE_ALLOC_BT : 0;
-       FrameData data;
-       uintptr_t len = mono_object_get_size (obj);
-       /* account for object alignment in the heap */
-       len += 7;
-       len &= ~7;
-
-       if (do_bt)
-               collect_bt (&data);
-
-       ENTER_LOG (&gc_allocs_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               LEB128_SIZE /* klass */ +
-               LEB128_SIZE /* obj */ +
-               LEB128_SIZE /* size */ +
-               (do_bt ? (
-                       LEB128_SIZE /* count */ +
-                       data.count * (
-                               LEB128_SIZE /* method */
-                       )
-               ) : 0)
-       );
-
-       emit_event (logbuffer, do_bt | TYPE_ALLOC);
-       emit_ptr (logbuffer, klass);
-       emit_obj (logbuffer, obj);
-       emit_value (logbuffer, len);
-
-       if (do_bt)
-               emit_bt (prof, logbuffer, &data);
-
-       EXIT_LOG;
-}
-
-static void
-gc_moves (MonoProfiler *prof, void **objects, int num)
-{
-       ENTER_LOG (&gc_moves_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               LEB128_SIZE /* num */ +
-               num * (
-                       LEB128_SIZE /* object */
-               )
-       );
-
-       emit_event (logbuffer, TYPE_GC_MOVE | TYPE_GC);
-       emit_value (logbuffer, num);
-
-       for (int i = 0; i < num; ++i)
-               emit_obj (logbuffer, objects [i]);
-
-       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
-}
-
-static void
-gc_handle (MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj)
-{
-       int do_bt = nocalls && InterlockedRead (&runtime_inited) && !notraces;
-       FrameData data;
-
-       if (do_bt)
-               collect_bt (&data);
-
-       gint32 *ctr = op == MONO_PROFILER_GC_HANDLE_CREATED ? &gc_handle_creations_ctr : &gc_handle_deletions_ctr;
-
-       ENTER_LOG (ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               LEB128_SIZE /* type */ +
-               LEB128_SIZE /* handle */ +
-               (op == MONO_PROFILER_GC_HANDLE_CREATED ? (
-                       LEB128_SIZE /* obj */
-               ) : 0) +
-               (do_bt ? (
-                       LEB128_SIZE /* count */ +
-                       data.count * (
-                               LEB128_SIZE /* method */
-                       )
-               ) : 0)
-       );
-
-       if (op == MONO_PROFILER_GC_HANDLE_CREATED)
-               emit_event (logbuffer, (do_bt ? TYPE_GC_HANDLE_CREATED_BT : TYPE_GC_HANDLE_CREATED) | TYPE_GC);
-       else if (op == MONO_PROFILER_GC_HANDLE_DESTROYED)
-               emit_event (logbuffer, (do_bt ? TYPE_GC_HANDLE_DESTROYED_BT : TYPE_GC_HANDLE_DESTROYED) | TYPE_GC);
-       else
-               g_assert_not_reached ();
-
-       emit_value (logbuffer, type);
-       emit_value (logbuffer, handle);
-
-       if (op == MONO_PROFILER_GC_HANDLE_CREATED)
-               emit_obj (logbuffer, obj);
-
-       if (do_bt)
-               emit_bt (prof, logbuffer, &data);
-
-       EXIT_LOG;
-}
-
-static void
-finalize_begin (MonoProfiler *prof)
-{
-       ENTER_LOG (&finalize_begins_ctr, buf,
-               EVENT_SIZE /* event */
-       );
-
-       emit_event (buf, TYPE_GC_FINALIZE_START | TYPE_GC);
-
-       EXIT_LOG;
-}
-
-static void
-finalize_end (MonoProfiler *prof)
-{
-       ENTER_LOG (&finalize_ends_ctr, buf,
-               EVENT_SIZE /* event */
-       );
-
-       emit_event (buf, TYPE_GC_FINALIZE_END | TYPE_GC);
-
-       EXIT_LOG;
-}
-
-static void
-finalize_object_begin (MonoProfiler *prof, MonoObject *obj)
-{
-       ENTER_LOG (&finalize_object_begins_ctr, buf,
-               EVENT_SIZE /* event */ +
-               LEB128_SIZE /* obj */
-       );
-
-       emit_event (buf, TYPE_GC_FINALIZE_OBJECT_START | TYPE_GC);
-       emit_obj (buf, obj);
-
-       EXIT_LOG;
-}
-
-static void
-finalize_object_end (MonoProfiler *prof, MonoObject *obj)
-{
-       ENTER_LOG (&finalize_object_ends_ctr, buf,
-               EVENT_SIZE /* event */ +
-               LEB128_SIZE /* obj */
-       );
-
-       emit_event (buf, TYPE_GC_FINALIZE_OBJECT_END | TYPE_GC);
-       emit_obj (buf, obj);
-
-       EXIT_LOG;
-}
-
-static char*
-push_nesting (char *p, MonoClass *klass)
-{
-       MonoClass *nesting;
-       const char *name;
-       const char *nspace;
-       nesting = mono_class_get_nesting_type (klass);
-       if (nesting) {
-               p = push_nesting (p, nesting);
-               *p++ = '/';
-               *p = 0;
-       }
-       name = mono_class_get_name (klass);
-       nspace = mono_class_get_namespace (klass);
-       if (*nspace) {
-               strcpy (p, nspace);
-               p += strlen (nspace);
-               *p++ = '.';
-               *p = 0;
-       }
-       strcpy (p, name);
-       p += strlen (name);
-       return p;
-}
-
-static char*
-type_name (MonoClass *klass)
-{
-       char buf [1024];
-       char *p;
-       push_nesting (buf, klass);
-       p = (char *) g_malloc (strlen (buf) + 1);
-       strcpy (p, buf);
-       return p;
-}
-
-static void
-image_loaded (MonoProfiler *prof, MonoImage *image, int result)
-{
-       if (result != MONO_PROFILE_OK)
-               return;
-
-       const char *name = mono_image_get_filename (image);
-       int nlen = strlen (name) + 1;
-
-       ENTER_LOG (&image_loads_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               BYTE_SIZE /* type */ +
-               LEB128_SIZE /* image */ +
-               nlen /* name */
-       );
-
-       emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
-       emit_byte (logbuffer, TYPE_IMAGE);
-       emit_ptr (logbuffer, image);
-       memcpy (logbuffer->cursor, name, nlen);
-       logbuffer->cursor += nlen;
-
-       EXIT_LOG;
-}
-
-static void
-image_unloaded (MonoProfiler *prof, MonoImage *image)
-{
-       const char *name = mono_image_get_filename (image);
-       int nlen = strlen (name) + 1;
-
-       ENTER_LOG (&image_unloads_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               BYTE_SIZE /* type */ +
-               LEB128_SIZE /* image */ +
-               nlen /* name */
-       );
-
-       emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
-       emit_byte (logbuffer, TYPE_IMAGE);
-       emit_ptr (logbuffer, image);
-       memcpy (logbuffer->cursor, name, nlen);
-       logbuffer->cursor += nlen;
-
-       EXIT_LOG;
-}
-
-static void
-assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly, int result)
-{
-       if (result != MONO_PROFILE_OK)
-               return;
-
-       char *name = mono_stringify_assembly_name (mono_assembly_get_name (assembly));
-       int nlen = strlen (name) + 1;
-
-       ENTER_LOG (&assembly_loads_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               BYTE_SIZE /* type */ +
-               LEB128_SIZE /* assembly */ +
-               nlen /* name */
-       );
-
-       emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
-       emit_byte (logbuffer, TYPE_ASSEMBLY);
-       emit_ptr (logbuffer, assembly);
-       memcpy (logbuffer->cursor, name, nlen);
-       logbuffer->cursor += nlen;
-
-       EXIT_LOG;
-
-       mono_free (name);
-}
-
-static void
-assembly_unloaded (MonoProfiler *prof, MonoAssembly *assembly)
-{
-       char *name = mono_stringify_assembly_name (mono_assembly_get_name (assembly));
-       int nlen = strlen (name) + 1;
-
-       ENTER_LOG (&assembly_unloads_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               BYTE_SIZE /* type */ +
-               LEB128_SIZE /* assembly */ +
-               nlen /* name */
-       );
-
-       emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
-       emit_byte (logbuffer, TYPE_ASSEMBLY);
-       emit_ptr (logbuffer, assembly);
-       memcpy (logbuffer->cursor, name, nlen);
-       logbuffer->cursor += nlen;
-
-       EXIT_LOG;
-
-       mono_free (name);
-}
-
-static void
-class_loaded (MonoProfiler *prof, MonoClass *klass, int result)
-{
-       if (result != MONO_PROFILE_OK)
-               return;
-
-       char *name;
-
-       if (InterlockedRead (&runtime_inited))
-               name = mono_type_get_name (mono_class_get_type (klass));
-       else
-               name = type_name (klass);
-
-       int nlen = strlen (name) + 1;
-       MonoImage *image = mono_class_get_image (klass);
-
-       ENTER_LOG (&class_loads_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               BYTE_SIZE /* type */ +
-               LEB128_SIZE /* klass */ +
-               LEB128_SIZE /* image */ +
-               nlen /* name */
-       );
-
-       emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
-       emit_byte (logbuffer, TYPE_CLASS);
-       emit_ptr (logbuffer, klass);
-       emit_ptr (logbuffer, image);
-       memcpy (logbuffer->cursor, name, nlen);
-       logbuffer->cursor += nlen;
-
-       EXIT_LOG;
-
-       if (runtime_inited)
-               mono_free (name);
-       else
-               g_free (name);
-}
-
-static void
-class_unloaded (MonoProfiler *prof, MonoClass *klass)
-{
-       char *name;
-
-       if (InterlockedRead (&runtime_inited))
-               name = mono_type_get_name (mono_class_get_type (klass));
-       else
-               name = type_name (klass);
-
-       int nlen = strlen (name) + 1;
-       MonoImage *image = mono_class_get_image (klass);
-
-       ENTER_LOG (&class_unloads_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               BYTE_SIZE /* type */ +
-               LEB128_SIZE /* klass */ +
-               LEB128_SIZE /* image */ +
-               nlen /* name */
-       );
-
-       emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
-       emit_byte (logbuffer, TYPE_CLASS);
-       emit_ptr (logbuffer, klass);
-       emit_ptr (logbuffer, image);
-       memcpy (logbuffer->cursor, name, nlen);
-       logbuffer->cursor += nlen;
-
-       EXIT_LOG;
-
-       if (runtime_inited)
-               mono_free (name);
-       else
-               g_free (name);
-}
-
-static void process_method_enter_coverage (MonoProfiler *prof, MonoMethod *method);
-
-static void
-method_enter (MonoProfiler *prof, MonoMethod *method)
-{
-       process_method_enter_coverage (prof, method);
-
-       if (!only_coverage && PROF_TLS_GET ()->call_depth++ <= max_call_depth) {
-               ENTER_LOG (&method_entries_ctr, logbuffer,
-                       EVENT_SIZE /* event */ +
-                       LEB128_SIZE /* method */
-               );
-
-               emit_event (logbuffer, TYPE_ENTER | TYPE_METHOD);
-               emit_method (logbuffer, method);
-
-               EXIT_LOG;
-       }
-}
-
-static void
-method_leave (MonoProfiler *prof, MonoMethod *method)
-{
-       if (!only_coverage && --PROF_TLS_GET ()->call_depth <= max_call_depth) {
-               ENTER_LOG (&method_exits_ctr, logbuffer,
-                       EVENT_SIZE /* event */ +
-                       LEB128_SIZE /* method */
-               );
-
-               emit_event (logbuffer, TYPE_LEAVE | TYPE_METHOD);
-               emit_method (logbuffer, method);
-
-               EXIT_LOG;
-       }
-}
-
-static void
-method_exc_leave (MonoProfiler *prof, MonoMethod *method)
-{
-       if (!only_coverage && !nocalls && --PROF_TLS_GET ()->call_depth <= max_call_depth) {
-               ENTER_LOG (&method_exception_exits_ctr, logbuffer,
-                       EVENT_SIZE /* event */ +
-                       LEB128_SIZE /* method */
-               );
-
-               emit_event (logbuffer, TYPE_EXC_LEAVE | TYPE_METHOD);
-               emit_method (logbuffer, method);
-
-               EXIT_LOG;
-       }
-}
-
-static void
-method_jitted (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *ji, int result)
-{
-       if (result != MONO_PROFILE_OK)
-               return;
-
-       register_method_local (method, ji);
-
-       process_requests ();
-}
-
-static void
-code_buffer_new (MonoProfiler *prof, void *buffer, int size, MonoProfilerCodeBufferType type, void *data)
-{
-       char *name;
-       int nlen;
-
-       if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
-               name = (char *) data;
-               nlen = strlen (name) + 1;
-       } else {
-               name = NULL;
-               nlen = 0;
-       }
-
-       ENTER_LOG (&code_buffers_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               BYTE_SIZE /* type */ +
-               LEB128_SIZE /* buffer */ +
-               LEB128_SIZE /* size */ +
-               (name ? (
-                       nlen /* name */
-               ) : 0)
-       );
-
-       emit_event (logbuffer, TYPE_JITHELPER | TYPE_RUNTIME);
-       emit_byte (logbuffer, type);
-       emit_ptr (logbuffer, buffer);
-       emit_value (logbuffer, size);
-
-       if (name) {
-               memcpy (logbuffer->cursor, name, nlen);
-               logbuffer->cursor += nlen;
-       }
-
-       EXIT_LOG;
-}
-
-static void
-throw_exc (MonoProfiler *prof, MonoObject *object)
-{
-       int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces) ? TYPE_THROW_BT : 0;
-       FrameData data;
-
-       if (do_bt)
-               collect_bt (&data);
-
-       ENTER_LOG (&exception_throws_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               LEB128_SIZE /* object */ +
-               (do_bt ? (
-                       LEB128_SIZE /* count */ +
-                       data.count * (
-                               LEB128_SIZE /* method */
-                       )
-               ) : 0)
-       );
-
-       emit_event (logbuffer, do_bt | TYPE_EXCEPTION);
-       emit_obj (logbuffer, object);
-
-       if (do_bt)
-               emit_bt (prof, logbuffer, &data);
-
-       EXIT_LOG;
-}
-
-static void
-clause_exc (MonoProfiler *prof, MonoMethod *method, int clause_type, int clause_num)
-{
-       ENTER_LOG (&exception_clauses_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               BYTE_SIZE /* clause type */ +
-               LEB128_SIZE /* clause num */ +
-               LEB128_SIZE /* method */
-       );
-
-       emit_event (logbuffer, TYPE_EXCEPTION | TYPE_CLAUSE);
-       emit_byte (logbuffer, clause_type);
-       emit_value (logbuffer, clause_num);
-       emit_method (logbuffer, method);
-
-       EXIT_LOG;
-}
-
-static void
-monitor_event (MonoProfiler *profiler, MonoObject *object, MonoProfilerMonitorEvent event)
-{
-       int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces && event == MONO_PROFILER_MONITOR_CONTENTION) ? TYPE_MONITOR_BT : 0;
-       FrameData data;
-
-       if (do_bt)
-               collect_bt (&data);
-
-       gint32 *ctr;
-
-       switch (event) {
-       case MONO_PROFILER_MONITOR_CONTENTION:
-               ctr = &monitor_contentions_ctr;
-               break;
-       case MONO_PROFILER_MONITOR_DONE:
-               ctr = &monitor_acquisitions_ctr;
-               break;
-       case MONO_PROFILER_MONITOR_FAIL:
-               ctr = &monitor_failures_ctr;
-               break;
-       default:
-               g_assert_not_reached ();
-               break;
-       }
-
-       ENTER_LOG (ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               LEB128_SIZE /* object */ +
-               (do_bt ? (
-                       LEB128_SIZE /* count */ +
-                       data.count * (
-                               LEB128_SIZE /* method */
-                       )
-               ) : 0)
-       );
-
-       emit_event (logbuffer, (event << 4) | do_bt | TYPE_MONITOR);
-       emit_obj (logbuffer, object);
-
-       if (do_bt)
-               emit_bt (profiler, logbuffer, &data);
-
-       EXIT_LOG;
-}
-
-static void
-thread_start (MonoProfiler *prof, uintptr_t tid)
-{
-       init_thread (prof, TRUE);
-
-       ENTER_LOG (&thread_starts_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               BYTE_SIZE /* type */ +
-               LEB128_SIZE /* tid */
-       );
-
-       emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
-       emit_byte (logbuffer, TYPE_THREAD);
-       emit_ptr (logbuffer, (void*) tid);
-
-       EXIT_LOG;
-}
-
-static void
-thread_end (MonoProfiler *prof, uintptr_t tid)
-{
-       ENTER_LOG (&thread_ends_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               BYTE_SIZE /* type */ +
-               LEB128_SIZE /* tid */
-       );
-
-       emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
-       emit_byte (logbuffer, TYPE_THREAD);
-       emit_ptr (logbuffer, (void*) tid);
-
-       EXIT_LOG_EXPLICIT (NO_SEND, NO_REQUESTS);
-
-       MonoProfilerThread *thread = PROF_TLS_GET ();
-
-       thread->ended = TRUE;
-       remove_thread (thread);
-
-       PROF_TLS_SET (NULL);
-}
-
-static void
-thread_name (MonoProfiler *prof, uintptr_t tid, const char *name)
-{
-       int len = strlen (name) + 1;
-
-       ENTER_LOG (&thread_names_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               BYTE_SIZE /* type */ +
-               LEB128_SIZE /* tid */ +
-               len /* name */
-       );
-
-       emit_event (logbuffer, TYPE_METADATA);
-       emit_byte (logbuffer, TYPE_THREAD);
-       emit_ptr (logbuffer, (void*)tid);
-       memcpy (logbuffer->cursor, name, len);
-       logbuffer->cursor += len;
-
-       EXIT_LOG;
-}
-
-static void
-domain_loaded (MonoProfiler *prof, MonoDomain *domain, int result)
-{
-       if (result != MONO_PROFILE_OK)
-               return;
-
-       ENTER_LOG (&domain_loads_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               BYTE_SIZE /* type */ +
-               LEB128_SIZE /* domain id */
-       );
-
-       emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
-       emit_byte (logbuffer, TYPE_DOMAIN);
-       emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
-
-       EXIT_LOG;
-}
-
-static void
-domain_unloaded (MonoProfiler *prof, MonoDomain *domain)
-{
-       ENTER_LOG (&domain_unloads_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               BYTE_SIZE /* type */ +
-               LEB128_SIZE /* domain id */
-       );
-
-       emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
-       emit_byte (logbuffer, TYPE_DOMAIN);
-       emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
-
-       EXIT_LOG;
-}
-
-static void
-domain_name (MonoProfiler *prof, MonoDomain *domain, const char *name)
-{
-       int nlen = strlen (name) + 1;
-
-       ENTER_LOG (&domain_names_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               BYTE_SIZE /* type */ +
-               LEB128_SIZE /* domain id */ +
-               nlen /* name */
-       );
-
-       emit_event (logbuffer, TYPE_METADATA);
-       emit_byte (logbuffer, TYPE_DOMAIN);
-       emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
-       memcpy (logbuffer->cursor, name, nlen);
-       logbuffer->cursor += nlen;
-
-       EXIT_LOG;
-}
-
-static void
-context_loaded (MonoProfiler *prof, MonoAppContext *context)
-{
-       ENTER_LOG (&context_loads_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               BYTE_SIZE /* type */ +
-               LEB128_SIZE /* context id */ +
-               LEB128_SIZE /* domain id */
-       );
-
-       emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
-       emit_byte (logbuffer, TYPE_CONTEXT);
-       emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_id (context));
-       emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_domain_id (context));
-
-       EXIT_LOG;
-}
-
-static void
-context_unloaded (MonoProfiler *prof, MonoAppContext *context)
-{
-       ENTER_LOG (&context_unloads_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               BYTE_SIZE /* type */ +
-               LEB128_SIZE /* context id */ +
-               LEB128_SIZE /* domain id */
-       );
-
-       emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
-       emit_byte (logbuffer, TYPE_CONTEXT);
-       emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_id (context));
-       emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_domain_id (context));
-
-       EXIT_LOG;
-}
-
-typedef struct {
-       MonoMethod *method;
-       MonoDomain *domain;
-       void *base_address;
-       int offset;
-} AsyncFrameInfo;
-
-typedef struct {
-       MonoLockFreeQueueNode node;
-       MonoProfiler *prof;
-       uint64_t time;
-       uintptr_t tid;
-       void *ip;
-       int count;
-       AsyncFrameInfo frames [MONO_ZERO_LEN_ARRAY];
-} SampleHit;
-
-static mono_bool
-async_walk_stack (MonoMethod *method, MonoDomain *domain, void *base_address, int offset, void *data)
-{
-       SampleHit *sample = (SampleHit *) data;
-
-       if (sample->count < num_frames) {
-               int i = sample->count;
-
-               sample->frames [i].method = method;
-               sample->frames [i].domain = domain;
-               sample->frames [i].base_address = base_address;
-               sample->frames [i].offset = offset;
-
-               sample->count++;
-       }
-
-       return sample->count == num_frames;
-}
-
-#define SAMPLE_SLOT_SIZE(FRAMES) (sizeof (SampleHit) + sizeof (AsyncFrameInfo) * (FRAMES - MONO_ZERO_LEN_ARRAY))
-#define SAMPLE_BLOCK_SIZE (mono_pagesize ())
-
-static void
-enqueue_sample_hit (gpointer p)
-{
-       SampleHit *sample = p;
-
-       mono_lock_free_queue_node_unpoison (&sample->node);
-       mono_lock_free_queue_enqueue (&sample->prof->dumper_queue, &sample->node);
-       mono_os_sem_post (&sample->prof->dumper_queue_sem);
-}
-
-static void
-mono_sample_hit (MonoProfiler *profiler, unsigned char *ip, void *context)
-{
-       /*
-        * Please note: We rely on the runtime loading the profiler with
-        * MONO_DL_EAGER (RTLD_NOW) so that references to runtime functions within
-        * this function (and its siblings) are resolved when the profiler is
-        * loaded. Otherwise, we would potentially invoke the dynamic linker when
-        * invoking runtime functions, which is not async-signal-safe.
-        */
-
-       if (InterlockedRead (&in_shutdown))
-               return;
-
-       SampleHit *sample = (SampleHit *) mono_lock_free_queue_dequeue (&profiler->sample_reuse_queue);
-
-       if (!sample) {
-               /*
-                * If we're out of reusable sample events and we're not allowed to
-                * allocate more, we have no choice but to drop the event.
-                */
-               if (InterlockedRead (&sample_allocations_ctr) >= max_allocated_sample_hits)
-                       return;
-
-               sample = mono_lock_free_alloc (&profiler->sample_allocator);
-               sample->prof = profiler;
-               mono_lock_free_queue_node_init (&sample->node, TRUE);
-
-               InterlockedIncrement (&sample_allocations_ctr);
-       }
-
-       sample->count = 0;
-       mono_stack_walk_async_safe (&async_walk_stack, context, sample);
-
-       sample->time = current_time ();
-       sample->tid = thread_id ();
-       sample->ip = ip;
-
-       mono_thread_hazardous_try_free (sample, enqueue_sample_hit);
-}
-
-static uintptr_t *code_pages = 0;
-static int num_code_pages = 0;
-static int size_code_pages = 0;
-#define CPAGE_SHIFT (9)
-#define CPAGE_SIZE (1 << CPAGE_SHIFT)
-#define CPAGE_MASK (~(CPAGE_SIZE - 1))
-#define CPAGE_ADDR(p) ((p) & CPAGE_MASK)
-
-static uintptr_t
-add_code_page (uintptr_t *hash, uintptr_t hsize, uintptr_t page)
-{
-       uintptr_t i;
-       uintptr_t start_pos;
-       start_pos = (page >> CPAGE_SHIFT) % hsize;
-       i = start_pos;
-       do {
-               if (hash [i] && CPAGE_ADDR (hash [i]) == CPAGE_ADDR (page)) {
-                       return 0;
-               } else if (!hash [i]) {
-                       hash [i] = page;
-                       return 1;
-               }
-               /* wrap around */
-               if (++i == hsize)
-                       i = 0;
-       } while (i != start_pos);
-       /* should not happen */
-       printf ("failed code page store\n");
-       return 0;
-}
-
-static void
-add_code_pointer (uintptr_t ip)
-{
-       uintptr_t i;
-       if (num_code_pages * 2 >= size_code_pages) {
-               uintptr_t *n;
-               uintptr_t old_size = size_code_pages;
-               size_code_pages *= 2;
-               if (size_code_pages == 0)
-                       size_code_pages = 16;
-               n = (uintptr_t *) g_calloc (sizeof (uintptr_t) * size_code_pages, 1);
-               for (i = 0; i < old_size; ++i) {
-                       if (code_pages [i])
-                               add_code_page (n, size_code_pages, code_pages [i]);
-               }
-               if (code_pages)
-                       g_free (code_pages);
-               code_pages = n;
-       }
-       num_code_pages += add_code_page (code_pages, size_code_pages, ip & CPAGE_MASK);
-}
-
-/* ELF code crashes on some systems. */
-//#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
-#if 0
-static void
-dump_ubin (MonoProfiler *prof, const char *filename, uintptr_t load_addr, uint64_t offset, uintptr_t size)
-{
-       int len = strlen (filename) + 1;
-
-       ENTER_LOG (&sample_ubins_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               LEB128_SIZE /* load address */ +
-               LEB128_SIZE /* offset */ +
-               LEB128_SIZE /* size */ +
-               nlen /* file name */
-       );
-
-       emit_event (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_UBIN);
-       emit_svalue (logbuffer, load_addr);
-       emit_uvalue (logbuffer, offset);
-       emit_uvalue (logbuffer, size);
-       memcpy (logbuffer->cursor, filename, len);
-       logbuffer->cursor += len;
-
-       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
-}
-#endif
-
-static void
-dump_usym (MonoProfiler *prof, const char *name, uintptr_t value, uintptr_t size)
-{
-       int len = strlen (name) + 1;
-
-       ENTER_LOG (&sample_usyms_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               LEB128_SIZE /* value */ +
-               LEB128_SIZE /* size */ +
-               len /* name */
-       );
-
-       emit_event (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_USYM);
-       emit_ptr (logbuffer, (void*)value);
-       emit_value (logbuffer, size);
-       memcpy (logbuffer->cursor, name, len);
-       logbuffer->cursor += len;
-
-       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
-}
-
-/* ELF code crashes on some systems. */
-//#if defined(ELFMAG0)
-#if 0
-
-#if SIZEOF_VOID_P == 4
-#define ELF_WSIZE 32
-#else
-#define ELF_WSIZE 64
-#endif
-#ifndef ElfW
-#define ElfW(type)      _ElfW (Elf, ELF_WSIZE, type)
-#define _ElfW(e,w,t)    _ElfW_1 (e, w, _##t)
-#define _ElfW_1(e,w,t)  e##w##t
-#endif
-
-static void
-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) {
-               const char* sym;
-               sym =  strtab + symbols [i].st_name;
-               if (!symbols [i].st_name || !symbols [i].st_size || (symbols [i].st_info & 0xf) != STT_FUNC)
-                       continue;
-               //printf ("symbol %s at %d\n", sym, symbols [i].st_value);
-               dump_usym (sym, (uintptr_t)load_addr + symbols [i].st_value, symbols [i].st_size);
-       }
-}
-
-static int
-read_elf_symbols (MonoProfiler *prof, const char *filename, void *load_addr)
-{
-       int fd, i;
-       void *data;
-       struct stat statb;
-       uint64_t file_size;
-       ElfW(Ehdr) *header;
-       ElfW(Shdr) *sheader;
-       ElfW(Shdr) *shstrtabh;
-       ElfW(Shdr) *symtabh = NULL;
-       ElfW(Shdr) *strtabh = NULL;
-       ElfW(Sym) *symbols = NULL;
-       const char *strtab;
-       int num_symbols;
-
-       fd = open (filename, O_RDONLY);
-       if (fd < 0)
-               return 0;
-       if (fstat (fd, &statb) != 0) {
-               close (fd);
-               return 0;
-       }
-       file_size = statb.st_size;
-       data = mmap (NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
-       close (fd);
-       if (data == MAP_FAILED)
-               return 0;
-       header = data;
-       if (header->e_ident [EI_MAG0] != ELFMAG0 ||
-                       header->e_ident [EI_MAG1] != ELFMAG1 ||
-                       header->e_ident [EI_MAG2] != ELFMAG2 ||
-                       header->e_ident [EI_MAG3] != ELFMAG3 ) {
-               munmap (data, file_size);
-               return 0;
-       }
-       sheader = (void*)((char*)data + header->e_shoff);
-       shstrtabh = (void*)((char*)sheader + (header->e_shentsize * header->e_shstrndx));
-       strtab = (const char*)data + shstrtabh->sh_offset;
-       for (i = 0; i < header->e_shnum; ++i) {
-               //printf ("section header: %d\n", sheader->sh_type);
-               if (sheader->sh_type == SHT_SYMTAB) {
-                       symtabh = sheader;
-                       strtabh = (void*)((char*)data + header->e_shoff + sheader->sh_link * header->e_shentsize);
-                       /*printf ("symtab section header: %d, .strstr: %d\n", i, sheader->sh_link);*/
-                       break;
-               }
-               sheader = (void*)((char*)sheader + header->e_shentsize);
-       }
-       if (!symtabh || !strtabh) {
-               munmap (data, file_size);
-               return 0;
-       }
-       strtab = (const char*)data + strtabh->sh_offset;
-       num_symbols = symtabh->sh_size / symtabh->sh_entsize;
-       symbols = (void*)((char*)data + symtabh->sh_offset);
-       dump_elf_symbols (symbols, num_symbols, strtab, load_addr);
-       munmap (data, file_size);
-       return 1;
-}
-#endif
-
-/* ELF code crashes on some systems. */
-//#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
-#if 0
-static int
-elf_dl_callback (struct dl_phdr_info *info, size_t size, void *data)
-{
-       MonoProfiler *prof = data;
-       char buf [256];
-       const char *filename;
-       BinaryObject *obj;
-       char *a = (void*)info->dlpi_addr;
-       int i, num_sym;
-       ElfW(Dyn) *dyn = NULL;
-       ElfW(Sym) *symtab = NULL;
-       ElfW(Word) *hash_table = NULL;
-       ElfW(Ehdr) *header = NULL;
-       const char* strtab = NULL;
-       for (obj = prof->binary_objects; obj; obj = obj->next) {
-               if (obj->addr == a)
-                       return 0;
-       }
-       filename = info->dlpi_name;
-       if (!filename)
-               return 0;
-       if (!info->dlpi_addr && !filename [0]) {
-               int l = readlink ("/proc/self/exe", buf, sizeof (buf) - 1);
-               if (l > 0) {
-                       buf [l] = 0;
-                       filename = buf;
-               }
-       }
-       obj = g_calloc (sizeof (BinaryObject), 1);
-       obj->addr = (void*)info->dlpi_addr;
-       obj->name = pstrdup (filename);
-       obj->next = prof->binary_objects;
-       prof->binary_objects = obj;
-       //printf ("loaded file: %s at %p, segments: %d\n", filename, (void*)info->dlpi_addr, info->dlpi_phnum);
-       a = NULL;
-       for (i = 0; i < info->dlpi_phnum; ++i) {
-               //printf ("segment type %d file offset: %d, size: %d\n", info->dlpi_phdr[i].p_type, info->dlpi_phdr[i].p_offset, info->dlpi_phdr[i].p_memsz);
-               if (info->dlpi_phdr[i].p_type == PT_LOAD && !header) {
-                       header = (ElfW(Ehdr)*)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
-                       if (header->e_ident [EI_MAG0] != ELFMAG0 ||
-                                       header->e_ident [EI_MAG1] != ELFMAG1 ||
-                                       header->e_ident [EI_MAG2] != ELFMAG2 ||
-                                       header->e_ident [EI_MAG3] != ELFMAG3 ) {
-                               header = NULL;
-                       }
-                       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);
-               }
-       }
-       if (read_elf_symbols (prof, filename, (void*)info->dlpi_addr))
-               return 0;
-       if (!info->dlpi_name || !info->dlpi_name[0])
-               return 0;
-       if (!dyn)
-               return 0;
-       for (i = 0; dyn [i].d_tag != DT_NULL; ++i) {
-               if (dyn [i].d_tag == DT_SYMTAB) {
-                       if (symtab && do_debug)
-                               printf ("multiple symtabs: %d\n", i);
-                       symtab = (ElfW(Sym) *)(a + dyn [i].d_un.d_ptr);
-               } else if (dyn [i].d_tag == DT_HASH) {
-                       hash_table = (ElfW(Word) *)(a + dyn [i].d_un.d_ptr);
-               } else if (dyn [i].d_tag == DT_STRTAB) {
-                       strtab = (const char*)(a + dyn [i].d_un.d_ptr);
-               }
-       }
-       if (!hash_table)
-               return 0;
-       num_sym = hash_table [1];
-       dump_elf_symbols (prof, symtab, num_sym, strtab, (void*)info->dlpi_addr);
-       return 0;
-}
-
-static int
-load_binaries (MonoProfiler *prof)
-{
-       dl_iterate_phdr (elf_dl_callback, prof);
-       return 1;
-}
-#else
-static int
-load_binaries (MonoProfiler *prof)
-{
-       return 0;
-}
-#endif
-
-static const char*
-symbol_for (uintptr_t code)
-{
-#ifdef HAVE_DLADDR
-       void *ip = (void*)code;
-       Dl_info di;
-       if (dladdr (ip, &di)) {
-               if (di.dli_sname)
-                       return di.dli_sname;
-       } else {
-       /*      char **names;
-               names = backtrace_symbols (&ip, 1);
-               if (names) {
-                       const char* p = names [0];
-                       g_free (names);
-                       return p;
-               }
-               */
-       }
-#endif
-       return NULL;
-}
-
-static void
-dump_unmanaged_coderefs (MonoProfiler *prof)
-{
-       int i;
-       const char* last_symbol;
-       uintptr_t addr, page_end;
-
-       if (load_binaries (prof))
-               return;
-       for (i = 0; i < size_code_pages; ++i) {
-               const char* sym;
-               if (!code_pages [i] || code_pages [i] & 1)
-                       continue;
-               last_symbol = NULL;
-               addr = CPAGE_ADDR (code_pages [i]);
-               page_end = addr + CPAGE_SIZE;
-               code_pages [i] |= 1;
-               /* we dump the symbols for the whole page */
-               for (; addr < page_end; addr += 16) {
-                       sym = symbol_for (addr);
-                       if (sym && sym == last_symbol)
-                               continue;
-                       last_symbol = sym;
-                       if (!sym)
-                               continue;
-                       dump_usym (prof, sym, addr, 0); /* let's not guess the size */
-                       //printf ("found symbol at %p: %s\n", (void*)addr, sym);
-               }
-       }
-}
-
-typedef struct MonoCounterAgent {
-       MonoCounter *counter;
-       // MonoCounterAgent specific data :
-       void *value;
-       size_t value_size;
-       short index;
-       short emitted;
-       struct MonoCounterAgent *next;
-} MonoCounterAgent;
-
-static MonoCounterAgent* counters;
-static int counters_index = 1;
-static mono_mutex_t counters_mutex;
-
-static void
-counters_add_agent (MonoCounter *counter)
-{
-       if (InterlockedRead (&in_shutdown))
-               return;
-
-       MonoCounterAgent *agent, *item;
-
-       mono_os_mutex_lock (&counters_mutex);
-
-       for (agent = counters; agent; agent = agent->next) {
-               if (agent->counter == counter) {
-                       agent->value_size = 0;
-                       if (agent->value) {
-                               g_free (agent->value);
-                               agent->value = NULL;
-                       }
-                       goto done;
-               }
-       }
-
-       agent = (MonoCounterAgent *) g_malloc (sizeof (MonoCounterAgent));
-       agent->counter = counter;
-       agent->value = NULL;
-       agent->value_size = 0;
-       agent->index = counters_index++;
-       agent->emitted = 0;
-       agent->next = NULL;
-
-       if (!counters) {
-               counters = agent;
-       } else {
-               item = counters;
-               while (item->next)
-                       item = item->next;
-               item->next = agent;
-       }
-
-done:
-       mono_os_mutex_unlock (&counters_mutex);
-}
-
-static mono_bool
-counters_init_foreach_callback (MonoCounter *counter, gpointer data)
-{
-       counters_add_agent (counter);
-       return TRUE;
-}
-
-static void
-counters_init (MonoProfiler *profiler)
-{
-       mono_os_mutex_init (&counters_mutex);
-
-       mono_counters_on_register (&counters_add_agent);
-       mono_counters_foreach (counters_init_foreach_callback, NULL);
-}
-
-static void
-counters_emit (MonoProfiler *profiler)
-{
-       MonoCounterAgent *agent;
-       int len = 0;
-       int size =
-               EVENT_SIZE /* event */ +
-               LEB128_SIZE /* len */
-       ;
-
-       mono_os_mutex_lock (&counters_mutex);
-
-       for (agent = counters; agent; agent = agent->next) {
-               if (agent->emitted)
-                       continue;
-
-               size +=
-                       LEB128_SIZE /* section */ +
-                       strlen (mono_counter_get_name (agent->counter)) + 1 /* name */ +
-                       BYTE_SIZE /* type */ +
-                       BYTE_SIZE /* unit */ +
-                       BYTE_SIZE /* variance */ +
-                       LEB128_SIZE /* index */
-               ;
-
-               len++;
-       }
-
-       if (!len)
-               goto done;
-
-       ENTER_LOG (&counter_descriptors_ctr, logbuffer, size);
-
-       emit_event (logbuffer, TYPE_SAMPLE_COUNTERS_DESC | TYPE_SAMPLE);
-       emit_value (logbuffer, len);
-
-       for (agent = counters; agent; agent = agent->next) {
-               const char *name;
-
-               if (agent->emitted)
-                       continue;
-
-               name = mono_counter_get_name (agent->counter);
-               emit_value (logbuffer, mono_counter_get_section (agent->counter));
-               emit_string (logbuffer, name, strlen (name) + 1);
-               emit_byte (logbuffer, mono_counter_get_type (agent->counter));
-               emit_byte (logbuffer, mono_counter_get_unit (agent->counter));
-               emit_byte (logbuffer, mono_counter_get_variance (agent->counter));
-               emit_value (logbuffer, agent->index);
-
-               agent->emitted = 1;
-       }
-
-       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
-
-done:
-       mono_os_mutex_unlock (&counters_mutex);
-}
-
-static void
-counters_sample (MonoProfiler *profiler, uint64_t timestamp)
-{
-       MonoCounterAgent *agent;
-       MonoCounter *counter;
-       int type;
-       int buffer_size;
-       void *buffer;
-       int size;
-
-       counters_emit (profiler);
-
-       buffer_size = 8;
-       buffer = g_calloc (1, buffer_size);
-
-       mono_os_mutex_lock (&counters_mutex);
-
-       size =
-               EVENT_SIZE /* event */
-       ;
-
-       for (agent = counters; agent; agent = agent->next) {
-               size +=
-                       LEB128_SIZE /* index */ +
-                       BYTE_SIZE /* type */ +
-                       mono_counter_get_size (agent->counter) /* value */
-               ;
-       }
-
-       size +=
-               LEB128_SIZE /* stop marker */
-       ;
-
-       ENTER_LOG (&counter_samples_ctr, logbuffer, size);
-
-       emit_event_time (logbuffer, TYPE_SAMPLE_COUNTERS | TYPE_SAMPLE, timestamp);
-
-       for (agent = counters; agent; agent = agent->next) {
-               size_t size;
-
-               counter = agent->counter;
-
-               size = mono_counter_get_size (counter);
-
-               if (size > buffer_size) {
-                       buffer_size = size;
-                       buffer = g_realloc (buffer, buffer_size);
-               }
-
-               memset (buffer, 0, buffer_size);
-
-               g_assert (mono_counters_sample (counter, buffer, size));
-
-               type = mono_counter_get_type (counter);
-
-               if (!agent->value) {
-                       agent->value = g_calloc (1, size);
-                       agent->value_size = size;
-               } else {
-                       if (type == MONO_COUNTER_STRING) {
-                               if (strcmp (agent->value, buffer) == 0)
-                                       continue;
-                       } else {
-                               if (agent->value_size == size && memcmp (agent->value, buffer, size) == 0)
-                                       continue;
-                       }
-               }
-
-               emit_uvalue (logbuffer, agent->index);
-               emit_byte (logbuffer, type);
-               switch (type) {
-               case MONO_COUNTER_INT:
-#if SIZEOF_VOID_P == 4
-               case MONO_COUNTER_WORD:
-#endif
-                       emit_svalue (logbuffer, *(int*)buffer - *(int*)agent->value);
-                       break;
-               case MONO_COUNTER_UINT:
-                       emit_uvalue (logbuffer, *(guint*)buffer - *(guint*)agent->value);
-                       break;
-               case MONO_COUNTER_TIME_INTERVAL:
-               case MONO_COUNTER_LONG:
-#if SIZEOF_VOID_P == 8
-               case MONO_COUNTER_WORD:
-#endif
-                       emit_svalue (logbuffer, *(gint64*)buffer - *(gint64*)agent->value);
-                       break;
-               case MONO_COUNTER_ULONG:
-                       emit_uvalue (logbuffer, *(guint64*)buffer - *(guint64*)agent->value);
-                       break;
-               case MONO_COUNTER_DOUBLE:
-                       emit_double (logbuffer, *(double*)buffer);
-                       break;
-               case MONO_COUNTER_STRING:
-                       if (size == 0) {
-                               emit_byte (logbuffer, 0);
-                       } else {
-                               emit_byte (logbuffer, 1);
-                               emit_string (logbuffer, (char*)buffer, size);
-                       }
-                       break;
-               default:
-                       g_assert_not_reached ();
-               }
-
-               if (type == MONO_COUNTER_STRING && size > agent->value_size) {
-                       agent->value = g_realloc (agent->value, size);
-                       agent->value_size = size;
-               }
-
-               if (size > 0)
-                       memcpy (agent->value, buffer, size);
-       }
-       g_free (buffer);
-
-       emit_value (logbuffer, 0);
-
-       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
-
-       mono_os_mutex_unlock (&counters_mutex);
-}
-
-typedef struct _PerfCounterAgent PerfCounterAgent;
-struct _PerfCounterAgent {
-       PerfCounterAgent *next;
-       int index;
-       char *category_name;
-       char *name;
-       int type;
-       gint64 value;
-       guint8 emitted;
-       guint8 updated;
-       guint8 deleted;
-};
-
-static PerfCounterAgent *perfcounters = NULL;
-
-static void
-perfcounters_emit (MonoProfiler *profiler)
-{
-       PerfCounterAgent *pcagent;
-       int len = 0;
-       int size =
-               EVENT_SIZE /* event */ +
-               LEB128_SIZE /* len */
-       ;
-
-       for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
-               if (pcagent->emitted)
-                       continue;
-
-               size +=
-                       LEB128_SIZE /* section */ +
-                       strlen (pcagent->category_name) + 1 /* category name */ +
-                       strlen (pcagent->name) + 1 /* name */ +
-                       BYTE_SIZE /* type */ +
-                       BYTE_SIZE /* unit */ +
-                       BYTE_SIZE /* variance */ +
-                       LEB128_SIZE /* index */
-               ;
-
-               len++;
-       }
-
-       if (!len)
-               return;
-
-       ENTER_LOG (&perfcounter_descriptors_ctr, logbuffer, size);
-
-       emit_event (logbuffer, TYPE_SAMPLE_COUNTERS_DESC | TYPE_SAMPLE);
-       emit_value (logbuffer, len);
-
-       for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
-               if (pcagent->emitted)
-                       continue;
-
-               emit_value (logbuffer, MONO_COUNTER_PERFCOUNTERS);
-               emit_string (logbuffer, pcagent->category_name, strlen (pcagent->category_name) + 1);
-               emit_string (logbuffer, pcagent->name, strlen (pcagent->name) + 1);
-               emit_byte (logbuffer, MONO_COUNTER_LONG);
-               emit_byte (logbuffer, MONO_COUNTER_RAW);
-               emit_byte (logbuffer, MONO_COUNTER_VARIABLE);
-               emit_value (logbuffer, pcagent->index);
-
-               pcagent->emitted = 1;
-       }
-
-       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
-}
-
-static gboolean
-perfcounters_foreach (char *category_name, char *name, unsigned char type, gint64 value, gpointer user_data)
-{
-       PerfCounterAgent *pcagent;
-
-       for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
-               if (strcmp (pcagent->category_name, category_name) != 0 || strcmp (pcagent->name, name) != 0)
-                       continue;
-               if (pcagent->value == value)
-                       return TRUE;
-
-               pcagent->value = value;
-               pcagent->updated = 1;
-               pcagent->deleted = 0;
-               return TRUE;
-       }
-
-       pcagent = g_new0 (PerfCounterAgent, 1);
-       pcagent->next = perfcounters;
-       pcagent->index = counters_index++;
-       pcagent->category_name = g_strdup (category_name);
-       pcagent->name = g_strdup (name);
-       pcagent->type = (int) type;
-       pcagent->value = value;
-       pcagent->emitted = 0;
-       pcagent->updated = 1;
-       pcagent->deleted = 0;
-
-       perfcounters = pcagent;
-
-       return TRUE;
-}
-
-static void
-perfcounters_sample (MonoProfiler *profiler, uint64_t timestamp)
-{
-       PerfCounterAgent *pcagent;
-       int len = 0;
-       int size;
-
-       mono_os_mutex_lock (&counters_mutex);
-
-       /* mark all perfcounters as deleted, foreach will unmark them as necessary */
-       for (pcagent = perfcounters; pcagent; pcagent = pcagent->next)
-               pcagent->deleted = 1;
-
-       mono_perfcounter_foreach (perfcounters_foreach, perfcounters);
-
-       perfcounters_emit (profiler);
-
-       size =
-               EVENT_SIZE /* event */
-       ;
-
-       for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
-               if (pcagent->deleted || !pcagent->updated)
-                       continue;
-
-               size +=
-                       LEB128_SIZE /* index */ +
-                       BYTE_SIZE /* type */ +
-                       LEB128_SIZE /* value */
-               ;
-
-               len++;
-       }
-
-       if (!len)
-               goto done;
-
-       size +=
-               LEB128_SIZE /* stop marker */
-       ;
-
-       ENTER_LOG (&perfcounter_samples_ctr, logbuffer, size);
-
-       emit_event_time (logbuffer, TYPE_SAMPLE_COUNTERS | TYPE_SAMPLE, timestamp);
-
-       for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
-               if (pcagent->deleted || !pcagent->updated)
-                       continue;
-               emit_uvalue (logbuffer, pcagent->index);
-               emit_byte (logbuffer, MONO_COUNTER_LONG);
-               emit_svalue (logbuffer, pcagent->value);
-
-               pcagent->updated = 0;
-       }
-
-       emit_value (logbuffer, 0);
-
-       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
-
-done:
-       mono_os_mutex_unlock (&counters_mutex);
-}
-
-static void
-counters_and_perfcounters_sample (MonoProfiler *prof)
-{
-       uint64_t now = current_time ();
-
-       counters_sample (prof, now);
-       perfcounters_sample (prof, now);
-}
-
-#define COVERAGE_DEBUG(x) if (debug_coverage) {x}
-static mono_mutex_t coverage_mutex;
-static MonoConcurrentHashTable *coverage_methods = NULL;
-static MonoConcurrentHashTable *coverage_assemblies = NULL;
-static MonoConcurrentHashTable *coverage_classes = NULL;
-
-static MonoConcurrentHashTable *filtered_classes = NULL;
-static MonoConcurrentHashTable *entered_methods = NULL;
-static MonoConcurrentHashTable *image_to_methods = NULL;
-static MonoConcurrentHashTable *suppressed_assemblies = NULL;
-static gboolean coverage_initialized = FALSE;
-
-static GPtrArray *coverage_data = NULL;
-static int previous_offset = 0;
-
-typedef struct {
-       MonoLockFreeQueueNode node;
-       MonoMethod *method;
-} MethodNode;
-
-typedef struct {
-       int offset;
-       int counter;
-       char *filename;
-       int line;
-       int column;
-} CoverageEntry;
-
-static void
-free_coverage_entry (gpointer data, gpointer userdata)
-{
-       CoverageEntry *entry = (CoverageEntry *)data;
-       g_free (entry->filename);
-       g_free (entry);
-}
-
-static void
-obtain_coverage_for_method (MonoProfiler *prof, const MonoProfileCoverageEntry *entry)
-{
-       int offset = entry->iloffset - previous_offset;
-       CoverageEntry *e = g_new (CoverageEntry, 1);
-
-       previous_offset = entry->iloffset;
-
-       e->offset = offset;
-       e->counter = entry->counter;
-       e->filename = g_strdup(entry->filename ? entry->filename : "");
-       e->line = entry->line;
-       e->column = entry->col;
-
-       g_ptr_array_add (coverage_data, e);
-}
-
-static char *
-parse_generic_type_names(char *name)
-{
-       char *new_name, *ret;
-       int within_generic_declaration = 0, generic_members = 1;
-
-       if (name == NULL || *name == '\0')
-               return g_strdup ("");
-
-       if (!(ret = new_name = (char *) g_calloc (strlen (name) * 4 + 1, sizeof (char))))
-               return NULL;
-
-       do {
-               switch (*name) {
-                       case '<':
-                               within_generic_declaration = 1;
-                               break;
-
-                       case '>':
-                               within_generic_declaration = 0;
-
-                               if (*(name - 1) != '<') {
-                                       *new_name++ = '`';
-                                       *new_name++ = '0' + generic_members;
-                               } else {
-                                       memcpy (new_name, "&lt;&gt;", 8);
-                                       new_name += 8;
-                               }
-
-                               generic_members = 0;
-                               break;
-
-                       case ',':
-                               generic_members++;
-                               break;
-
-                       default:
-                               if (!within_generic_declaration)
-                                       *new_name++ = *name;
-
-                               break;
-               }
-       } while (*name++);
-
-       return ret;
-}
-
-static int method_id;
-static void
-build_method_buffer (gpointer key, gpointer value, gpointer userdata)
-{
-       MonoMethod *method = (MonoMethod *)value;
-       MonoProfiler *prof = (MonoProfiler *)userdata;
-       MonoClass *klass;
-       MonoImage *image;
-       char *class_name;
-       const char *image_name, *method_name, *sig, *first_filename;
-       guint i;
-
-       previous_offset = 0;
-       coverage_data = g_ptr_array_new ();
-
-       mono_profiler_coverage_get (prof, method, obtain_coverage_for_method);
-
-       klass = mono_method_get_class (method);
-       image = mono_class_get_image (klass);
-       image_name = mono_image_get_name (image);
-
-       sig = mono_signature_get_desc (mono_method_signature (method), TRUE);
-       class_name = parse_generic_type_names (mono_type_get_name (mono_class_get_type (klass)));
-       method_name = mono_method_get_name (method);
-
-       if (coverage_data->len != 0) {
-               CoverageEntry *entry = (CoverageEntry *)coverage_data->pdata[0];
-               first_filename = entry->filename ? entry->filename : "";
-       } else
-               first_filename = "";
-
-       image_name = image_name ? image_name : "";
-       sig = sig ? sig : "";
-       method_name = method_name ? method_name : "";
-
-       ENTER_LOG (&coverage_methods_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               strlen (image_name) + 1 /* image name */ +
-               strlen (class_name) + 1 /* class name */ +
-               strlen (method_name) + 1 /* method name */ +
-               strlen (sig) + 1 /* signature */ +
-               strlen (first_filename) + 1 /* first file name */ +
-               LEB128_SIZE /* token */ +
-               LEB128_SIZE /* method id */ +
-               LEB128_SIZE /* entries */
-       );
-
-       emit_event (logbuffer, TYPE_COVERAGE_METHOD | TYPE_COVERAGE);
-       emit_string (logbuffer, image_name, strlen (image_name) + 1);
-       emit_string (logbuffer, class_name, strlen (class_name) + 1);
-       emit_string (logbuffer, method_name, strlen (method_name) + 1);
-       emit_string (logbuffer, sig, strlen (sig) + 1);
-       emit_string (logbuffer, first_filename, strlen (first_filename) + 1);
-
-       emit_uvalue (logbuffer, mono_method_get_token (method));
-       emit_uvalue (logbuffer, method_id);
-       emit_value (logbuffer, coverage_data->len);
-
-       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
-
-       for (i = 0; i < coverage_data->len; i++) {
-               CoverageEntry *entry = (CoverageEntry *)coverage_data->pdata[i];
-
-               ENTER_LOG (&coverage_statements_ctr, logbuffer,
-                       EVENT_SIZE /* event */ +
-                       LEB128_SIZE /* method id */ +
-                       LEB128_SIZE /* offset */ +
-                       LEB128_SIZE /* counter */ +
-                       LEB128_SIZE /* line */ +
-                       LEB128_SIZE /* column */
-               );
-
-               emit_event (logbuffer, TYPE_COVERAGE_STATEMENT | TYPE_COVERAGE);
-               emit_uvalue (logbuffer, method_id);
-               emit_uvalue (logbuffer, entry->offset);
-               emit_uvalue (logbuffer, entry->counter);
-               emit_uvalue (logbuffer, entry->line);
-               emit_uvalue (logbuffer, entry->column);
-
-               EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
-       }
-
-       method_id++;
-
-       g_free (class_name);
-
-       g_ptr_array_foreach (coverage_data, free_coverage_entry, NULL);
-       g_ptr_array_free (coverage_data, TRUE);
-       coverage_data = NULL;
-}
-
-/* This empties the queue */
-static guint
-count_queue (MonoLockFreeQueue *queue)
-{
-       MonoLockFreeQueueNode *node;
-       guint count = 0;
-
-       while ((node = mono_lock_free_queue_dequeue (queue))) {
-               count++;
-               mono_thread_hazardous_try_free (node, g_free);
-       }
-
-       return count;
-}
-
-static void
-build_class_buffer (gpointer key, gpointer value, gpointer userdata)
-{
-       MonoClass *klass = (MonoClass *)key;
-       MonoLockFreeQueue *class_methods = (MonoLockFreeQueue *)value;
-       MonoImage *image;
-       char *class_name;
-       const char *assembly_name;
-       int number_of_methods, partially_covered;
-       guint fully_covered;
-
-       image = mono_class_get_image (klass);
-       assembly_name = mono_image_get_name (image);
-       class_name = mono_type_get_name (mono_class_get_type (klass));
-
-       assembly_name = assembly_name ? assembly_name : "";
-       number_of_methods = mono_class_num_methods (klass);
-       fully_covered = count_queue (class_methods);
-       /* We don't handle partial covered yet */
-       partially_covered = 0;
-
-       ENTER_LOG (&coverage_classes_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               strlen (assembly_name) + 1 /* assembly name */ +
-               strlen (class_name) + 1 /* class name */ +
-               LEB128_SIZE /* no. methods */ +
-               LEB128_SIZE /* fully covered */ +
-               LEB128_SIZE /* partially covered */
-       );
-
-       emit_event (logbuffer, TYPE_COVERAGE_CLASS | TYPE_COVERAGE);
-       emit_string (logbuffer, assembly_name, strlen (assembly_name) + 1);
-       emit_string (logbuffer, class_name, strlen (class_name) + 1);
-       emit_uvalue (logbuffer, number_of_methods);
-       emit_uvalue (logbuffer, fully_covered);
-       emit_uvalue (logbuffer, partially_covered);
-
-       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
-
-       g_free (class_name);
-}
-
-static void
-get_coverage_for_image (MonoImage *image, int *number_of_methods, guint *fully_covered, int *partially_covered)
-{
-       MonoLockFreeQueue *image_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (image_to_methods, image);
-
-       *number_of_methods = mono_image_get_table_rows (image, MONO_TABLE_METHOD);
-       if (image_methods)
-               *fully_covered = count_queue (image_methods);
-       else
-               *fully_covered = 0;
-
-       // FIXME: We don't handle partially covered yet.
-       *partially_covered = 0;
-}
-
-static void
-build_assembly_buffer (gpointer key, gpointer value, gpointer userdata)
-{
-       MonoAssembly *assembly = (MonoAssembly *)value;
-       MonoImage *image = mono_assembly_get_image (assembly);
-       const char *name, *guid, *filename;
-       int number_of_methods = 0, partially_covered = 0;
-       guint fully_covered = 0;
-
-       name = mono_image_get_name (image);
-       guid = mono_image_get_guid (image);
-       filename = mono_image_get_filename (image);
-
-       name = name ? name : "";
-       guid = guid ? guid : "";
-       filename = filename ? filename : "";
-
-       get_coverage_for_image (image, &number_of_methods, &fully_covered, &partially_covered);
-
-       ENTER_LOG (&coverage_assemblies_ctr, logbuffer,
-               EVENT_SIZE /* event */ +
-               strlen (name) + 1 /* name */ +
-               strlen (guid) + 1 /* guid */ +
-               strlen (filename) + 1 /* file name */ +
-               LEB128_SIZE /* no. methods */ +
-               LEB128_SIZE /* fully covered */ +
-               LEB128_SIZE /* partially covered */
-       );
-
-       emit_event (logbuffer, TYPE_COVERAGE_ASSEMBLY | TYPE_COVERAGE);
-       emit_string (logbuffer, name, strlen (name) + 1);
-       emit_string (logbuffer, guid, strlen (guid) + 1);
-       emit_string (logbuffer, filename, strlen (filename) + 1);
-       emit_uvalue (logbuffer, number_of_methods);
-       emit_uvalue (logbuffer, fully_covered);
-       emit_uvalue (logbuffer, partially_covered);
-
-       EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
-}
-
-static void
-dump_coverage (MonoProfiler *prof)
-{
-       if (!coverage_initialized)
-               return;
-
-       COVERAGE_DEBUG(fprintf (stderr, "Coverage: Started dump\n");)
-       method_id = 0;
-
-       mono_os_mutex_lock (&coverage_mutex);
-       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);
-
-       COVERAGE_DEBUG(fprintf (stderr, "Coverage: Finished dump\n");)
-}
-
-static void
-process_method_enter_coverage (MonoProfiler *prof, MonoMethod *method)
-{
-       MonoClass *klass;
-       MonoImage *image;
-
-       if (!coverage_initialized)
-               return;
-
-       klass = mono_method_get_class (method);
-       image = mono_class_get_image (klass);
-
-       if (mono_conc_hashtable_lookup (suppressed_assemblies, (gpointer) mono_image_get_name (image)))
-               return;
-
-       mono_os_mutex_lock (&coverage_mutex);
-       mono_conc_hashtable_insert (entered_methods, method, method);
-       mono_os_mutex_unlock (&coverage_mutex);
-}
-
-static MonoLockFreeQueueNode *
-create_method_node (MonoMethod *method)
-{
-       MethodNode *node = (MethodNode *) g_malloc (sizeof (MethodNode));
-       mono_lock_free_queue_node_init ((MonoLockFreeQueueNode *) node, FALSE);
-       node->method = method;
-
-       return (MonoLockFreeQueueNode *) node;
-}
-
-static gboolean
-coverage_filter (MonoProfiler *prof, MonoMethod *method)
-{
-       MonoError error;
-       MonoClass *klass;
-       MonoImage *image;
-       MonoAssembly *assembly;
-       MonoMethodHeader *header;
-       guint32 iflags, flags, code_size;
-       char *fqn, *classname;
-       gboolean has_positive, found;
-       MonoLockFreeQueue *image_methods, *class_methods;
-       MonoLockFreeQueueNode *node;
-
-       g_assert (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));)
-
-       flags = mono_method_get_flags (method, &iflags);
-       if ((iflags & 0x1000 /*METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL*/) ||
-           (flags & 0x2000 /*METHOD_ATTRIBUTE_PINVOKE_IMPL*/)) {
-               COVERAGE_DEBUG(fprintf (stderr, "   Internal call or pinvoke - ignoring\n");)
-               return FALSE;
-       }
-
-       // Don't need to do anything else if we're already tracking this method
-       if (mono_conc_hashtable_lookup (coverage_methods, method)) {
-               COVERAGE_DEBUG(fprintf (stderr, "   Already tracking\n");)
-               return TRUE;
-       }
-
-       klass = mono_method_get_class (method);
-       image = mono_class_get_image (klass);
-
-       // Don't handle coverage for the core assemblies
-       if (mono_conc_hashtable_lookup (suppressed_assemblies, (gpointer) mono_image_get_name (image)) != NULL)
-               return FALSE;
-
-       if (prof->coverage_filters) {
-               /* Check already filtered classes first */
-               if (mono_conc_hashtable_lookup (filtered_classes, klass)) {
-                       COVERAGE_DEBUG(fprintf (stderr, "   Already filtered\n");)
-                       return FALSE;
-               }
-
-               classname = mono_type_get_name (mono_class_get_type (klass));
-
-               fqn = g_strdup_printf ("[%s]%s", mono_image_get_name (image), classname);
-
-               COVERAGE_DEBUG(fprintf (stderr, "   Looking for %s in filter\n", fqn);)
-               // Check positive filters first
-               has_positive = FALSE;
-               found = FALSE;
-               for (guint i = 0; i < prof->coverage_filters->len; ++i) {
-                       char *filter = (char *)g_ptr_array_index (prof->coverage_filters, i);
-
-                       if (filter [0] == '+') {
-                               filter = &filter [1];
-
-                               COVERAGE_DEBUG(fprintf (stderr, "   Checking against +%s ...", filter);)
-
-                               if (strstr (fqn, filter) != NULL) {
-                                       COVERAGE_DEBUG(fprintf (stderr, "matched\n");)
-                                       found = TRUE;
-                               } else
-                                       COVERAGE_DEBUG(fprintf (stderr, "no match\n");)
-
-                               has_positive = TRUE;
-                       }
-               }
-
-               if (has_positive && !found) {
-                       COVERAGE_DEBUG(fprintf (stderr, "   Positive match was not found\n");)
-
-                       mono_os_mutex_lock (&coverage_mutex);
-                       mono_conc_hashtable_insert (filtered_classes, klass, klass);
-                       mono_os_mutex_unlock (&coverage_mutex);
-                       g_free (fqn);
-                       g_free (classname);
-
-                       return FALSE;
-               }
-
-               for (guint i = 0; i < prof->coverage_filters->len; ++i) {
-                       // FIXME: Is substring search sufficient?
-                       char *filter = (char *)g_ptr_array_index (prof->coverage_filters, i);
-                       if (filter [0] == '+')
-                               continue;
-
-                       // Skip '-'
-                       filter = &filter [1];
-                       COVERAGE_DEBUG(fprintf (stderr, "   Checking against -%s ...", filter);)
-
-                       if (strstr (fqn, filter) != NULL) {
-                               COVERAGE_DEBUG(fprintf (stderr, "matched\n");)
-
-                               mono_os_mutex_lock (&coverage_mutex);
-                               mono_conc_hashtable_insert (filtered_classes, klass, klass);
-                               mono_os_mutex_unlock (&coverage_mutex);
-                               g_free (fqn);
-                               g_free (classname);
-
-                               return FALSE;
-                       } else
-                               COVERAGE_DEBUG(fprintf (stderr, "no match\n");)
-
-               }
-
-               g_free (fqn);
-               g_free (classname);
-       }
-
-       COVERAGE_DEBUG(fprintf (stderr, "   Handling coverage for %s\n", mono_method_get_name (method));)
-       header = mono_method_get_header_checked (method, &error);
-       mono_error_cleanup (&error);
-
-       mono_method_header_get_code (header, &code_size, NULL);
-
-       assembly = mono_image_get_assembly (image);
-
-       // Need to keep the assemblies around for as long as they are kept in the hashtable
-       // Nunit, for example, has a habit of unloading them before the coverage statistics are
-       // generated causing a crash. See https://bugzilla.xamarin.com/show_bug.cgi?id=39325
-       mono_assembly_addref (assembly);
-
-       mono_os_mutex_lock (&coverage_mutex);
-       mono_conc_hashtable_insert (coverage_methods, method, method);
-       mono_conc_hashtable_insert (coverage_assemblies, assembly, assembly);
-       mono_os_mutex_unlock (&coverage_mutex);
-
-       image_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (image_to_methods, image);
-
-       if (image_methods == NULL) {
-               image_methods = (MonoLockFreeQueue *) g_malloc (sizeof (MonoLockFreeQueue));
-               mono_lock_free_queue_init (image_methods);
-               mono_os_mutex_lock (&coverage_mutex);
-               mono_conc_hashtable_insert (image_to_methods, image, image_methods);
-               mono_os_mutex_unlock (&coverage_mutex);
-       }
-
-       node = create_method_node (method);
-       mono_lock_free_queue_enqueue (image_methods, node);
-
-       class_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (coverage_classes, klass);
-
-       if (class_methods == NULL) {
-               class_methods = (MonoLockFreeQueue *) g_malloc (sizeof (MonoLockFreeQueue));
-               mono_lock_free_queue_init (class_methods);
-               mono_os_mutex_lock (&coverage_mutex);
-               mono_conc_hashtable_insert (coverage_classes, klass, class_methods);
-               mono_os_mutex_unlock (&coverage_mutex);
-       }
-
-       node = create_method_node (method);
-       mono_lock_free_queue_enqueue (class_methods, node);
-
-       return TRUE;
-}
-
-#define LINE_BUFFER_SIZE 4096
-/* Max file limit of 128KB */
-#define MAX_FILE_SIZE 128 * 1024
-static char *
-get_file_content (FILE *stream)
-{
-       char *buffer;
-       ssize_t bytes_read;
-       long filesize;
-       int res, offset = 0;
-
-       res = fseek (stream, 0, SEEK_END);
-       if (res < 0)
-         return NULL;
-
-       filesize = ftell (stream);
-       if (filesize < 0)
-         return NULL;
-
-       res = fseek (stream, 0, SEEK_SET);
-       if (res < 0)
-         return NULL;
-
-       if (filesize > MAX_FILE_SIZE)
-         return NULL;
-
-       buffer = (char *) g_malloc ((filesize + 1) * sizeof (char));
-       while ((bytes_read = fread (buffer + offset, 1, LINE_BUFFER_SIZE, stream)) > 0)
-               offset += bytes_read;
-
-       /* NULL terminate our buffer */
-       buffer[filesize] = '\0';
-       return buffer;
-}
-
-static char *
-get_next_line (char *contents, char **next_start)
-{
-       char *p = contents;
-
-       if (p == NULL || *p == '\0') {
-               *next_start = NULL;
-               return NULL;
-       }
-
-       while (*p != '\n' && *p != '\0')
-               p++;
-
-       if (*p == '\n') {
-               *p = '\0';
-               *next_start = p + 1;
-       } else
-               *next_start = NULL;
-
-       return contents;
-}
-
-static void
-init_suppressed_assemblies (void)
-{
-       char *content;
-       char *line;
-       FILE *sa_file;
-
-       suppressed_assemblies = mono_conc_hashtable_new (g_str_hash, g_str_equal);
-       sa_file = fopen (SUPPRESSION_DIR "/mono-profiler-log.suppression", "r");
-       if (sa_file == NULL)
-               return;
-
-       /* Don't need to free @content as it is referred to by the lines stored in @suppressed_assemblies */
-       content = get_file_content (sa_file);
-       if (content == NULL) {
-               g_error ("mono-profiler-log.suppression is greater than 128kb - aborting\n");
-       }
-
-       while ((line = get_next_line (content, &content))) {
-               line = g_strchomp (g_strchug (line));
-               /* No locking needed as we're doing initialization */
-               mono_conc_hashtable_insert (suppressed_assemblies, line, line);
-       }
-
-       fclose (sa_file);
-}
-
-static void
-parse_cov_filter_file (GPtrArray *filters, const char *file)
-{
-       FILE *filter_file;
-       char *line, *content;
-
-       filter_file = fopen (file, "r");
-       if (filter_file == NULL) {
-               fprintf (stderr, "Unable to open %s\n", file);
-               return;
-       }
-
-       /* Don't need to free content as it is referred to by the lines stored in @filters */
-       content = get_file_content (filter_file);
-       if (content == NULL)
-               fprintf (stderr, "WARNING: %s is greater than 128kb - ignoring\n", file);
-
-       while ((line = get_next_line (content, &content)))
-               g_ptr_array_add (filters, g_strchug (g_strchomp (line)));
-
-       fclose (filter_file);
-}
-
-static void
-coverage_init (MonoProfiler *prof)
-{
-       g_assert (!coverage_initialized && "Why are we initializing coverage twice?");
-
-       COVERAGE_DEBUG(fprintf (stderr, "Coverage initialized\n");)
-
-       mono_os_mutex_init (&coverage_mutex);
-       coverage_methods = mono_conc_hashtable_new (NULL, NULL);
-       coverage_assemblies = mono_conc_hashtable_new (NULL, NULL);
-       coverage_classes = mono_conc_hashtable_new (NULL, NULL);
-       filtered_classes = mono_conc_hashtable_new (NULL, NULL);
-       entered_methods = mono_conc_hashtable_new (NULL, NULL);
-       image_to_methods = mono_conc_hashtable_new (NULL, NULL);
-       init_suppressed_assemblies ();
-
-       coverage_initialized = TRUE;
-}
-
-static void
-unref_coverage_assemblies (gpointer key, gpointer value, gpointer userdata)
-{
-       MonoAssembly *assembly = (MonoAssembly *)value;
-       mono_assembly_close (assembly);
-}
-
-static void
-free_sample_hit (gpointer p)
-{
-       mono_lock_free_free (p, SAMPLE_BLOCK_SIZE);
-}
-
-static void
-cleanup_reusable_samples (MonoProfiler *prof)
-{
-       SampleHit *sample;
-
-       while ((sample = (SampleHit *) mono_lock_free_queue_dequeue (&prof->sample_reuse_queue)))
-               mono_thread_hazardous_try_free (sample, free_sample_hit);
-}
-
-static void
-log_shutdown (MonoProfiler *prof)
-{
-       InterlockedWrite (&in_shutdown, 1);
-
-       if (!no_counters)
-               counters_and_perfcounters_sample (prof);
-
-       dump_coverage (prof);
-
-       char c = 1;
-
-       if (write (prof->pipes [1], &c, 1) != 1) {
-               fprintf (stderr, "Could not write to pipe: %s\n", strerror (errno));
-               exit (1);
-       }
-
-       mono_native_thread_join (prof->helper_thread);
-
-       mono_os_mutex_destroy (&counters_mutex);
-
-       MonoCounterAgent *mc_next;
-
-       for (MonoCounterAgent *cur = counters; cur; cur = mc_next) {
-               mc_next = cur->next;
-               g_free (cur);
-       }
-
-       PerfCounterAgent *pc_next;
-
-       for (PerfCounterAgent *cur = perfcounters; cur; cur = pc_next) {
-               pc_next = cur->next;
-               g_free (cur);
-       }
-
-       /*
-        * Ensure that we empty the LLS completely, even if some nodes are
-        * not immediately removed upon calling mono_lls_remove (), by
-        * iterating until the head is NULL.
-        */
-       while (profiler_thread_list.head) {
-               MONO_LLS_FOREACH_SAFE (&profiler_thread_list, MonoProfilerThread, thread) {
-                       g_assert (thread->attached && "Why is a thread in the LLS not attached?");
-
-                       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);
-       mono_native_thread_join (prof->dumper_thread);
-       mono_os_sem_destroy (&prof->dumper_queue_sem);
-
-       InterlockedWrite (&prof->run_writer_thread, 0);
-       mono_os_sem_post (&prof->writer_queue_sem);
-       mono_native_thread_join (prof->writer_thread);
-       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);
-
-       /*
-        * 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 ();
-
-       g_assert (!InterlockedRead (&buffer_rwlock_count) && "Why is the reader count still non-zero?");
-       g_assert (!InterlockedReadPointer (&buffer_rwlock_exclusive) && "Why does someone still hold the exclusive lock?");
-
-#if defined (HAVE_SYS_ZLIB)
-       if (prof->gzfile)
-               gzclose (prof->gzfile);
-#endif
-       if (prof->pipe_output)
-               pclose (prof->file);
-       else
-               fclose (prof->file);
-
-       mono_conc_hashtable_destroy (prof->method_table);
-       mono_os_mutex_destroy (&prof->method_table_mutex);
-
-       if (coverage_initialized) {
-               mono_os_mutex_lock (&coverage_mutex);
-               mono_conc_hashtable_foreach (coverage_assemblies, unref_coverage_assemblies, prof);
-               mono_os_mutex_unlock (&coverage_mutex);
-
-               mono_conc_hashtable_destroy (coverage_methods);
-               mono_conc_hashtable_destroy (coverage_assemblies);
-               mono_conc_hashtable_destroy (coverage_classes);
-               mono_conc_hashtable_destroy (filtered_classes);
-
-               mono_conc_hashtable_destroy (entered_methods);
-               mono_conc_hashtable_destroy (image_to_methods);
-               mono_conc_hashtable_destroy (suppressed_assemblies);
-               mono_os_mutex_destroy (&coverage_mutex);
-       }
-
-       PROF_TLS_FREE ();
-
-       g_free (prof->args);
-       g_free (prof);
-}
-
-static char*
-new_filename (const char* filename)
-{
-       time_t t = time (NULL);
-       int pid = process_id ();
-       char pid_buf [16];
-       char time_buf [16];
-       char *res, *d;
-       const char *p;
-       int count_dates = 0;
-       int count_pids = 0;
-       int s_date, s_pid;
-       struct tm *ts;
-       for (p = filename; *p; p++) {
-               if (*p != '%')
-                       continue;
-               p++;
-               if (*p == 't')
-                       count_dates++;
-               else if (*p == 'p')
-                       count_pids++;
-               else if (*p == 0)
-                       break;
-       }
-       if (!count_dates && !count_pids)
-               return pstrdup (filename);
-       snprintf (pid_buf, sizeof (pid_buf), "%d", pid);
-       ts = gmtime (&t);
-       snprintf (time_buf, sizeof (time_buf), "%d%02d%02d%02d%02d%02d",
-               1900 + ts->tm_year, 1 + ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec);
-       s_date = strlen (time_buf);
-       s_pid = strlen (pid_buf);
-       d = res = (char *) g_malloc (strlen (filename) + s_date * count_dates + s_pid * count_pids);
-       for (p = filename; *p; p++) {
-               if (*p != '%') {
-                       *d++ = *p;
-                       continue;
-               }
-               p++;
-               if (*p == 't') {
-                       strcpy (d, time_buf);
-                       d += s_date;
-                       continue;
-               } else if (*p == 'p') {
-                       strcpy (d, pid_buf);
-                       d += s_pid;
-                       continue;
-               } else if (*p == '%') {
-                       *d++ = '%';
-                       continue;
-               } else if (*p == 0)
-                       break;
-               *d++ = '%';
-               *d++ = *p;
-       }
-       *d = 0;
-       return res;
-}
-
-static void
-add_to_fd_set (fd_set *set, int fd, int *max_fd)
-{
-       /*
-        * This should only trigger for the basic FDs (server socket, pipes) at
-        * startup if for some mysterious reason they're too large. In this case,
-        * the profiler really can't function, and we're better off printing an
-        * error and exiting.
-        */
-       if (fd >= FD_SETSIZE) {
-               fprintf (stderr, "File descriptor is out of bounds for fd_set: %d\n", fd);
-               exit (1);
-       }
-
-       FD_SET (fd, set);
-
-       if (*max_fd < fd)
-               *max_fd = fd;
-}
-
-static void *
-helper_thread (void *arg)
-{
-       MonoProfiler *prof = (MonoProfiler *) arg;
-
-       mono_threads_attach_tools_thread ();
-       mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler helper");
-
-       MonoProfilerThread *thread = init_thread (prof, FALSE);
-
-       GArray *command_sockets = g_array_new (FALSE, FALSE, sizeof (int));
-
-       while (1) {
-               fd_set rfds;
-               int max_fd = -1;
-
-               FD_ZERO (&rfds);
-
-               add_to_fd_set (&rfds, prof->server_socket, &max_fd);
-               add_to_fd_set (&rfds, prof->pipes [0], &max_fd);
-
-               for (gint i = 0; i < command_sockets->len; i++)
-                       add_to_fd_set (&rfds, g_array_index (command_sockets, int, i), &max_fd);
-
-               struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
-
-               // Sleep for 1sec or until a file descriptor has data.
-               if (select (max_fd + 1, &rfds, NULL, NULL, &tv) == -1) {
-                       if (errno == EINTR)
-                               continue;
-
-                       fprintf (stderr, "Error in mono-profiler-log server: %s", strerror (errno));
-                       exit (1);
-               }
-
-               if (!no_counters)
-                       counters_and_perfcounters_sample (prof);
-
-               buffer_lock_excl ();
-
-               sync_point (SYNC_POINT_PERIODIC);
-
-               buffer_unlock_excl ();
-
-               // Are we shutting down?
-               if (FD_ISSET (prof->pipes [0], &rfds)) {
-                       char c;
-                       read (prof->pipes [0], &c, 1);
-                       break;
-               }
-
-               for (gint i = 0; i < command_sockets->len; i++) {
-                       int fd = g_array_index (command_sockets, int, i);
-
-                       if (!FD_ISSET (fd, &rfds))
-                               continue;
-
-                       char buf [64];
-                       int len = read (fd, buf, sizeof (buf) - 1);
-
-                       if (len == -1)
-                               continue;
-
-                       if (!len) {
-                               // The other end disconnected.
-                               g_array_remove_index (command_sockets, i);
-                               close (fd);
-
-                               continue;
-                       }
-
-                       buf [len] = 0;
-
-                       if (!strcmp (buf, "heapshot\n") && hs_mode_ondemand) {
-                               // Rely on the finalization callbacks invoking process_requests ().
-                               heapshot_requested = 1;
-                               mono_gc_finalize_notify ();
-                       }
-               }
-
-               if (FD_ISSET (prof->server_socket, &rfds)) {
-                       int fd = accept (prof->server_socket, NULL, NULL);
-
-                       if (fd != -1) {
-                               if (fd >= FD_SETSIZE)
-                                       close (fd);
-                               else
-                                       g_array_append_val (command_sockets, fd);
-                       }
-               }
-       }
-
-       for (gint i = 0; i < command_sockets->len; i++)
-               close (g_array_index (command_sockets, int, i));
-
-       g_array_free (command_sockets, TRUE);
-
-       send_log_unsafe (FALSE);
-       deinit_thread (thread);
-
-       mono_thread_info_detach ();
-
-       return NULL;
-}
-
-static void
-start_helper_thread (MonoProfiler* prof)
-{
-       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 == -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)) == -1) {
-               fprintf (stderr, "Cannot bind server socket on port %d: %s\n", prof->command_port, strerror (errno));
-               close (prof->server_socket);
-               exit (1);
-       }
-
-       if (listen (prof->server_socket, 1) == -1) {
-               fprintf (stderr, "Cannot listen on server socket: %s\n", strerror (errno));
-               close (prof->server_socket);
-               exit (1);
-       }
-
-       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);
-       }
-
-       prof->command_port = ntohs (server_address.sin_port);
-
-       if (!mono_native_thread_create (&prof->helper_thread, helper_thread, prof)) {
-               fprintf (stderr, "Could not start helper thread\n");
-               close (prof->server_socket);
-               exit (1);
-       }
-}
-
-static void
-free_writer_entry (gpointer p)
-{
-       mono_lock_free_free (p, WRITER_ENTRY_BLOCK_SIZE);
-}
-
-static gboolean
-handle_writer_queue_entry (MonoProfiler *prof)
-{
-       WriterQueueEntry *entry;
-
-       if ((entry = (WriterQueueEntry *) mono_lock_free_queue_dequeue (&prof->writer_queue))) {
-               if (!entry->methods)
-                       goto no_methods;
-
-               gboolean wrote_methods = FALSE;
-
-               /*
-                * 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.
-                *
-                * We use a 'proper' thread-local buffer for this as opposed
-                * to allocating and freeing a buffer by hand because the call
-                * to mono_method_full_name () below may trigger class load
-                * events when it retrieves the signature of the method. So a
-                * thread-local buffer needs to exist when such events occur.
-                */
-               for (guint i = 0; i < entry->methods->len; i++) {
-                       MethodInfo *info = (MethodInfo *) g_ptr_array_index (entry->methods, i);
-
-                       if (mono_conc_hashtable_lookup (prof->method_table, info->method))
-                               goto free_info; // This method already has metadata emitted.
-
-                       /*
-                        * 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_os_mutex_lock (&prof->method_table_mutex);
-                       mono_conc_hashtable_insert (prof->method_table, info->method, info->method);
-                       mono_os_mutex_unlock (&prof->method_table_mutex);
-
-                       char *name = mono_method_full_name (info->method, 1);
-                       int nlen = strlen (name) + 1;
-                       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;
-
-                       ENTER_LOG (&method_jits_ctr, logbuffer,
-                               EVENT_SIZE /* event */ +
-                               LEB128_SIZE /* method */ +
-                               LEB128_SIZE /* start */ +
-                               LEB128_SIZE /* size */ +
-                               nlen /* name */
-                       );
-
-                       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;
-
-                       EXIT_LOG_EXPLICIT (NO_SEND, NO_REQUESTS);
-
-                       mono_free (name);
-
-                       wrote_methods = TRUE;
-
-               free_info:
-                       g_free (info);
-               }
-
-               g_ptr_array_free (entry->methods, TRUE);
-
-               if (wrote_methods) {
-                       dump_buffer_threadless (prof, PROF_TLS_GET ()->buffer);
-                       init_buffer_state (PROF_TLS_GET ());
-               }
-
-       no_methods:
-               dump_buffer (prof, entry->buffer);
-
-               mono_thread_hazardous_try_free (entry, free_writer_entry);
-
-               return TRUE;
-       }
-
-       return FALSE;
-}
-
-static void *
-writer_thread (void *arg)
-{
-       MonoProfiler *prof = (MonoProfiler *)arg;
-
-       mono_threads_attach_tools_thread ();
-       mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler writer");
-
-       dump_header (prof);
-
-       MonoProfilerThread *thread = init_thread (prof, FALSE);
-
-       while (InterlockedRead (&prof->run_writer_thread)) {
-               mono_os_sem_wait (&prof->writer_queue_sem, MONO_SEM_FLAGS_NONE);
-               handle_writer_queue_entry (prof);
-       }
-
-       /* Drain any remaining entries on shutdown. */
-       while (handle_writer_queue_entry (prof));
-
-       free_buffer (thread->buffer, thread->buffer->size);
-       deinit_thread (thread);
-
-       mono_thread_info_detach ();
-
-       return NULL;
-}
-
-static void
-start_writer_thread (MonoProfiler* prof)
-{
-       InterlockedWrite (&prof->run_writer_thread, 1);
-
-       if (!mono_native_thread_create (&prof->writer_thread, writer_thread, prof)) {
-               fprintf (stderr, "Could not start writer thread\n");
-               exit (1);
-       }
-}
-
-static void
-reuse_sample_hit (gpointer p)
-{
-       SampleHit *sample = p;
-
-       mono_lock_free_queue_node_unpoison (&sample->node);
-       mono_lock_free_queue_enqueue (&sample->prof->sample_reuse_queue, &sample->node);
-}
-
-static gboolean
-handle_dumper_queue_entry (MonoProfiler *prof)
-{
-       SampleHit *sample;
-
-       if ((sample = (SampleHit *) mono_lock_free_queue_dequeue (&prof->dumper_queue))) {
-               for (int i = 0; i < sample->count; ++i) {
-                       MonoMethod *method = sample->frames [i].method;
-                       MonoDomain *domain = sample->frames [i].domain;
-                       void *address = sample->frames [i].base_address;
-
-                       if (!method) {
-                               g_assert (domain && "What happened to the domain pointer?");
-                               g_assert (address && "What happened to the instruction pointer?");
-
-                               MonoJitInfo *ji = mono_jit_info_table_find (domain, (char *) address);
-
-                               if (ji)
-                                       sample->frames [i].method = mono_jit_info_get_method (ji);
-                       }
-               }
-
-               ENTER_LOG (&sample_hits_ctr, logbuffer,
-                       EVENT_SIZE /* event */ +
-                       BYTE_SIZE /* type */ +
-                       LEB128_SIZE /* tid */ +
-                       LEB128_SIZE /* count */ +
-                       1 * (
-                               LEB128_SIZE /* ip */
-                       ) +
-                       LEB128_SIZE /* managed count */ +
-                       sample->count * (
-                               LEB128_SIZE /* method */
-                       )
-               );
-
-               emit_event_time (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_HIT, sample->time);
-               emit_byte (logbuffer, SAMPLE_CYCLES);
-               emit_ptr (logbuffer, (void *) sample->tid);
-               emit_value (logbuffer, 1);
-
-               // TODO: Actual native unwinding.
-               for (int i = 0; i < 1; ++i) {
-                       emit_ptr (logbuffer, sample->ip);
-                       add_code_pointer ((uintptr_t) sample->ip);
-               }
-
-               /* new in data version 6 */
-               emit_uvalue (logbuffer, sample->count);
-
-               for (int i = 0; i < sample->count; ++i)
-                       emit_method (logbuffer, sample->frames [i].method);
-
-               EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
-
-               mono_thread_hazardous_try_free (sample, reuse_sample_hit);
-
-               dump_unmanaged_coderefs (prof);
-       }
-
-       return FALSE;
-}
-
-static void *
-dumper_thread (void *arg)
-{
-       MonoProfiler *prof = (MonoProfiler *)arg;
-
-       mono_threads_attach_tools_thread ();
-       mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler dumper");
-
-       MonoProfilerThread *thread = init_thread (prof, FALSE);
-
-       while (InterlockedRead (&prof->run_dumper_thread)) {
-               mono_os_sem_wait (&prof->dumper_queue_sem, MONO_SEM_FLAGS_NONE);
-               handle_dumper_queue_entry (prof);
-       }
-
-       /* Drain any remaining entries on shutdown. */
-       while (handle_dumper_queue_entry (prof));
-
-       send_log_unsafe (FALSE);
-       deinit_thread (thread);
-
-       mono_thread_info_detach ();
-
-       return NULL;
-}
-
-static void
-start_dumper_thread (MonoProfiler* prof)
-{
-       InterlockedWrite (&prof->run_dumper_thread, 1);
-
-       if (!mono_native_thread_create (&prof->dumper_thread, dumper_thread, prof)) {
-               fprintf (stderr, "Could not start dumper thread\n");
-               exit (1);
-       }
-}
-
-static void
-register_counter (const char *name, gint32 *counter)
-{
-       mono_counters_register (name, MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, counter);
-}
-
-static void
-runtime_initialized (MonoProfiler *profiler)
-{
-       InterlockedWrite (&runtime_inited, 1);
-
-       register_counter ("Sample events allocated", &sample_allocations_ctr);
-       register_counter ("Log buffers allocated", &buffer_allocations_ctr);
-
-       register_counter ("Event: Sync points", &sync_points_ctr);
-       register_counter ("Event: Heap objects", &heap_objects_ctr);
-       register_counter ("Event: Heap starts", &heap_starts_ctr);
-       register_counter ("Event: Heap ends", &heap_ends_ctr);
-       register_counter ("Event: Heap roots", &heap_roots_ctr);
-       register_counter ("Event: GC events", &gc_events_ctr);
-       register_counter ("Event: GC resizes", &gc_resizes_ctr);
-       register_counter ("Event: GC allocations", &gc_allocs_ctr);
-       register_counter ("Event: GC moves", &gc_moves_ctr);
-       register_counter ("Event: GC handle creations", &gc_handle_creations_ctr);
-       register_counter ("Event: GC handle deletions", &gc_handle_deletions_ctr);
-       register_counter ("Event: GC finalize starts", &finalize_begins_ctr);
-       register_counter ("Event: GC finalize ends", &finalize_ends_ctr);
-       register_counter ("Event: GC finalize object starts", &finalize_object_begins_ctr);
-       register_counter ("Event: GC finalize object ends", &finalize_object_ends_ctr);
-       register_counter ("Event: Image loads", &image_loads_ctr);
-       register_counter ("Event: Image unloads", &image_unloads_ctr);
-       register_counter ("Event: Assembly loads", &assembly_loads_ctr);
-       register_counter ("Event: Assembly unloads", &assembly_unloads_ctr);
-       register_counter ("Event: Class loads", &class_loads_ctr);
-       register_counter ("Event: Class unloads", &class_unloads_ctr);
-       register_counter ("Event: Method entries", &method_entries_ctr);
-       register_counter ("Event: Method exits", &method_exits_ctr);
-       register_counter ("Event: Method exception leaves", &method_exception_exits_ctr);
-       register_counter ("Event: Method JITs", &method_jits_ctr);
-       register_counter ("Event: Code buffers", &code_buffers_ctr);
-       register_counter ("Event: Exception throws", &exception_throws_ctr);
-       register_counter ("Event: Exception clauses", &exception_clauses_ctr);
-       register_counter ("Event: Monitor contentions", &monitor_contentions_ctr);
-       register_counter ("Event: Monitor acquisitions", &monitor_acquisitions_ctr);
-       register_counter ("Event: Monitor failures", &monitor_failures_ctr);
-       register_counter ("Event: Thread starts", &thread_starts_ctr);
-       register_counter ("Event: Thread ends", &thread_ends_ctr);
-       register_counter ("Event: Thread names", &thread_names_ctr);
-       register_counter ("Event: Domain loads", &domain_loads_ctr);
-       register_counter ("Event: Domain unloads", &domain_unloads_ctr);
-       register_counter ("Event: Domain names", &domain_names_ctr);
-       register_counter ("Event: Context loads", &context_loads_ctr);
-       register_counter ("Event: Context unloads", &context_unloads_ctr);
-       register_counter ("Event: Sample binaries", &sample_ubins_ctr);
-       register_counter ("Event: Sample symbols", &sample_usyms_ctr);
-       register_counter ("Event: Sample hits", &sample_hits_ctr);
-       register_counter ("Event: Counter descriptors", &counter_descriptors_ctr);
-       register_counter ("Event: Counter samples", &counter_samples_ctr);
-       register_counter ("Event: Performance counter descriptors", &perfcounter_descriptors_ctr);
-       register_counter ("Event: Performance counter samples", &perfcounter_samples_ctr);
-       register_counter ("Event: Coverage methods", &coverage_methods_ctr);
-       register_counter ("Event: Coverage statements", &coverage_statements_ctr);
-       register_counter ("Event: Coverage classes", &coverage_classes_ctr);
-       register_counter ("Event: Coverage assemblies", &coverage_assemblies_ctr);
-
-       counters_init (profiler);
-
-       /*
-        * We must start the helper thread before the writer thread. This is
-        * because the helper thread sets up the command port which is written to
-        * the log header by the writer thread.
-        */
-       start_helper_thread (profiler);
-       start_writer_thread (profiler);
-       start_dumper_thread (profiler);
-}
-
-static MonoProfiler*
-create_profiler (const char *args, const char *filename, GPtrArray *filters)
-{
-       MonoProfiler *prof;
-       char *nf;
-       int force_delete = 0;
-       prof = (MonoProfiler *) g_calloc (1, sizeof (MonoProfiler));
-
-       prof->args = pstrdup (args);
-       prof->command_port = command_port;
-       if (filename && *filename == '-') {
-               force_delete = 1;
-               filename++;
-               g_warning ("WARNING: the output:-FILENAME option is deprecated, the profiler now always overrides the output file\n");
-       }
-       if (!filename) {
-               if (do_report)
-                       filename = "|mprof-report -";
-               else
-                       filename = "output.mlpd";
-               nf = (char*)filename;
-       } else {
-               nf = new_filename (filename);
-               if (do_report) {
-                       int s = strlen (nf) + 32;
-                       char *p = (char *) g_malloc (s);
-                       snprintf (p, s, "|mprof-report '--out=%s' -", nf);
-                       g_free (nf);
-                       nf = p;
-               }
-       }
-       if (*nf == '|') {
-               prof->file = popen (nf + 1, "w");
-               prof->pipe_output = 1;
-       } else if (*nf == '#') {
-               int fd = strtol (nf + 1, NULL, 10);
-               prof->file = fdopen (fd, "a");
-       } else {
-               if (force_delete)
-                       unlink (nf);
-               prof->file = fopen (nf, "wb");
-       }
-       if (!prof->file) {
-               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 you hit this assert while increasing MAX_FRAMES, you need to increase
-        * SAMPLE_BLOCK_SIZE as well.
-        */
-       g_assert (SAMPLE_SLOT_SIZE (MAX_FRAMES) * 2 < LOCK_FREE_ALLOC_SB_USABLE_SIZE (SAMPLE_BLOCK_SIZE));
-
-       // FIXME: We should free this stuff too.
-       mono_lock_free_allocator_init_size_class (&prof->sample_size_class, SAMPLE_SLOT_SIZE (num_frames), SAMPLE_BLOCK_SIZE);
-       mono_lock_free_allocator_init_allocator (&prof->sample_allocator, &prof->sample_size_class, MONO_MEM_ACCOUNT_PROFILER);
-
-       mono_lock_free_queue_init (&prof->sample_reuse_queue);
-
-       g_assert (sizeof (WriterQueueEntry) * 2 < LOCK_FREE_ALLOC_SB_USABLE_SIZE (WRITER_ENTRY_BLOCK_SIZE));
-
-       // FIXME: We should free this stuff too.
-       mono_lock_free_allocator_init_size_class (&prof->writer_entry_size_class, sizeof (WriterQueueEntry), WRITER_ENTRY_BLOCK_SIZE);
-       mono_lock_free_allocator_init_allocator (&prof->writer_entry_allocator, &prof->writer_entry_size_class, MONO_MEM_ACCOUNT_PROFILER);
-
-       mono_lock_free_queue_init (&prof->writer_queue);
-       mono_os_sem_init (&prof->writer_queue_sem, 0);
-
-       mono_lock_free_queue_init (&prof->dumper_queue);
-       mono_os_sem_init (&prof->dumper_queue_sem, 0);
-
-       mono_os_mutex_init (&prof->method_table_mutex);
-       prof->method_table = mono_conc_hashtable_new (NULL, NULL);
-
-       if (do_coverage)
-               coverage_init (prof);
-       prof->coverage_filters = filters;
-
-       prof->startup_time = current_time ();
-       return prof;
-}
-
-/*
- * declaration to silence the compiler: this is the entry point that
- * mono will load from the shared library and call.
- */
-extern void
-mono_profiler_startup (const char *desc);
-
-extern void
-mono_profiler_startup_log (const char *desc);
-
-/*
- * this is the entry point that will be used when the profiler
- * is embedded inside the main executable.
- */
-void
-mono_profiler_startup_log (const char *desc)
-{
-       mono_profiler_startup (desc);
-}
-
-static ProfilerConfig config;
-
-void
-mono_profiler_startup (const char *desc)
-{
-       GPtrArray *filters = NULL;
-       MonoProfiler *prof;
-
-       proflog_parse_args (&config, desc [3] == ':' ? desc + 4 : "");
-
-       //XXX maybe later cleanup to use config directly
-       nocalls = !(config.effective_mask & PROFLOG_CALL_EVENTS);
-       no_counters = !(config.effective_mask & PROFLOG_COUNTER_EVENTS);
-       do_report = config.do_report;
-       do_debug = config.do_debug;
-       do_heap_shot = (config.effective_mask & PROFLOG_HEAPSHOT_FEATURE);
-       hs_mode_ondemand = config.hs_mode_ondemand;
-       hs_mode_ms = config.hs_mode_ms;
-       hs_mode_gc = config.hs_mode_gc;
-       do_mono_sample = (config.effective_mask & PROFLOG_SAMPLING_FEATURE);
-       use_zip = config.use_zip;
-       command_port = config.command_port;
-       num_frames = config.num_frames;
-       notraces = config.notraces;
-       max_allocated_sample_hits = config.max_allocated_sample_hits;
-       max_call_depth = config.max_call_depth;
-       do_coverage = (config.effective_mask & PROFLOG_CODE_COV_FEATURE);
-       debug_coverage = config.debug_coverage;
-       only_coverage = config.only_coverage;
-
-       if (config.cov_filter_files) {
-               filters = g_ptr_array_new ();
-               int i;
-               for (i = 0; i < config.cov_filter_files->len; ++i) {
-                       const char *name = config.cov_filter_files->pdata [i];
-                       parse_cov_filter_file (filters, name);
-               }
-       }
-
-       init_time ();
-
-       PROF_TLS_INIT ();
-
-       prof = create_profiler (desc, config.output_filename, filters);
-       if (!prof) {
-               PROF_TLS_FREE ();
-               return;
-       }
-
-       mono_lls_init (&profiler_thread_list, NULL);
-
-       init_thread (prof, TRUE);
-
-       //This two events are required for the profiler to work
-       int events = MONO_PROFILE_THREADS | MONO_PROFILE_GC;
-
-       //Required callbacks
-       mono_profiler_install (prof, log_shutdown);
-       mono_profiler_install_runtime_initialized (runtime_initialized);
-
-       mono_profiler_install_gc (gc_event, gc_resize);
-       mono_profiler_install_thread (thread_start, thread_end);
-
-       //It's questionable whether we actually want this to be mandatory, maybe put it behind the actual event?
-       mono_profiler_install_thread_name (thread_name);
-
-
-       if (config.effective_mask & PROFLOG_DOMAIN_EVENTS) {
-               events |= MONO_PROFILE_APPDOMAIN_EVENTS;
-               mono_profiler_install_appdomain (NULL, domain_loaded, domain_unloaded, NULL);
-               mono_profiler_install_appdomain_name (domain_name);
-       }
-
-       if (config.effective_mask & PROFLOG_ASSEMBLY_EVENTS) {
-               events |= MONO_PROFILE_ASSEMBLY_EVENTS;
-               mono_profiler_install_assembly (NULL, assembly_loaded, assembly_unloaded, NULL);
-       }
-
-       if (config.effective_mask & PROFLOG_MODULE_EVENTS) {
-               events |= MONO_PROFILE_MODULE_EVENTS;
-               mono_profiler_install_module (NULL, image_loaded, image_unloaded, NULL);
-       }
-
-       if (config.effective_mask & PROFLOG_CLASS_EVENTS) {
-               events |= MONO_PROFILE_CLASS_EVENTS;
-               mono_profiler_install_class (NULL, class_loaded, class_unloaded, NULL);
-       }
-
-       if (config.effective_mask & PROFLOG_JIT_COMPILATION_EVENTS) {
-               events |= MONO_PROFILE_JIT_COMPILATION;
-               mono_profiler_install_jit_end (method_jitted);
-               mono_profiler_install_code_buffer_new (code_buffer_new);
-       }
-
-       if (config.effective_mask & PROFLOG_EXCEPTION_EVENTS) {
-               events |= MONO_PROFILE_EXCEPTIONS;
-               mono_profiler_install_exception (throw_exc, method_exc_leave, clause_exc);
-       }
-
-       if (config.effective_mask & PROFLOG_ALLOCATION_EVENTS) {
-               events |= MONO_PROFILE_ALLOCATIONS;
-               mono_profiler_install_allocation (gc_alloc);
-       }
-
-       //PROFLOG_GC_EVENTS is mandatory
-       //PROFLOG_THREAD_EVENTS is mandatory
-
-       if (config.effective_mask & PROFLOG_CALL_EVENTS) {
-               events |= MONO_PROFILE_ENTER_LEAVE;
-               mono_profiler_install_enter_leave (method_enter, method_leave);
-       }
-
-       if (config.effective_mask & PROFLOG_INS_COVERAGE_EVENTS) {
-               events |= MONO_PROFILE_INS_COVERAGE;
-               mono_profiler_install_coverage_filter (coverage_filter);
-       }
-
-       //XXX should we check for PROFLOG_SAMPLING_FEATURE instead??
-       if (config.effective_mask & PROFLOG_SAMPLING_EVENTS) {
-               events |= MONO_PROFILE_STATISTICAL;
-               mono_profiler_set_statistical_mode (config.sampling_mode, config.sample_freq);
-               mono_profiler_install_statistical (mono_sample_hit);
-       }
-
-       if (config.effective_mask & PROFLOG_MONITOR_EVENTS) {
-               events |= MONO_PROFILE_MONITOR_EVENTS;
-               mono_profiler_install_monitor (monitor_event);
-       }
-
-       if (config.effective_mask & PROFLOG_GC_MOVES_EVENTS) {
-               events |= MONO_PROFILE_GC_MOVES;
-               mono_profiler_install_gc_moves (gc_moves);
-       }
-
-       // TODO split those in two profiler events
-       if (config.effective_mask & (PROFLOG_GC_ROOT_EVENTS | PROFLOG_GC_HANDLE_EVENTS)) {
-               events |= MONO_PROFILE_GC_ROOTS;
-               mono_profiler_install_gc_roots (
-                       config.effective_mask & (PROFLOG_GC_HANDLE_EVENTS) ? gc_handle : NULL,
-                       (config.effective_mask & PROFLOG_GC_ROOT_EVENTS) ? gc_roots : NULL);
-       }
-
-       if (config.effective_mask & PROFLOG_CONTEXT_EVENTS) {
-               events |= MONO_PROFILE_CONTEXT_EVENTS;
-               mono_profiler_install_context (context_loaded, context_unloaded);
-       }
-
-       if (config.effective_mask & PROFLOG_FINALIZATION_EVENTS) {
-               events |= MONO_PROFILE_GC_FINALIZATION;
-               mono_profiler_install_gc_finalize (finalize_begin, finalize_object_begin, finalize_object_end, finalize_end);   
-       }
-
-       //PROFLOG_COUNTER_EVENTS is a pseudo event controled by the no_counters global var
-       //PROFLOG_GC_HANDLE_EVENTS is handled together with PROFLOG_GC_ROOT_EVENTS
-
-       mono_profiler_set_events ((MonoProfileFlags)events);
-}
diff --git a/mono/profiler/mono-profiler-log.h b/mono/profiler/mono-profiler-log.h
deleted file mode 100644 (file)
index ee1d1d3..0000000
+++ /dev/null
@@ -1,267 +0,0 @@
-#ifndef __MONO_PROFLOG_H__
-#define __MONO_PROFLOG_H__
-
-#include <glib.h>
-#include <mono/metadata/profiler.h>
-
-#define BUF_ID 0x4D504C01
-#define LOG_HEADER_ID 0x4D505A01
-#define LOG_VERSION_MAJOR 1
-#define LOG_VERSION_MINOR 1
-#define LOG_DATA_VERSION 13
-
-/*
- * Changes in major/minor versions:
- * version 1.0: removed sysid field from header
- *              added args, arch, os fields to header
- *
- * Changes in data versions:
- * version 2: added offsets in heap walk
- * version 3: added GC roots
- * version 4: added sample/statistical profiling
- * version 5: added counters sampling
- * version 6: added optional backtrace in sampling info
- * version 8: added TYPE_RUNTIME and JIT helpers/trampolines
- * version 9: added MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING
- * version 10: added TYPE_COVERAGE
- * version 11: added thread ID to TYPE_SAMPLE_HIT
-               added more load/unload events
-                   unload for class
-                   unload for image
-                   load/unload for appdomain
-                   load/unload for contexts
-                   load/unload/name for assemblies
-               removed TYPE_LOAD_ERR flag (profiler never generated it, now removed from the format itself)
-               added TYPE_GC_HANDLE_{CREATED,DESTROYED}_BT
-               TYPE_JIT events are no longer guaranteed to have code start/size info (can be zero)
- * version 12: added MONO_COUNTER_PROFILER
- * version 13: added MONO_GC_EVENT_{PRE_STOP_WORLD_LOCKED,POST_START_WORLD_UNLOCKED}
-               added TYPE_META + TYPE_SYNC_POINT
-               removed il and native offset in TYPE_SAMPLE_HIT
-               methods in backtraces are now encoded as proper method pointers
-               removed flags in backtrace format
-               removed flags in metadata events
-               changed the following fields to a single byte rather than leb128
-                   TYPE_GC_EVENT: event_type, generation
-                   TYPE_HEAP_ROOT: root_type
-                   TYPE_JITHELPER: type
-                   TYPE_SAMPLE_HIT: sample_type
-                   TYPE_CLAUSE: clause_type
-                   TYPE_SAMPLE_COUNTERS_DESC: type, unit, variance
-                   TYPE_SAMPLE_COUNTERS: type
-               added time fields to all events that were missing one
-                   TYPE_HEAP_OBJECT
-                   TYPE_HEAP_ROOT
-                   TYPE_SAMPLE_USYM
-                   TYPE_SAMPLE_COUNTERS_DESC
-                   TYPE_COVERAGE_METHOD
-                   TYPE_COVERAGE_STATEMENT
-                   TYPE_COVERAGE_CLASS
-                   TYPE_COVERAGE_ASSEMBLY
-               moved the time field in TYPE_SAMPLE_HIT to right after the event byte, now encoded as a regular time field
-               changed the time field in TYPE_SAMPLE_COUNTERS to be encoded as a regular time field (in nanoseconds)
-               added TYPE_GC_FINALIZE_{START,END,OBJECT_START,OBJECT_END}
- */
-
-enum {
-       TYPE_ALLOC,
-       TYPE_GC,
-       TYPE_METADATA,
-       TYPE_METHOD,
-       TYPE_EXCEPTION,
-       TYPE_MONITOR,
-       TYPE_HEAP,
-       TYPE_SAMPLE,
-       TYPE_RUNTIME,
-       TYPE_COVERAGE,
-       TYPE_META,
-       /* extended type for TYPE_HEAP */
-       TYPE_HEAP_START  = 0 << 4,
-       TYPE_HEAP_END    = 1 << 4,
-       TYPE_HEAP_OBJECT = 2 << 4,
-       TYPE_HEAP_ROOT   = 3 << 4,
-       /* extended type for TYPE_METADATA */
-       TYPE_END_LOAD     = 2 << 4,
-       TYPE_END_UNLOAD   = 4 << 4,
-       /* extended type for TYPE_GC */
-       TYPE_GC_EVENT  = 1 << 4,
-       TYPE_GC_RESIZE = 2 << 4,
-       TYPE_GC_MOVE   = 3 << 4,
-       TYPE_GC_HANDLE_CREATED      = 4 << 4,
-       TYPE_GC_HANDLE_DESTROYED    = 5 << 4,
-       TYPE_GC_HANDLE_CREATED_BT   = 6 << 4,
-       TYPE_GC_HANDLE_DESTROYED_BT = 7 << 4,
-       TYPE_GC_FINALIZE_START = 8 << 4,
-       TYPE_GC_FINALIZE_END = 9 << 4,
-       TYPE_GC_FINALIZE_OBJECT_START = 10 << 4,
-       TYPE_GC_FINALIZE_OBJECT_END = 11 << 4,
-       /* extended type for TYPE_METHOD */
-       TYPE_LEAVE     = 1 << 4,
-       TYPE_ENTER     = 2 << 4,
-       TYPE_EXC_LEAVE = 3 << 4,
-       TYPE_JIT       = 4 << 4,
-       /* extended type for TYPE_EXCEPTION */
-       TYPE_THROW_NO_BT = 0 << 7,
-       TYPE_THROW_BT    = 1 << 7,
-       TYPE_CLAUSE      = 1 << 4,
-       /* extended type for TYPE_ALLOC */
-       TYPE_ALLOC_NO_BT  = 0 << 4,
-       TYPE_ALLOC_BT     = 1 << 4,
-       /* extended type for TYPE_MONITOR */
-       TYPE_MONITOR_NO_BT  = 0 << 7,
-       TYPE_MONITOR_BT     = 1 << 7,
-       /* extended type for TYPE_SAMPLE */
-       TYPE_SAMPLE_HIT           = 0 << 4,
-       TYPE_SAMPLE_USYM          = 1 << 4,
-       TYPE_SAMPLE_UBIN          = 2 << 4,
-       TYPE_SAMPLE_COUNTERS_DESC = 3 << 4,
-       TYPE_SAMPLE_COUNTERS      = 4 << 4,
-       /* extended type for TYPE_RUNTIME */
-       TYPE_JITHELPER = 1 << 4,
-       /* extended type for TYPE_COVERAGE */
-       TYPE_COVERAGE_ASSEMBLY = 0 << 4,
-       TYPE_COVERAGE_METHOD   = 1 << 4,
-       TYPE_COVERAGE_STATEMENT = 2 << 4,
-       TYPE_COVERAGE_CLASS = 3 << 4,
-       /* extended type for TYPE_META */
-       TYPE_SYNC_POINT = 0 << 4,
-       TYPE_END
-};
-
-enum {
-       /* metadata type byte for TYPE_METADATA */
-       TYPE_CLASS    = 1,
-       TYPE_IMAGE    = 2,
-       TYPE_ASSEMBLY = 3,
-       TYPE_DOMAIN   = 4,
-       TYPE_THREAD   = 5,
-       TYPE_CONTEXT  = 6,
-};
-
-typedef enum {
-       SYNC_POINT_PERIODIC,
-       SYNC_POINT_WORLD_STOP,
-       SYNC_POINT_WORLD_START
-} MonoProfilerSyncPointType;
-
-// Sampling sources
-// Unless you have compiled with --enable-perf-events, only SAMPLE_CYCLES is available
-enum {
-       SAMPLE_CYCLES = 1,
-       SAMPLE_INSTRUCTIONS,
-       SAMPLE_CACHE_MISSES,
-       SAMPLE_CACHE_REFS,
-       SAMPLE_BRANCHES,
-       SAMPLE_BRANCH_MISSES,
-       SAMPLE_LAST
-};
-
-
-// If you alter MAX_FRAMES, you may need to alter SAMPLE_BLOCK_SIZE too.
-#define MAX_FRAMES 32
-
-//The following flags control emitting individual events
-#define PROFLOG_DOMAIN_EVENTS (1 << 0)
-#define PROFLOG_ASSEMBLY_EVENTS        (1 << 1)
-#define PROFLOG_MODULE_EVENTS (1 << 2)
-#define PROFLOG_CLASS_EVENTS (1 << 3)
-#define PROFLOG_JIT_COMPILATION_EVENTS (1 << 4)
-#define PROFLOG_EXCEPTION_EVENTS (1 << 5)
-#define PROFLOG_ALLOCATION_EVENTS (1 << 6)
-#define PROFLOG_GC_EVENTS (1 << 7)
-#define PROFLOG_THREAD_EVENTS (1 << 8)
-//This generate enter/leave events
-#define PROFLOG_CALL_EVENTS (1 << 9)
-#define PROFLOG_INS_COVERAGE_EVENTS (1 << 10)
-#define PROFLOG_SAMPLING_EVENTS (1 << 11)
-#define PROFLOG_MONITOR_EVENTS (1 << 12)
-#define PROFLOG_GC_MOVES_EVENTS (1 << 13)
-
-#define PROFLOG_GC_ROOT_EVENTS (1 << 14)
-#define PROFLOG_CONTEXT_EVENTS (1 << 15)
-#define PROFLOG_FINALIZATION_EVENTS (1 << 16)
-#define PROFLOG_COUNTER_EVENTS (1 << 17)
-#define PROFLOG_GC_HANDLE_EVENTS (1 << 18)
-
-//The following flags control whole subsystems
-//Enables code coverage generation
-#define PROFLOG_CODE_COV_FEATURE (1 << 19)
-//This enables sampling to be generated
-#define PROFLOG_SAMPLING_FEATURE (1 << 20)
-//This enable heap dumping during GCs and filter GCRoots and GCHandle events outside of the dumped collections
-#define PROFLOG_HEAPSHOT_FEATURE (1 << 21)
-
-
-
-//The follow flags are the common aliases we want ppl to use
-#define PROFLOG_TYPELOADING_ALIAS (PROFLOG_DOMAIN_EVENTS | PROFLOG_ASSEMBLY_EVENTS | PROFLOG_MODULE_EVENTS | PROFLOG_CLASS_EVENTS)
-#define PROFLOG_CODECOV_ALIAS (PROFLOG_GC_EVENTS | PROFLOG_THREAD_EVENTS | PROFLOG_CALL_EVENTS | PROFLOG_INS_COVERAGE_EVENTS | PROFLOG_CODE_COV_FEATURE)
-#define PROFLOG_PERF_SAMPLING_ALIAS (PROFLOG_TYPELOADING_ALIAS | PROFLOG_THREAD_EVENTS | PROFLOG_SAMPLING_EVENTS | PROFLOG_SAMPLING_FEATURE)
-#define PROFLOG_GC_ALLOC_ALIAS (PROFLOG_TYPELOADING_ALIAS | PROFLOG_THREAD_EVENTS | PROFLOG_GC_EVENTS | PROFLOG_ALLOCATION_EVENTS)
-#define PROFLOG_HEAPSHOT_ALIAS (PROFLOG_TYPELOADING_ALIAS | PROFLOG_THREAD_EVENTS | PROFLOG_GC_EVENTS | PROFLOG_GC_ROOT_EVENTS | PROFLOG_HEAPSHOT_FEATURE)
-#define PROFLOG_LEGACY_ALIAS (PROFLOG_TYPELOADING_ALIAS | PROFLOG_GC_EVENTS | PROFLOG_THREAD_EVENTS | PROFLOG_JIT_COMPILATION_EVENTS | PROFLOG_EXCEPTION_EVENTS | PROFLOG_MONITOR_EVENTS | PROFLOG_GC_ROOT_EVENTS | PROFLOG_CONTEXT_EVENTS | PROFLOG_FINALIZATION_EVENTS | PROFLOG_COUNTER_EVENTS)
-
-
-typedef struct {
-       //Events explicitly enabled
-       int enable_mask;
-       //Events explicitly disabled
-       int disable_mask;
-
-       //Actual mask the profiler should use
-       int effective_mask;
-
-       //Emit a report at the end of execution
-       gboolean do_report;
-
-       //Enable profiler internal debugging
-       gboolean do_debug;
-
-       //Enable code coverage specific debugging
-       gboolean debug_coverage;
-
-       //Where to compress the output file
-       gboolean use_zip;
-
-       //If true, don't generate stacktraces
-       gboolean notraces;
-
-       //If true, emit coverage but don't emit enter/exit events - this happens cuz they share an event
-       gboolean only_coverage;
-
-       //If true, heapshots are generated on demand only
-       gboolean hs_mode_ondemand;
-
-       //HeapShort frequency in milliseconds
-       unsigned int hs_mode_ms;
-
-       //HeapShort frequency in number of collections
-       unsigned int hs_mode_gc;
-
-       //Sample frequency in Hertz
-       int sample_freq;
-
-       //Maximum number of frames to collect
-       int num_frames;
-
-       //Max depth to record enter/leave events
-       int max_call_depth;
-
-       //Name of the generated mlpd file
-       const char *output_filename;
-
-       //Filter files used by the code coverage mode
-       GPtrArray *cov_filter_files;
-
-       //Port to listen for profiling commands
-       int command_port;
-
-       //Max size of the sample hit buffer, we'll drop frames if it's reached
-       int max_allocated_sample_hits;
-
-       MonoProfileSamplingMode sampling_mode;
-} ProfilerConfig;
-
-void proflog_parse_args (ProfilerConfig *config, const char *desc);
-
-#endif /* __MONO_PROFLOG_H__ */
diff --git a/mono/profiler/mono-profiler-vtune.c b/mono/profiler/mono-profiler-vtune.c
deleted file mode 100644 (file)
index 2ed52c5..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * mono-codeanalyst.c: VTune profiler
- *
- * Author:
- *   Virgile Bello (virgile.bello@gmail.com)
- *
- * (C) 2011 Virgile Bello
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-#include <mono/metadata/profiler.h>
-#include <mono/metadata/tokentype.h>
-#include <mono/metadata/tabledefs.h>
-#include <mono/metadata/debug-helpers.h>
-#include <mono/metadata/assembly.h>
-#include <mono/metadata/mono-debug.h>
-#include <mono/metadata/debug-internals.h>
-#include <string.h>
-#include <glib.h>
-
-#define bool char
-
-#include <jitprofiling.h>
-
-static const char*
-code_buffer_desc (MonoProfilerCodeBufferType type)
-{
-       switch (type) {
-       case MONO_PROFILER_CODE_BUFFER_METHOD:
-               return "code_buffer_method";
-       case MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE:
-               return "code_buffer_method_trampoline";
-       case MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE:
-               return "code_buffer_unbox_trampoline";
-       case MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE:
-               return "code_buffer_imt_trampoline";
-       case MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE:
-               return "code_buffer_generics_trampoline";
-       case MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE:
-               return "code_buffer_specific_trampoline";
-       case MONO_PROFILER_CODE_BUFFER_HELPER:
-               return "code_buffer_misc_helper";
-       case MONO_PROFILER_CODE_BUFFER_MONITOR:
-               return "code_buffer_monitor";
-       case MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE:
-               return "code_buffer_delegate_invoke";
-       case MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING:
-               return "code_buffer_exception_handling";
-       default:
-               return "unspecified";
-       }
-}
-
-/* called at the end of the program */
-static void
-codeanalyst_shutdown (MonoProfiler *prof)
-{
-       iJIT_NotifyEvent(iJVM_EVENT_TYPE_SHUTDOWN, NULL);
-}
-
-static void
-method_jit_result (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo, int result) {
-       if (result == MONO_PROFILE_OK) {
-               int i;
-               MonoDebugSourceLocation *sourceLoc;
-               MonoDebugMethodJitInfo *dmji;
-               MonoClass *klass = mono_method_get_class (method);
-               char *signature = mono_signature_get_desc (mono_method_signature (method), TRUE);
-               char *name = g_strdup_printf ("%s(%s)", mono_method_get_name (method), signature);
-               char *classname = g_strdup_printf ("%s%s%s", mono_class_get_namespace (klass), mono_class_get_namespace (klass)[0] != 0 ? "::" : "", mono_class_get_name (klass));
-               gpointer code_start = mono_jit_info_get_code_start (jinfo);
-               int code_size = mono_jit_info_get_code_size (jinfo);
-               
-               iJIT_Method_Load vtuneMethod;
-               memset(&vtuneMethod, 0, sizeof(vtuneMethod));
-               vtuneMethod.method_id = iJIT_GetNewMethodID();
-               vtuneMethod.method_name = name;
-               vtuneMethod.method_load_address = code_start;
-               vtuneMethod.method_size = code_size;
-               vtuneMethod.class_file_name = classname;
-
-               dmji = mono_debug_find_method (method, mono_domain_get());
-
-               if (dmji != NULL)
-               {
-                       vtuneMethod.line_number_size = dmji->num_line_numbers;
-                       vtuneMethod.line_number_table = (vtuneMethod.line_number_size != 0) ?
-                               (LineNumberInfo*)malloc(sizeof(LineNumberInfo) * vtuneMethod.line_number_size) : NULL;
-
-                       for (i = 0; i < dmji->num_line_numbers; ++i)
-                       {
-                               sourceLoc = mono_debug_lookup_source_location (method, dmji->line_numbers[i].native_offset, mono_domain_get());
-                               if (sourceLoc == NULL)
-                               {
-                                       g_free (vtuneMethod.line_number_table);
-                                       vtuneMethod.line_number_table = NULL;
-                                       vtuneMethod.line_number_size = 0;
-                                       break;
-                               }
-                               if (i == 0)
-                                       vtuneMethod.source_file_name = strdup(sourceLoc->source_file);
-                               vtuneMethod.line_number_table[i].Offset = dmji->line_numbers[i].native_offset;
-                               vtuneMethod.line_number_table[i].LineNumber = sourceLoc->row;
-                               mono_debug_free_source_location (sourceLoc);
-                       }
-                       mono_debug_free_method_jit_info (dmji);
-               }
-
-               iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, &vtuneMethod);
-
-               if (vtuneMethod.source_file_name != NULL)
-                       g_free (vtuneMethod.source_file_name);
-               if (vtuneMethod.line_number_table != NULL)
-                       g_free (vtuneMethod.line_number_table);
-       
-               g_free (signature);
-               g_free (name);
-               g_free (classname);
-       }
-}
-
-static void
-code_buffer_new (MonoProfiler *prof, void *buffer, int size, MonoProfilerCodeBufferType type, void *data)
-{
-       char *name;
-       iJIT_Method_Load vtuneMethod;
-
-       if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE)
-               name = g_strdup_printf ("code_buffer_specific_trampoline_%s", (char*) data);
-       else
-               name = (char*) code_buffer_desc (type);
-
-       memset (&vtuneMethod, 0, sizeof (vtuneMethod));
-       vtuneMethod.method_id = iJIT_GetNewMethodID ();
-       vtuneMethod.method_name = name;
-       vtuneMethod.method_load_address = buffer;
-       vtuneMethod.method_size = size;
-
-       iJIT_NotifyEvent (iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, &vtuneMethod);
-
-       if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
-               g_free (name);
-       }
-}
-
-/* the entry point */
-void
-mono_profiler_startup (const char *desc)
-{
-       iJIT_IsProfilingActiveFlags flags = iJIT_IsProfilingActive();
-       if (flags == iJIT_SAMPLING_ON)
-       {
-               mono_profiler_install (NULL, codeanalyst_shutdown);
-               mono_profiler_install_jit_end (method_jit_result);
-               mono_profiler_install_code_buffer_new (code_buffer_new);
-               mono_profiler_set_events (MONO_PROFILE_JIT_COMPILATION);
-       }
-}
index 1017d1f8e6d4fb9e09b8f08fc4c7c8a976f35db6..f98fd353e378c0dfa7cb9baa2225b2b8a48e20b6 100644 (file)
@@ -54,7 +54,7 @@
  *                    - column: The column on the line
  */
 #include <config.h>
-#include "mono-profiler-log.h"
+#include "log.h"
 #include <string.h>
 #include <assert.h>
 #include <stdio.h>
diff --git a/mono/profiler/vtune.c b/mono/profiler/vtune.c
new file mode 100644 (file)
index 0000000..2ed52c5
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * mono-codeanalyst.c: VTune profiler
+ *
+ * Author:
+ *   Virgile Bello (virgile.bello@gmail.com)
+ *
+ * (C) 2011 Virgile Bello
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <mono/metadata/profiler.h>
+#include <mono/metadata/tokentype.h>
+#include <mono/metadata/tabledefs.h>
+#include <mono/metadata/debug-helpers.h>
+#include <mono/metadata/assembly.h>
+#include <mono/metadata/mono-debug.h>
+#include <mono/metadata/debug-internals.h>
+#include <string.h>
+#include <glib.h>
+
+#define bool char
+
+#include <jitprofiling.h>
+
+static const char*
+code_buffer_desc (MonoProfilerCodeBufferType type)
+{
+       switch (type) {
+       case MONO_PROFILER_CODE_BUFFER_METHOD:
+               return "code_buffer_method";
+       case MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE:
+               return "code_buffer_method_trampoline";
+       case MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE:
+               return "code_buffer_unbox_trampoline";
+       case MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE:
+               return "code_buffer_imt_trampoline";
+       case MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE:
+               return "code_buffer_generics_trampoline";
+       case MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE:
+               return "code_buffer_specific_trampoline";
+       case MONO_PROFILER_CODE_BUFFER_HELPER:
+               return "code_buffer_misc_helper";
+       case MONO_PROFILER_CODE_BUFFER_MONITOR:
+               return "code_buffer_monitor";
+       case MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE:
+               return "code_buffer_delegate_invoke";
+       case MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING:
+               return "code_buffer_exception_handling";
+       default:
+               return "unspecified";
+       }
+}
+
+/* called at the end of the program */
+static void
+codeanalyst_shutdown (MonoProfiler *prof)
+{
+       iJIT_NotifyEvent(iJVM_EVENT_TYPE_SHUTDOWN, NULL);
+}
+
+static void
+method_jit_result (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo, int result) {
+       if (result == MONO_PROFILE_OK) {
+               int i;
+               MonoDebugSourceLocation *sourceLoc;
+               MonoDebugMethodJitInfo *dmji;
+               MonoClass *klass = mono_method_get_class (method);
+               char *signature = mono_signature_get_desc (mono_method_signature (method), TRUE);
+               char *name = g_strdup_printf ("%s(%s)", mono_method_get_name (method), signature);
+               char *classname = g_strdup_printf ("%s%s%s", mono_class_get_namespace (klass), mono_class_get_namespace (klass)[0] != 0 ? "::" : "", mono_class_get_name (klass));
+               gpointer code_start = mono_jit_info_get_code_start (jinfo);
+               int code_size = mono_jit_info_get_code_size (jinfo);
+               
+               iJIT_Method_Load vtuneMethod;
+               memset(&vtuneMethod, 0, sizeof(vtuneMethod));
+               vtuneMethod.method_id = iJIT_GetNewMethodID();
+               vtuneMethod.method_name = name;
+               vtuneMethod.method_load_address = code_start;
+               vtuneMethod.method_size = code_size;
+               vtuneMethod.class_file_name = classname;
+
+               dmji = mono_debug_find_method (method, mono_domain_get());
+
+               if (dmji != NULL)
+               {
+                       vtuneMethod.line_number_size = dmji->num_line_numbers;
+                       vtuneMethod.line_number_table = (vtuneMethod.line_number_size != 0) ?
+                               (LineNumberInfo*)malloc(sizeof(LineNumberInfo) * vtuneMethod.line_number_size) : NULL;
+
+                       for (i = 0; i < dmji->num_line_numbers; ++i)
+                       {
+                               sourceLoc = mono_debug_lookup_source_location (method, dmji->line_numbers[i].native_offset, mono_domain_get());
+                               if (sourceLoc == NULL)
+                               {
+                                       g_free (vtuneMethod.line_number_table);
+                                       vtuneMethod.line_number_table = NULL;
+                                       vtuneMethod.line_number_size = 0;
+                                       break;
+                               }
+                               if (i == 0)
+                                       vtuneMethod.source_file_name = strdup(sourceLoc->source_file);
+                               vtuneMethod.line_number_table[i].Offset = dmji->line_numbers[i].native_offset;
+                               vtuneMethod.line_number_table[i].LineNumber = sourceLoc->row;
+                               mono_debug_free_source_location (sourceLoc);
+                       }
+                       mono_debug_free_method_jit_info (dmji);
+               }
+
+               iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, &vtuneMethod);
+
+               if (vtuneMethod.source_file_name != NULL)
+                       g_free (vtuneMethod.source_file_name);
+               if (vtuneMethod.line_number_table != NULL)
+                       g_free (vtuneMethod.line_number_table);
+       
+               g_free (signature);
+               g_free (name);
+               g_free (classname);
+       }
+}
+
+static void
+code_buffer_new (MonoProfiler *prof, void *buffer, int size, MonoProfilerCodeBufferType type, void *data)
+{
+       char *name;
+       iJIT_Method_Load vtuneMethod;
+
+       if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE)
+               name = g_strdup_printf ("code_buffer_specific_trampoline_%s", (char*) data);
+       else
+               name = (char*) code_buffer_desc (type);
+
+       memset (&vtuneMethod, 0, sizeof (vtuneMethod));
+       vtuneMethod.method_id = iJIT_GetNewMethodID ();
+       vtuneMethod.method_name = name;
+       vtuneMethod.method_load_address = buffer;
+       vtuneMethod.method_size = size;
+
+       iJIT_NotifyEvent (iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, &vtuneMethod);
+
+       if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
+               g_free (name);
+       }
+}
+
+/* the entry point */
+void
+mono_profiler_startup (const char *desc)
+{
+       iJIT_IsProfilingActiveFlags flags = iJIT_IsProfilingActive();
+       if (flags == iJIT_SAMPLING_ON)
+       {
+               mono_profiler_install (NULL, codeanalyst_shutdown);
+               mono_profiler_install_jit_end (method_jit_result);
+               mono_profiler_install_code_buffer_new (code_buffer_new);
+               mono_profiler_set_events (MONO_PROFILE_JIT_COMPILATION);
+       }
+}