[referencesource] Import System.Threading.ThreadLocal
[mono.git] / mono / profiler / decode.c
index eea1dbf5db5e27987c35b915dcc515999cdf9921..c7f0539b7e3273f44f2694730b557999925d8665 100644 (file)
@@ -6,6 +6,51 @@
  *
  * Copyright 2010 Novell, Inc (http://www.novell.com)
  */
+
+/*
+ * The Coverage XML output schema
+ * <coverage>
+ *   <assembly/>
+ *   <class/>
+ *   <method>
+ *     <statement/>
+ *   </method>
+ * </coverage>
+ *
+ * Elements:
+ *   <coverage> - The root element of the documentation. It can contain any number of
+ *                <assembly>, <class> or <method> elements.
+ *                Attributes:
+ *                   - version: The version number for the file format - (eg: "0.3")
+ *   <assembly> - Contains data about assemblies. Has no child elements
+ *                Attributes:
+ *                   - name: The name of the assembly - (eg: "System.Xml")
+ *                   - guid: The GUID of the assembly
+ *                   - filename: The filename of the assembly
+ *                   - method-count: The number of methods in the assembly
+ *                   - full: The number of fully covered methods
+ *                   - partial: The number of partially covered methods
+ *   <class> - Contains data about classes. Has no child elements
+ *             Attributes:
+ *                - name: The name of the class
+ *                - method-count: The number of methods in the class
+ *                - full: The number of fully covered methods
+ *                - partial: The number of partially covered methods
+ *   <method> - Contains data about methods. Can contain any number of <statement> elements
+ *              Attributes:
+ *                 - assembly: The name of the parent assembly
+ *                 - class: The name of the parent class
+ *                 - name: The name of the method, with all it's parameters
+ *                 - filename: The name of the source file containing this method
+ *                 - token
+ *   <statement> - Contains data about IL statements. Has no child elements
+ *                 Attributes:
+ *                    - offset: The offset of the statement in the IL code after the previous
+ *                              statement's offset
+ *                    - counter: 1 if the line was covered, 0 if it was not
+ *                    - line: The line number in the parent method's file
+ *                    - column: The column on the line
+ */
 #include <config.h>
 #include "utils.c"
 #include "proflog.h"
@@ -20,6 +65,7 @@
 #if defined (HAVE_SYS_ZLIB)
 #include <zlib.h>
 #endif
+#include <glib.h>
 #include <mono/metadata/profiler.h>
 #include <mono/metadata/object.h>
 #include <mono/metadata/debug-helpers.h>
@@ -48,6 +94,7 @@ static uint64_t time_to = 0xffffffffffffffffULL;
 static int use_time_filter = 0;
 static uint64_t startup_time = 0;
 static FILE* outfile = NULL;
+static FILE* coverage_outfile = NULL;
 
 static int32_t
 read_int16 (unsigned char *p)
@@ -100,7 +147,7 @@ struct _CounterValue {
 typedef struct _Counter Counter;
 struct _Counter {
        int index;
-       int section;
+       const char *section;
        const char *name;
        int type;
        int unit;
@@ -117,7 +164,7 @@ struct _CounterList {
 
 typedef struct _CounterSection CounterSection;
 struct _CounterSection {
-       int value;
+       const char *value;
        CounterList *counters;
        CounterList *counters_last;
        CounterSection *next;
@@ -152,7 +199,7 @@ add_counter_to_section (Counter *counter)
        clist->counter = counter;
 
        for (csection = counters_sections; csection; csection = csection->next) {
-               if (csection->value == counter->section) {
+               if (strcmp (csection->value, counter->section) == 0) {
                        /* If section exist */
                        if (!csection->counters)
                                csection->counters = clist;
@@ -180,7 +227,7 @@ add_counter_to_section (Counter *counter)
 }
 
 static void
-add_counter (int section, const char *name, int type, int unit, int variance, int index)
+add_counter (const char *section, const char *name, int type, int unit, int variance, int index)
 {
        CounterList *list, *l;
        Counter *counter;
@@ -226,7 +273,7 @@ add_counter_to_timestamp (uint64_t timestamp, Counter *counter)
        for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
                if (ctimestamp->value == timestamp) {
                        for (csection = ctimestamp->sections; csection; csection = csection->next) {
-                               if (csection->value == counter->section) {
+                               if (strcmp (csection->value, counter->section) == 0) {
                                        /* if timestamp exist and section exist */
                                        if (!csection->counters)
                                                csection->counters = clist;
@@ -445,7 +492,7 @@ dump_counters (void)
                                for (i = 0; counters_to_print [i][0] != 0; i++) {
                                        if (strcmp (counters_to_print [i], counter->name) == 0) {
                                                if (!section_printed) {
-                                                       fprintf (outfile, "\t%s:\n", section_name (csection->value));
+                                                       fprintf (outfile, "\t%s:\n", csection->value);
                                                        section_printed = 1;
                                                }
 
@@ -457,12 +504,15 @@ dump_counters (void)
                }
        } else if (counters_sort_mode == COUNTERS_SORT_TIME) {
                for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
-                       fprintf (outfile, "\t%lld:%02lld:%02lld:%02lld.%03lld:\n", ctimestamp->value / 1000 / 60 / 60 / 24 % 1000,
-                               ctimestamp->value / 1000 / 60 / 60 % 24, ctimestamp->value / 1000 / 60 % 60,
-                               ctimestamp->value / 1000 % 60, ctimestamp->value % 1000);
+                       fprintf (outfile, "\t%llu:%02llu:%02llu:%02llu.%03llu:\n",
+                               (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 / 24 % 1000),
+                               (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 % 24),
+                               (unsigned long long) (ctimestamp->value / 1000 / 60 % 60),
+                               (unsigned long long) (ctimestamp->value / 1000 % 60),
+                               (unsigned long long) (ctimestamp->value % 1000));
 
                        for (csection = ctimestamp->sections; csection; csection = csection->next) {
-                               fprintf (outfile, "\t\t%s:\n", section_name (csection->value));
+                               fprintf (outfile, "\t\t%s:\n", csection->value);
 
                                for (clist = csection->counters; clist; clist = clist->next) {
                                        counter = clist->counter;
@@ -477,7 +527,7 @@ dump_counters (void)
                }
        } else if (counters_sort_mode == COUNTERS_SORT_CATEGORY) {
                for (csection = counters_sections; csection; csection = csection->next) {
-                       fprintf (outfile, "\t%s:\n", section_name (csection->value));
+                       fprintf (outfile, "\t%s:\n", csection->value);
 
                        for (clist = csection->counters; clist; clist = clist->next) {
                                counter = clist->counter;
@@ -485,9 +535,12 @@ dump_counters (void)
                                        counter->name, type_name (counter->type), unit_name (counter->unit), variance_name (counter->variance));
 
                                for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
-                                       snprintf (strtimestamp, sizeof (strtimestamp), "%lld:%02lld:%02lld:%02lld.%03lld", cvalue->timestamp / 1000 / 60 / 60 / 24 % 1000,
-                                               cvalue->timestamp / 1000 / 60 / 60 % 24, cvalue->timestamp / 1000 / 60 % 60,
-                                               cvalue->timestamp / 1000 % 60, cvalue->timestamp % 1000);
+                                       snprintf (strtimestamp, sizeof (strtimestamp), "%llu:%02llu:%02llu:%02llu.%03llu",
+                                               (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 / 24 % 1000),
+                                               (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 % 24),
+                                               (unsigned long long) (cvalue->timestamp / 1000 / 60 % 60),
+                                               (unsigned long long) (cvalue->timestamp / 1000 % 60),
+                                               (unsigned long long) (cvalue->timestamp % 1000));
 
                                        dump_counters_value (counter, "\t\t\t%s", strtimestamp, cvalue->buffer);
                                }
@@ -852,9 +905,9 @@ static void
 print_usym (UnmanagedSymbol* um)
 {
        if (um->parent)
-               fprintf (outfile, "\t%6d %6.2f %-36s in %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name, um->parent->name);
+               fprintf (outfile, "\t%6zd %6.2f %-36s in %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name, um->parent->name);
        else
-               fprintf (outfile, "\t%6d %6.2f %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name);
+               fprintf (outfile, "\t%6zd %6.2f %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name);
 }
 
 static int
@@ -1197,7 +1250,7 @@ heap_shot_obj_add_refs (HeapShot *hs, uintptr_t objaddr, uintptr_t num, uintptr_
        /* should not happen */
        printf ("failed heap obj update\n");
        return NULL;
-       
+
 }
 
 static uintptr_t
@@ -1333,7 +1386,7 @@ heap_shot_mark_objects (HeapShot *hs)
                        fprintf (outfile, "object %p (%s) unmarked\n", (void*)hs->objects_hash [i], hs->objects_hash [i]->hklass->klass->name);
                }
        }
-       fprintf (outfile, "Total unmarked: %d/%d\n", num_unmarked, hs->objects_count);
+       fprintf (outfile, "Total unmarked: %zd/%zd\n", num_unmarked, hs->objects_count);
        free (marks);
 }
 
@@ -1426,7 +1479,7 @@ typedef struct _ThreadContext ThreadContext;
 typedef struct {
        FILE *file;
 #if defined (HAVE_SYS_ZLIB)
-       gzFile *gzfile;
+       gzFile gzfile;
 #endif
        unsigned char *buf;
        int size;
@@ -1482,7 +1535,7 @@ load_data (ProfContext *ctx, int size)
                if (r == 0)
                        return size == 0? 1: 0;
                return r == size;
-       } else 
+       } else
 #endif
        {
                int r = fread (ctx->buf, size, 1, ctx->file);
@@ -1840,7 +1893,7 @@ tracked_creation (uintptr_t obj, ClassDesc *cd, uint64_t size, BackTrace *bt, ui
        for (i = 0; i < num_tracked_objects; ++i) {
                if (tracked_objects [i] != obj)
                        continue;
-               fprintf (outfile, "Object %p created (%s, %llu bytes) at %.3f secs.\n", (void*)obj, cd->name, size, (timestamp - startup_time)/1000000000.0);
+               fprintf (outfile, "Object %p created (%s, %llu bytes) at %.3f secs.\n", (void*)obj, cd->name, (unsigned long long) size, (timestamp - startup_time)/1000000000.0);
                if (bt && bt->count) {
                        int k;
                        for (k = 0; k < bt->count; ++k)
@@ -1876,7 +1929,7 @@ track_obj_reference (uintptr_t obj, uintptr_t parent, ClassDesc *cd)
 {
        int i;
        for (i = 0; i < num_tracked_objects; ++i) {
-               if (tracked_objects [i] == obj) 
+               if (tracked_objects [i] == obj)
                        fprintf (outfile, "Object %p referenced from %p (%s).\n", (void*)obj, (void*)parent, cd->name);
        }
 }
@@ -1889,6 +1942,149 @@ found_object (uintptr_t obj)
        tracked_objects [num_tracked_objects - 1] = obj;
 }
 
+static int num_jit_helpers = 0;
+static int jit_helpers_code_size = 0;
+
+static const char*
+code_buffer_desc (int type)
+{
+       switch (type) {
+       case MONO_PROFILER_CODE_BUFFER_METHOD:
+               return "method";
+       case MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE:
+               return "method trampoline";
+       case MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE:
+               return "unbox trampoline";
+       case MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE:
+               return "imt trampoline";
+       case MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE:
+               return "generics trampoline";
+       case MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE:
+               return "specific trampoline";
+       case MONO_PROFILER_CODE_BUFFER_HELPER:
+               return "misc helper";
+       case MONO_PROFILER_CODE_BUFFER_MONITOR:
+               return "monitor/lock";
+       case MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE:
+               return "delegate invoke";
+       case MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING:
+               return "exception handling";
+       default:
+               return "unspecified";
+       }
+}
+
+typedef struct _CoverageAssembly CoverageAssembly;
+struct _CoverageAssembly {
+       char *name;
+       char *guid;
+       char *filename;
+       int number_of_methods;
+       int fully_covered;
+       int partially_covered;
+};
+
+typedef struct _CoverageClass CoverageClass;
+struct _CoverageClass {
+       char *assembly_name;
+       char *class_name;
+       int number_of_methods;
+       int fully_covered;
+       int partially_covered;
+};
+
+typedef struct _CoverageCoverage CoverageCoverage;
+struct _CoverageCoverage {
+       int method_id;
+       int offset;
+       int count;
+       int line;
+       int column;
+};
+
+typedef struct _CoverageMethod CoverageMethod;
+struct _CoverageMethod {
+       char *assembly_name;
+       char *class_name;
+       char *method_name;
+       char *method_signature;
+       char *filename;
+       int token;
+       int n_statements;
+       int method_id;
+       GPtrArray *coverage;
+};
+static GPtrArray *coverage_assemblies = NULL;
+static GPtrArray *coverage_methods = NULL;
+static GPtrArray *coverage_statements = NULL;
+static GHashTable *coverage_methods_hash = NULL;
+static GPtrArray *coverage_classes = NULL;
+static GHashTable *coverage_assembly_classes = NULL;
+
+static void
+gather_coverage_statements (void)
+{
+       for (guint i = 0; i < coverage_statements->len; i++) {
+               CoverageCoverage *coverage = coverage_statements->pdata[i];
+               CoverageMethod *method = g_hash_table_lookup (coverage_methods_hash, GINT_TO_POINTER (coverage->method_id));
+               if (method == NULL) {
+                       fprintf (outfile, "Cannot find method with ID: %d\n", coverage->method_id);
+                       continue;
+               }
+
+               g_ptr_array_add (method->coverage, coverage);
+       }
+}
+
+static void
+coverage_add_assembly (CoverageAssembly *assembly)
+{
+       if (coverage_assemblies == NULL)
+               coverage_assemblies = g_ptr_array_new ();
+
+       g_ptr_array_add (coverage_assemblies, assembly);
+}
+
+static void
+coverage_add_method (CoverageMethod *method)
+{
+       if (coverage_methods == NULL) {
+               coverage_methods = g_ptr_array_new ();
+               coverage_methods_hash = g_hash_table_new (NULL, NULL);
+       }
+
+       g_ptr_array_add (coverage_methods, method);
+       g_hash_table_insert (coverage_methods_hash, GINT_TO_POINTER (method->method_id), method);
+}
+
+static void
+coverage_add_class (CoverageClass *klass)
+{
+       GPtrArray *classes = NULL;
+
+       if (coverage_classes == NULL) {
+               coverage_classes = g_ptr_array_new ();
+               coverage_assembly_classes = g_hash_table_new (g_str_hash, g_str_equal);
+       }
+
+       g_ptr_array_add (coverage_classes, klass);
+       classes = g_hash_table_lookup (coverage_assembly_classes, klass->assembly_name);
+       if (classes == NULL) {
+               classes = g_ptr_array_new ();
+               g_hash_table_insert (coverage_assembly_classes, klass->assembly_name, classes);
+       }
+       g_ptr_array_add (classes, klass);
+}
+
+static void
+coverage_add_coverage (CoverageCoverage *coverage)
+{
+       if (coverage_statements == NULL)
+               coverage_statements = g_ptr_array_new ();
+
+       g_ptr_array_add (coverage_statements, coverage);
+}
+
 #define OBJ_ADDR(diff) ((obj_base + diff) << 3)
 #define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
 
@@ -1929,7 +2125,7 @@ decode_buffer (ProfContext *ctx)
        thread_id = read_int64 (p + 32);
        method_base = read_int64 (p + 40);
        if (debug)
-               fprintf (outfile, "buf: thread:%x, len: %d, time: %llu, file offset: %llu\n", thread_id, len, time_base, file_offset);
+               fprintf (outfile, "buf: thread:%zx, len: %d, time: %llu, file offset: %llu\n", thread_id, len, (unsigned long long) time_base, (unsigned long long) file_offset);
        thread = load_thread (ctx, thread_id);
        if (!load_data (ctx, len))
                return 0;
@@ -1956,7 +2152,7 @@ decode_buffer (ProfContext *ctx)
                        if (subtype == TYPE_GC_RESIZE) {
                                uint64_t new_size = decode_uleb128 (p, &p);
                                if (debug)
-                                       fprintf (outfile, "gc heap resized to %llu\n", new_size);
+                                       fprintf (outfile, "gc heap resized to %llu\n", (unsigned long long) new_size);
                                gc_resizes++;
                                if (new_size > max_heap_size)
                                        max_heap_size = new_size;
@@ -1964,7 +2160,7 @@ decode_buffer (ProfContext *ctx)
                                uint64_t ev = decode_uleb128 (p, &p);
                                int gen = decode_uleb128 (p, &p);
                                if (debug)
-                                       fprintf (outfile, "gc event for gen%d: %s at %llu (thread: 0x%x)\n", gen, gc_event_name (ev), time_base, thread->thread_id);
+                                       fprintf (outfile, "gc event for gen%d: %s at %llu (thread: 0x%zx)\n", gen, gc_event_name (ev), (unsigned long long) time_base, thread->thread_id);
                                if (gen > 2) {
                                        fprintf (outfile, "incorrect gc gen: %d\n", gen);
                                        break;
@@ -2033,7 +2229,7 @@ decode_buffer (ProfContext *ctx)
                                        return 0;
                                }
                                if (debug)
-                                       fprintf (outfile, "loaded class %p (%s in %p) at %llu\n", (void*)(ptr_base + ptrdiff), p, (void*)(ptr_base + imptrdiff), time_base);
+                                       fprintf (outfile, "loaded class %p (%s in %p) at %llu\n", (void*)(ptr_base + ptrdiff), p, (void*)(ptr_base + imptrdiff), (unsigned long long) time_base);
                                if (!error)
                                        add_class (ptr_base + ptrdiff, (char*)p);
                                while (*p) p++;
@@ -2045,7 +2241,7 @@ decode_buffer (ProfContext *ctx)
                                        return 0;
                                }
                                if (debug)
-                                       fprintf (outfile, "loaded image %p (%s) at %llu\n", (void*)(ptr_base + ptrdiff), p, time_base);
+                                       fprintf (outfile, "loaded image %p (%s) at %llu\n", (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
                                if (!error)
                                        add_image (ptr_base + ptrdiff, (char*)p);
                                while (*p) p++;
@@ -2080,7 +2276,7 @@ decode_buffer (ProfContext *ctx)
                        LOG_TIME (time_base, tdiff);
                        time_base += tdiff;
                        if (debug)
-                               fprintf (outfile, "alloced object %p, size %llu (%s) at %llu\n", (void*)OBJ_ADDR (objdiff), len, lookup_class (ptr_base + ptrdiff)->name, time_base);
+                               fprintf (outfile, "alloced object %p, size %llu (%s) at %llu\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) len, lookup_class (ptr_base + ptrdiff)->name, (unsigned long long) time_base);
                        if (has_bt) {
                                num_bt = 8;
                                frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
@@ -2149,13 +2345,13 @@ decode_buffer (ProfContext *ctx)
                case TYPE_HEAP: {
                        int subtype = *p & 0xf0;
                        if (subtype == TYPE_HEAP_OBJECT) {
-                               HeapObjectDesc *ho;
+                               HeapObjectDesc *ho = NULL;
                                int i;
                                intptr_t objdiff = decode_sleb128 (p + 1, &p);
                                intptr_t ptrdiff = decode_sleb128 (p, &p);
                                uint64_t size = decode_uleb128 (p, &p);
                                uintptr_t num = decode_uleb128 (p, &p);
-                               uintptr_t ref_offset;
+                               uintptr_t ref_offset = 0;
                                uintptr_t last_obj_offset = 0;
                                ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
                                if (size) {
@@ -2182,10 +2378,10 @@ decode_buffer (ProfContext *ctx)
                                                track_obj_reference (OBJ_ADDR (obj1diff), OBJ_ADDR (objdiff), cd);
                                }
                                if (debug && size)
-                                       fprintf (outfile, "traced object %p, size %llu (%s), refs: %d\n", (void*)OBJ_ADDR (objdiff), size, cd->name, num);
+                                       fprintf (outfile, "traced object %p, size %llu (%s), refs: %zd\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) size, cd->name, num);
                        } else if (subtype == TYPE_HEAP_ROOT) {
                                uintptr_t num = decode_uleb128 (p + 1, &p);
-                               uintptr_t gc_num = decode_uleb128 (p, &p);
+                               uintptr_t gc_num G_GNUC_UNUSED = decode_uleb128 (p, &p);
                                int i;
                                for (i = 0; i < num; ++i) {
                                        intptr_t objdiff = decode_sleb128 (p, &p);
@@ -2347,6 +2543,30 @@ decode_buffer (ProfContext *ctx)
                        }
                        break;
                }
+               case TYPE_RUNTIME: {
+                       int subtype = *p & 0xf0;
+                       uint64_t tdiff = decode_uleb128 (p + 1, &p);
+                       LOG_TIME (time_base, tdiff);
+                       time_base += tdiff;
+                       if (subtype == TYPE_JITHELPER) {
+                               int type = decode_uleb128 (p, &p);
+                               intptr_t codediff = decode_sleb128 (p, &p);
+                               int codelen = decode_uleb128 (p, &p);
+                               const char *name;
+                               if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
+                                       name = (void*)p;
+                                       while (*p) p++;
+                                               p++;
+                               } else {
+                                       name = code_buffer_desc (type);
+                               }
+                               num_jit_helpers++;
+                               jit_helpers_code_size += codelen;
+                               if (debug)
+                                       fprintf (outfile, "jit helper %s, size: %d, code: %p\n", name, codelen, (void*)(ptr_base + codediff));
+                       }
+                       break;
+               }
                case TYPE_SAMPLE: {
                        int subtype = *p & 0xf0;
                        if (subtype == TYPE_SAMPLE_HIT) {
@@ -2389,7 +2609,7 @@ decode_buffer (ProfContext *ctx)
                                /* un unmanaged binary loaded in memory */
                                uint64_t tdiff = decode_uleb128 (p + 1, &p);
                                uintptr_t addr = decode_sleb128 (p, &p);
-                               uint64_t offset = decode_uleb128 (p, &p);
+                               uint64_t offset G_GNUC_UNUSED = decode_uleb128 (p, &p);
                                uintptr_t size = decode_uleb128 (p, &p);
                                char *name;
                                LOG_TIME (time_base, tdiff);
@@ -2405,13 +2625,20 @@ decode_buffer (ProfContext *ctx)
                                for (i = 0; i < len; i++) {
                                        uint64_t type, unit, variance, index;
                                        uint64_t section = decode_uleb128 (p, &p);
-                                       char *name = pstrdup ((char*)p);
+                                       char *section_str, *name;
+                                       if (section != MONO_COUNTER_PERFCOUNTERS) {
+                                               section_str = (char*) section_name (section);
+                                       } else {
+                                               section_str = pstrdup ((char*)p);
+                                               while (*p++);
+                                       }
+                                       name = pstrdup ((char*)p);
                                        while (*p++);
                                        type = decode_uleb128 (p, &p);
                                        unit = decode_uleb128 (p, &p);
                                        variance = decode_uleb128 (p, &p);
                                        index = decode_uleb128 (p, &p);
-                                       add_counter ((int)section, name, (int)type, (int)unit, (int)variance, (int)index);
+                                       add_counter (section_str, name, (int)type, (int)unit, (int)variance, (int)index);
                                }
                        } else if (subtype == TYPE_SAMPLE_COUNTERS) {
                                int i;
@@ -2486,8 +2713,111 @@ decode_buffer (ProfContext *ctx)
                        }
                        break;
                }
+               case TYPE_COVERAGE:{
+                       int subtype = *p & 0xf0;
+                       switch (subtype) {
+                       case TYPE_COVERAGE_METHOD: {
+                               CoverageMethod *method = g_new0 (CoverageMethod, 1);
+                               const char *assembly, *klass, *name, *sig, *filename;
+                               int token, n_offsets, method_id;
+
+                               p++;
+                               assembly = (void *)p; while (*p) p++; p++;
+                               klass = (void *)p; while (*p) p++; p++;
+                               name = (void *)p; while (*p) p++; p++;
+                               sig = (void *)p; while (*p) p++; p++;
+                               filename = (void *)p; while (*p) p++; p++;
+
+                               token = decode_uleb128 (p, &p);
+                               method_id = decode_uleb128 (p, &p);
+                               n_offsets = decode_uleb128 (p, &p);
+
+                               method->assembly_name = g_strdup (assembly);
+                               method->class_name = g_strdup (klass);
+                               method->method_name = g_strdup (name);
+                               method->method_signature = g_strdup (sig);
+                               method->filename = g_strdup (filename);
+                               method->token = token;
+                               method->n_statements = n_offsets;
+                               method->coverage = g_ptr_array_new ();
+                               method->method_id = method_id;
+
+                               coverage_add_method (method);
+
+                               break;
+                       }
+                       case TYPE_COVERAGE_STATEMENT: {
+                               CoverageCoverage *coverage = g_new0 (CoverageCoverage, 1);
+                               int offset, count, line, column, method_id;
+
+                               p++;
+                               method_id = decode_uleb128 (p, &p);
+                               offset = decode_uleb128 (p, &p);
+                               count = decode_uleb128 (p, &p);
+                               line = decode_uleb128 (p, &p);
+                               column = decode_uleb128 (p, &p);
+
+                               coverage->method_id = method_id;
+                               coverage->offset = offset;
+                               coverage->count = count;
+                               coverage->line = line;
+                               coverage->column = column;
+
+                               coverage_add_coverage (coverage);
+                               break;
+                       }
+                       case TYPE_COVERAGE_ASSEMBLY: {
+                               CoverageAssembly *assembly = g_new0 (CoverageAssembly, 1);
+                               char *name, *guid, *filename;
+                               int number_of_methods, fully_covered, partially_covered;
+                               p++;
+
+                               name = (void *)p; while (*p) p++; p++;
+                               guid = (void *)p; while (*p) p++; p++;
+                               filename = (void *)p; while (*p) p++; p++;
+                               number_of_methods = decode_uleb128 (p, &p);
+                               fully_covered = decode_uleb128 (p, &p);
+                               partially_covered = decode_uleb128 (p, &p);
+
+                               assembly->name = g_strdup (name);
+                               assembly->guid = g_strdup (guid);
+                               assembly->filename = g_strdup (filename);
+                               assembly->number_of_methods = number_of_methods;
+                               assembly->fully_covered = fully_covered;
+                               assembly->partially_covered = partially_covered;
+
+                               coverage_add_assembly (assembly);
+                               break;
+                       }
+                       case TYPE_COVERAGE_CLASS: {
+                               CoverageClass *klass = g_new0 (CoverageClass, 1);
+                               char *assembly_name, *class_name;
+                               int number_of_methods, fully_covered, partially_covered;
+                               p++;
+
+                               assembly_name = (void *)p; while (*p) p++; p++;
+                               class_name = (void *)p; while (*p) p++; p++;
+                               number_of_methods = decode_uleb128 (p, &p);
+                               fully_covered = decode_uleb128 (p, &p);
+                               partially_covered = decode_uleb128 (p, &p);
+
+                               klass->assembly_name = g_strdup (assembly_name);
+                               klass->class_name = g_strdup (class_name);
+                               klass->number_of_methods = number_of_methods;
+                               klass->fully_covered = fully_covered;
+                               klass->partially_covered = partially_covered;
+
+                               coverage_add_class (klass);
+                               break;
+                       }
+
+                       default:
+                               break;
+                       }
+                       break;
+               }
                default:
-                       fprintf (outfile, "unhandled profiler event: 0x%x at file offset: %llu + %d (len: %d\n)\n", *p, file_offset, p - ctx->buf, len);
+                       fprintf (outfile, "unhandled profiler event: 0x%x at file offset: %llu + %lld (len: %d\n)\n", *p, (unsigned long long) file_offset, (long long) (p - ctx->buf), len);
                        exit (1);
                }
        }
@@ -2591,7 +2921,7 @@ dump_traces (TraceDesc *traces, const char *desc)
                bt = traces->traces [j].bt;
                if (!bt->count)
                        continue;
-               fprintf (outfile, "\t%llu %s from:\n", traces->traces [j].count, desc);
+               fprintf (outfile, "\t%llu %s from:\n", (unsigned long long) traces->traces [j].count, desc);
                for (k = 0; k < bt->count; ++k)
                        fprintf (outfile, "\t\t%s\n", bt->methods [k]->name);
        }
@@ -2612,12 +2942,12 @@ dump_exceptions (void)
 {
        int i;
        fprintf (outfile, "\nException summary\n");
-       fprintf (outfile, "\tThrows: %llu\n", throw_count);
+       fprintf (outfile, "\tThrows: %llu\n", (unsigned long long) throw_count);
        dump_traces (&exc_traces, "throws");
        for (i = 0; i <= MONO_EXCEPTION_CLAUSE_FAULT; ++i) {
                if (!clause_summary [i])
                        continue;
-               fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), clause_summary [i]);
+               fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), (unsigned long long) clause_summary [i]);
        }
 }
 
@@ -2657,9 +2987,9 @@ dump_monitors (void)
                        mdesc->wait_time/1000000000.0, mdesc->max_wait_time/1000000000.0, mdesc->wait_time/1000000000.0/mdesc->contentions);
                dump_traces (&mdesc->traces, "contentions");
        }
-       fprintf (outfile, "\tLock contentions: %llu\n", monitor_contention);
-       fprintf (outfile, "\tLock acquired: %llu\n", monitor_acquired);
-       fprintf (outfile, "\tLock failures: %llu\n", monitor_failed);
+       fprintf (outfile, "\tLock contentions: %llu\n", (unsigned long long) monitor_contention);
+       fprintf (outfile, "\tLock acquired: %llu\n", (unsigned long long) monitor_acquired);
+       fprintf (outfile, "\tLock failures: %llu\n", (unsigned long long) monitor_failed);
 }
 
 static void
@@ -2668,20 +2998,25 @@ dump_gcs (void)
        int i;
        fprintf (outfile, "\nGC summary\n");
        fprintf (outfile, "\tGC resizes: %d\n", gc_resizes);
-       fprintf (outfile, "\tMax heap size: %llu\n", max_heap_size);
-       fprintf (outfile, "\tObject moves: %llu\n", gc_object_moves);
+       fprintf (outfile, "\tMax heap size: %llu\n", (unsigned long long) max_heap_size);
+       fprintf (outfile, "\tObject moves: %llu\n", (unsigned long long) gc_object_moves);
        for (i = 0; i < 3; ++i) {
                if (!gc_info [i].count)
                        continue;
                fprintf (outfile, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
-                       i, gc_info [i].count, gc_info [i].max_time / 1000, gc_info [i].total_time / 1000,
-                       gc_info [i].total_time / gc_info [i].count / 1000);
+                       i, gc_info [i].count,
+                       (unsigned long long) (gc_info [i].max_time / 1000),
+                       (unsigned long long) (gc_info [i].total_time / 1000),
+                       (unsigned long long) (gc_info [i].total_time / gc_info [i].count / 1000));
        }
        for (i = 0; i < 3; ++i) {
                if (!handle_info [i].max_live)
                        continue;
                fprintf (outfile, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
-                       get_handle_name (i), handle_info [i].created, handle_info [i].destroyed, handle_info [i].max_live);
+                       get_handle_name (i),
+                       (unsigned long long) (handle_info [i].created),
+                       (unsigned long long) (handle_info [i].destroyed),
+                       (unsigned long long) (handle_info [i].max_live));
                dump_traces (&handle_info [i].traces, "created");
        }
 }
@@ -2705,6 +3040,8 @@ dump_jit (void)
        }
        fprintf (outfile, "\tCompiled methods: %d\n", compiled_methods);
        fprintf (outfile, "\tGenerated code size: %d\n", code_size);
+       fprintf (outfile, "\tJIT helpers: %d\n", num_jit_helpers);
+       fprintf (outfile, "\tJIT helpers code size: %d\n", jit_helpers_code_size);
 }
 
 static void
@@ -2735,11 +3072,15 @@ dump_allocations (void)
                        fprintf (outfile, "\nAllocation summary\n");
                        fprintf (outfile, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
                }
-               fprintf (outfile, "%10llu %10d %8llu %s\n", cd->alloc_size, cd->allocs, cd->alloc_size / cd->allocs, cd->name);
+               fprintf (outfile, "%10llu %10zd %8llu %s\n",
+                       (unsigned long long) (cd->alloc_size),
+                       cd->allocs,
+                       (unsigned long long) (cd->alloc_size / cd->allocs),
+                       cd->name);
                dump_traces (&cd->traces, "bytes");
        }
        if (allocs)
-               fprintf (outfile, "Total memory allocated: %llu bytes in %d objects\n", size, allocs);
+               fprintf (outfile, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size, allocs);
 }
 
 enum {
@@ -2825,11 +3166,15 @@ dump_methods (void)
                        fprintf (outfile, "\nMethod call summary\n");
                        fprintf (outfile, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
                }
-               fprintf (outfile, "%8llu %8llu %10llu %s\n", msecs, smsecs, cd->calls, cd->name);
+               fprintf (outfile, "%8llu %8llu %10llu %s\n",
+                       (unsigned long long) (msecs),
+                       (unsigned long long) (smsecs),
+                       (unsigned long long) (cd->calls),
+                       cd->name);
                dump_traces (&cd->traces, "calls");
        }
        if (calls)
-               fprintf (outfile, "Total calls: %llu\n", calls);
+               fprintf (outfile, "Total calls: %llu\n", (unsigned long long) calls);
 }
 
 static int
@@ -2874,7 +3219,9 @@ dump_rev_claases (HeapClassRevRef *revs, int count)
                return;
        for (j = 0; j < count; ++j) {
                HeapClassDesc *cd = revs [j].klass;
-               fprintf (outfile, "\t\t%llu references from: %s\n", revs [j].count, cd->klass->name);
+               fprintf (outfile, "\t\t%llu references from: %s\n",
+                       (unsigned long long) (revs [j].count),
+                       cd->klass->name);
        }
 }
 
@@ -2898,8 +3245,12 @@ heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
        }
        hs->sorted = sorted;
        qsort (sorted, ccount, sizeof (void*), compare_heap_class);
-       fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %d\n",
-               hs_num, (hs->timestamp - startup_time)/1000000000.0, size, count, ccount, hs->num_roots);
+       fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %zd\n",
+               hs_num,
+               (hs->timestamp - startup_time)/1000000000.0,
+               (unsigned long long) (size),
+               (unsigned long long) (count),
+               ccount, hs->num_roots);
        if (!verbose && ccount > 30)
                ccount = 30;
        fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
@@ -2910,11 +3261,15 @@ heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
                cd = sorted [i];
                if (last_hs)
                        ocd = heap_class_lookup (last_hs, cd->klass);
-               fprintf (outfile, "\t%10llu %10llu %8llu %s", cd->total_size, cd->count, cd->total_size / cd->count, cd->klass->name);
+               fprintf (outfile, "\t%10llu %10llu %8llu %s",
+                       (unsigned long long) (cd->total_size),
+                       (unsigned long long) (cd->count),
+                       (unsigned long long) (cd->total_size / cd->count),
+                       cd->klass->name);
                if (ocd) {
                        int64_t bdiff = cd->total_size - ocd->total_size;
                        int64_t cdiff = cd->count - ocd->count;
-                       fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", bdiff, cdiff);
+                       fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", (long long) bdiff, (long long) cdiff);
                } else {
                        fprintf (outfile, "\n");
                }
@@ -2929,7 +3284,7 @@ heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
                assert (cd->rev_count == k);
                qsort (rev_sorted, cd->rev_count, sizeof (HeapClassRevRef), compare_rev_class);
                if (cd->root_references)
-                       fprintf (outfile, "\t\t%d root references (%d pinning)\n", cd->root_references, cd->pinned_references);
+                       fprintf (outfile, "\t\t%zd root references (%zd pinning)\n", cd->root_references, cd->pinned_references);
                dump_rev_claases (rev_sorted, cd->rev_count);
                free (rev_sorted);
        }
@@ -2970,6 +3325,160 @@ dump_heap_shots (void)
        }
 }
 
+/* This is a very basic escape function that escapes < > and &
+   Ideally we'd use g_markup_escape_string but that function isn't
+        available in Mono's eglib. This was written without looking at the
+        source of that function in glib. */
+static char *
+escape_string_for_xml (const char *string)
+{
+       GString *string_builder = g_string_new (NULL);
+       const char *start, *p;
+
+       start = p = string;
+       while (*p) {
+               while (*p && *p != '&' && *p != '<' && *p != '>')
+                       p++;
+
+               g_string_append_len (string_builder, start, p - start);
+
+               if (*p == '\0')
+                       break;
+
+               switch (*p) {
+               case '<':
+                       g_string_append (string_builder, "&lt;");
+                       break;
+
+               case '>':
+                       g_string_append (string_builder, "&gt;");
+                       break;
+
+               case '&':
+                       g_string_append (string_builder, "&amp;");
+                       break;
+
+               default:
+                       break;
+               }
+
+               p++;
+               start = p;
+       }
+
+       return g_string_free (string_builder, FALSE);
+}
+
+static int
+sort_assemblies (gconstpointer a, gconstpointer b)
+{
+       CoverageAssembly *assembly_a = *(CoverageAssembly **)a;
+       CoverageAssembly *assembly_b = *(CoverageAssembly **)b;
+
+       if (assembly_a->name == NULL && assembly_b->name == NULL)
+               return 0;
+       else if (assembly_a->name == NULL)
+               return -1;
+       else if (assembly_b->name == NULL)
+               return 1;
+
+       return strcmp (assembly_a->name, assembly_b->name);
+}
+
+static void
+dump_coverage (void)
+{
+       if (!coverage_methods && !coverage_assemblies)
+               return;
+
+       gather_coverage_statements ();
+       fprintf (outfile, "\nCoverage Summary:\n");
+
+       if (coverage_outfile) {
+               fprintf (coverage_outfile, "<?xml version=\"1.0\"?>\n");
+               fprintf (coverage_outfile, "<coverage version=\"0.3\">\n");
+       }
+
+       g_ptr_array_sort (coverage_assemblies, sort_assemblies);
+
+       for (guint i = 0; i < coverage_assemblies->len; i++) {
+               CoverageAssembly *assembly = coverage_assemblies->pdata[i];
+               GPtrArray *classes;
+
+               if (assembly->number_of_methods != 0) {
+                       int percentage = ((assembly->fully_covered + assembly->partially_covered) * 100) / assembly->number_of_methods;
+                       fprintf (outfile, "\t%s (%s) %d%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, percentage, assembly->number_of_methods, assembly->fully_covered);
+               } else
+                       fprintf (outfile, "\t%s (%s) ?%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, assembly->number_of_methods, assembly->fully_covered);
+
+               if (coverage_outfile) {
+                       char *escaped_name, *escaped_filename;
+                       escaped_name = escape_string_for_xml (assembly->name);
+                       escaped_filename = escape_string_for_xml (assembly->filename);
+
+                       fprintf (coverage_outfile, "\t<assembly name=\"%s\" guid=\"%s\" filename=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n", escaped_name, assembly->guid, escaped_filename, assembly->number_of_methods, assembly->fully_covered, assembly->partially_covered);
+
+                       g_free (escaped_name);
+                       g_free (escaped_filename);
+               }
+
+               classes = g_hash_table_lookup (coverage_assembly_classes, assembly->name);
+               if (classes) {
+                       for (guint j = 0; j < classes->len; j++) {
+                               CoverageClass *klass = classes->pdata[j];
+
+                               if (klass->number_of_methods > 0) {
+                                       int percentage = ((klass->fully_covered + klass->partially_covered) * 100) / klass->number_of_methods;
+                                       fprintf (outfile, "\t\t%s %d%% covered (%d methods - %d covered)\n", klass->class_name, percentage, klass->number_of_methods, klass->fully_covered);
+                               } else
+                                       fprintf (outfile, "\t\t%s ?%% covered (%d methods - %d covered)\n", klass->class_name, klass->number_of_methods, klass->fully_covered);
+
+                               if (coverage_outfile) {
+                                       char *escaped_name;
+                                       escaped_name = escape_string_for_xml (klass->class_name);
+
+                                       fprintf (coverage_outfile, "\t\t<class name=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n", escaped_name, klass->number_of_methods, klass->fully_covered, klass->partially_covered);
+                                       g_free (escaped_name);
+                               }
+                       }
+               }
+       }
+
+       for (guint i = 0; i < coverage_methods->len; i++) {
+               CoverageMethod *method = coverage_methods->pdata[i];
+
+               if (coverage_outfile) {
+                       char *escaped_assembly, *escaped_class, *escaped_method, *escaped_sig, *escaped_filename;
+
+                       escaped_assembly = escape_string_for_xml (method->assembly_name);
+                       escaped_class = escape_string_for_xml (method->class_name);
+                       escaped_method = escape_string_for_xml (method->method_name);
+                       escaped_sig = escape_string_for_xml (method->method_signature);
+                       escaped_filename = escape_string_for_xml (method->filename);
+
+                       fprintf (coverage_outfile, "\t<method assembly=\"%s\" class=\"%s\" name=\"%s (%s)\" filename=\"%s\" token=\"%d\">\n", escaped_assembly, escaped_class, escaped_method, escaped_sig, escaped_filename, method->token);
+
+                       g_free (escaped_assembly);
+                       g_free (escaped_class);
+                       g_free (escaped_method);
+                       g_free (escaped_sig);
+                       g_free (escaped_filename);
+
+                       for (guint j = 0; j < method->coverage->len; j++) {
+                               CoverageCoverage *coverage = method->coverage->pdata[j];
+                               fprintf (coverage_outfile, "\t\t<statement offset=\"%d\" counter=\"%d\" line=\"%d\" column=\"%d\"/>\n", coverage->offset, coverage->count, coverage->line, coverage->column);
+                       }
+                       fprintf (coverage_outfile, "\t</method>\n");
+               }
+       }
+
+       if (coverage_outfile) {
+               fprintf (coverage_outfile, "</coverage>\n");
+               fclose (coverage_outfile);
+               coverage_outfile = NULL;
+       }
+}
+
 static void
 flush_context (ProfContext *ctx)
 {
@@ -2984,7 +3493,7 @@ flush_context (ProfContext *ctx)
        }
 }
 
-static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,heapshot,counters";
+static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,heapshot,counters,coverage";
 
 static const char*
 match_option (const char *p, const char *opt)
@@ -3064,6 +3573,11 @@ print_reports (ProfContext *ctx, const char *reps, int parse_only)
                                dump_counters ();
                        continue;
                }
+               if ((opt = match_option (p, "coverage")) != p) {
+                       if (!parse_only)
+                               dump_coverage ();
+                       continue;
+               }
                return 0;
        }
        return 1;
@@ -3092,7 +3606,7 @@ usage (void)
        printf ("Options:\n");
        printf ("\t--help               display this help\n");
        printf ("\t--out=FILE           write to FILE instead of stdout\n");
-       printf ("\t--traces             collect and show backtraces\n"); 
+       printf ("\t--traces             collect and show backtraces\n");
        printf ("\t--maxframes=NUM      limit backtraces to NUM entries\n");
        printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
        printf ("\t                     %s\n", reports);
@@ -3107,6 +3621,7 @@ usage (void)
        printf ("\t--time=FROM-TO       consider data FROM seconds from startup up to TO seconds\n");
        printf ("\t--verbose            increase verbosity level\n");
        printf ("\t--debug              display decoding debug info for mprof-report devs\n");
+       printf ("\t--coverage-out=FILE  write the coverage info to FILE as XML\n");
 }
 
 int
@@ -3218,6 +3733,13 @@ main (int argc, char *argv[])
                } else if (strcmp ("--traces", argv [i]) == 0) {
                        show_traces = 1;
                        collect_traces = 1;
+               } else if (strncmp ("--coverage-out=", argv [i], 15) == 0) {
+                       const char *val = argv [i] + 15;
+                       coverage_outfile = fopen (val, "w");
+                       if (!coverage_outfile) {
+                               printf ("Cannot open output file: %s\n", val);
+                               return 1;
+                       }
                } else {
                        break;
                }
@@ -3238,4 +3760,3 @@ main (int argc, char *argv[])
        print_reports (ctx, reports, 0);
        return 0;
 }
-