NaCl runtime fixes
[mono.git] / mono / profiler / decode.c
index fc2c13fa7e39df15c0885072ebeca7216dbd5b52..1383ae90b27e10fb2f9434cd9e856184a82fd5d3 100644 (file)
 #include "utils.c"
 #include "proflog.h"
 #include <string.h>
+#include <assert.h>
 #include <stdio.h>
-#if !defined(__APPLE__)
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
 #include <malloc.h>
 #endif
 #include <unistd.h>
 #include <stdlib.h>
-#ifdef HOST_WIN32
-#undef HAVE_ZLIB
-#endif
-#if defined (HAVE_ZLIB)
+#if defined (HAVE_SYS_ZLIB)
 #include <zlib.h>
 #endif
 #include <mono/metadata/profiler.h>
 #define HASH_SIZE 9371
 #define SMALL_HASH_SIZE 31
 
+#if defined(__native_client__) || defined(__native_client_codegen__)
+volatile int __nacl_thread_suspension_needed = 0;
+void __nacl_suspend_thread_if_needed() {}
+#endif
+
 static int debug = 0;
 static int collect_traces = 0;
 static int show_traces = 0;
@@ -44,6 +47,14 @@ static uint64_t time_to = 0xffffffffffffffffULL;
 static uint64_t startup_time = 0;
 static FILE* outfile = NULL;
 
+static int32_t
+read_int16 (unsigned char *p)
+{
+       int32_t value = *p++;
+       value |= (*p++) << 8;
+       return value;
+}
+
 static int32_t
 read_int32 (unsigned char *p)
 {
@@ -160,8 +171,11 @@ lookup_class (intptr_t klass)
        ClassDesc *cd = class_hash [slot];
        while (cd && cd->klass != klass)
                cd = cd->next;
-       if (!cd)
-               return add_class (klass, "unresolved class");
+       if (!cd) {
+               char buf [128];
+               snprintf (buf, sizeof (buf), "unresolved class %p", (void*)klass);
+               return add_class (klass, buf);
+       }
        return cd;
 }
 
@@ -173,6 +187,7 @@ struct _MethodDesc {
        intptr_t code;
        int len;
        int recurse_count;
+       int sample_hits;
        uint64_t calls;
        uint64_t total_time;
        uint64_t callee_time;
@@ -223,16 +238,391 @@ lookup_method (intptr_t method)
        MethodDesc *cd = method_hash [slot];
        while (cd && cd->method != method)
                cd = cd->next;
-       if (!cd)
-               return add_method (method, "unknown method", 0, 0);
+       if (!cd) {
+               char buf [128];
+               snprintf (buf, sizeof (buf), "unknown method %p", (void*)method);
+               return add_method (method, buf, 0, 0);
+       }
        return cd;
 }
 
+static int num_stat_samples = 0;
+static int size_stat_samples = 0;
+uintptr_t *stat_samples = NULL;
+int *stat_sample_desc = NULL;
+
+static void
+add_stat_sample (int type, uintptr_t ip) {
+       if (num_stat_samples == size_stat_samples) {
+               size_stat_samples *= 2;
+               if (!size_stat_samples)
+               size_stat_samples = 32;
+               stat_samples = realloc (stat_samples, size_stat_samples * sizeof (uintptr_t));
+               stat_sample_desc = realloc (stat_sample_desc, size_stat_samples * sizeof (int));
+       }
+       stat_samples [num_stat_samples] = ip;
+       stat_sample_desc [num_stat_samples++] = type;
+}
+
+static MethodDesc*
+lookup_method_by_ip (uintptr_t ip)
+{
+       int i;
+       MethodDesc* m;
+       /* dumb */
+       for (i = 0; i < HASH_SIZE; ++i) {
+               m = method_hash [i];
+               while (m) {
+                       //printf ("checking %p against %p-%p\n", (void*)ip, (void*)(m->code), (void*)(m->code + m->len));
+                       if (ip >= (uintptr_t)m->code && ip < (uintptr_t)m->code + m->len) {
+                               return m;
+                       }
+                       m = m->next;
+               }
+       }
+       return NULL;
+}
+
+static int
+compare_method_samples (const void *a, const void *b)
+{
+       MethodDesc *const*A = a;
+       MethodDesc *const*B = b;
+       if ((*A)->sample_hits == (*B)->sample_hits)
+               return 0;
+       if ((*B)->sample_hits < (*A)->sample_hits)
+               return -1;
+       return 1;
+}
+
+typedef struct _UnmanagedSymbol UnmanagedSymbol;
+struct _UnmanagedSymbol {
+       UnmanagedSymbol *parent;
+       char *name;
+       int is_binary;
+       uintptr_t addr;
+       uintptr_t size;
+       uintptr_t sample_hits;
+};
+
+static UnmanagedSymbol **usymbols = NULL;
+static int usymbols_size = 0;
+static int usymbols_num = 0;
+
+static int
+compare_usymbol_addr (const void *a, const void *b)
+{
+       UnmanagedSymbol *const*A = a;
+       UnmanagedSymbol *const*B = b;
+       if ((*B)->addr == (*A)->addr)
+               return 0;
+       if ((*B)->addr > (*A)->addr)
+               return -1;
+       return 1;
+}
+
+static int
+compare_usymbol_samples (const void *a, const void *b)
+{
+       UnmanagedSymbol *const*A = a;
+       UnmanagedSymbol *const*B = b;
+       if ((*B)->sample_hits == (*A)->sample_hits)
+               return 0;
+       if ((*B)->sample_hits < (*A)->sample_hits)
+               return -1;
+       return 1;
+}
+
+static void
+add_unmanaged_symbol (uintptr_t addr, char *name, uintptr_t size)
+{
+       UnmanagedSymbol *sym;
+       if (usymbols_num == usymbols_size) {
+               int new_size = usymbols_size * 2;
+               if (!new_size)
+                       new_size = 16;
+               usymbols = realloc (usymbols, sizeof (void*) * new_size);
+               usymbols_size = new_size;
+       }
+       sym = calloc (sizeof (UnmanagedSymbol), 1);
+       sym->addr = addr;
+       sym->name = name;
+       sym->size = size;
+       usymbols [usymbols_num++] = sym;
+}
+
+/* only valid after the symbols are sorted */
+static UnmanagedSymbol*
+lookup_unmanaged_symbol (uintptr_t addr)
+{
+       int r = usymbols_num - 1;
+       int l = 0;
+       UnmanagedSymbol *sym;
+       int last_best = -1;
+       while (r >= l) {
+               int m = (l + r) / 2;
+               sym = usymbols [m];
+               if (addr == sym->addr)
+                       return sym;
+               if (addr < sym->addr) {
+                       r = m - 1;
+               } else if (addr > sym->addr) {
+                       l = m + 1;
+                       last_best = m;
+               }
+       }
+       if (last_best >= 0 && (addr - usymbols [last_best]->addr) < 4096)
+               return usymbols [last_best];
+       return NULL;
+}
+
+/* we use the same structure for binaries */
+static UnmanagedSymbol **ubinaries = NULL;
+static int ubinaries_size = 0;
+static int ubinaries_num = 0;
+
+static void
+add_unmanaged_binary (uintptr_t addr, char *name, uintptr_t size)
+{
+       UnmanagedSymbol *sym;
+       if (ubinaries_num == ubinaries_size) {
+               int new_size = ubinaries_size * 2;
+               if (!new_size)
+                       new_size = 16;
+               ubinaries = realloc (ubinaries, sizeof (void*) * new_size);
+               ubinaries_size = new_size;
+       }
+       sym = calloc (sizeof (UnmanagedSymbol), 1);
+       sym->addr = addr;
+       sym->name = name;
+       sym->size = size;
+       sym->is_binary = 1;
+       ubinaries [ubinaries_num++] = sym;
+}
+
+static UnmanagedSymbol*
+lookup_unmanaged_binary (uintptr_t addr)
+{
+       int i;
+       for (i = 0; i < ubinaries_num; ++i) {
+               UnmanagedSymbol *ubin = ubinaries [i];
+               if (addr >= ubin->addr && addr < ubin->addr + ubin->size) {
+                       return ubin;
+               }
+       }
+       return NULL;
+}
+
+static const char*
+sample_type_name (int type)
+{
+       switch (type) {
+       case SAMPLE_CYCLES: return "cycles";
+       case SAMPLE_INSTRUCTIONS: return "instructions retired";
+       case SAMPLE_CACHE_MISSES: return "cache misses";
+       case SAMPLE_CACHE_REFS: return "cache references";
+       case SAMPLE_BRANCHES: return "executed branches";
+       case SAMPLE_BRANCH_MISSES: return "unpredicted branches";
+       }
+       return "unknown";
+}
+
+static void
+set_usym_parent (UnmanagedSymbol** cachedus, int count)
+{
+       int i;
+       for (i = 0; i < count; ++i) {
+               UnmanagedSymbol *ubin = lookup_unmanaged_binary (cachedus [i]->addr);
+               if (ubin == cachedus [i])
+                       continue;
+               cachedus [i]->parent = ubin;
+       }
+}
+
+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);
+       else
+               fprintf (outfile, "\t%6d %6.2f %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name);
+}
+
+static int
+sym_percent (uintptr_t sample_hits)
+{
+       double pc;
+       if (verbose)
+               return 1;
+       pc = sample_hits*100.0/num_stat_samples;
+       return pc >= 0.1;
+}
+
+static void
+dump_samples (void)
+{
+       int i, u;
+       int count = 0, msize = 0;
+       int unmanaged_hits = 0;
+       int unresolved_hits = 0;
+       MethodDesc** cachedm = NULL;
+       int ucount = 0, usize = 0;
+       UnmanagedSymbol** cachedus = NULL;
+       if (!num_stat_samples)
+               return;
+       qsort (usymbols, usymbols_num, sizeof (UnmanagedSymbol*), compare_usymbol_addr);
+       for (i = 0; i < num_stat_samples; ++i) {
+               MethodDesc *m = lookup_method_by_ip (stat_samples [i]);
+               if (m) {
+                       if (!m->sample_hits) {
+                               if (count == msize) {
+                                       msize *= 2;
+                                       if (!msize)
+                                               msize = 4;
+                                       cachedm = realloc (cachedm, sizeof (void*) * msize);
+                               }
+                               cachedm [count++] = m;
+                       }
+                       m->sample_hits++;
+               } else {
+                       UnmanagedSymbol *usym = lookup_unmanaged_symbol (stat_samples [i]);
+                       if (!usym) {
+                               unresolved_hits++;
+                               //printf ("unmanaged hit at %p\n", (void*)stat_samples [i]);
+                               usym = lookup_unmanaged_binary (stat_samples [i]);
+                       }
+                       if (usym) {
+                               if (!usym->sample_hits) {
+                                       if (ucount == usize) {
+                                               usize *= 2;
+                                               if (!usize)
+                                                       usize = 4;
+                                               cachedus = realloc (cachedus, sizeof (void*) * usize);
+                                       }
+                                       cachedus [ucount++] = usym;
+                               }
+                               usym->sample_hits++;
+                       }
+                       unmanaged_hits++;
+               }
+       }
+       qsort (cachedm, count, sizeof (MethodDesc*), compare_method_samples);
+       qsort (cachedus, ucount, sizeof (UnmanagedSymbol*), compare_usymbol_samples);
+       set_usym_parent (cachedus, ucount);
+       fprintf (outfile, "\nStatistical samples summary\n");
+       fprintf (outfile, "\tSample type: %s\n", sample_type_name (stat_sample_desc [0]));
+       fprintf (outfile, "\tUnmanaged hits:  %6d (%4.1f%%)\n", unmanaged_hits, (100.0*unmanaged_hits)/num_stat_samples);
+       fprintf (outfile, "\tManaged hits:    %6d (%4.1f%%)\n", num_stat_samples - unmanaged_hits, (100.0*(num_stat_samples-unmanaged_hits))/num_stat_samples);
+       fprintf (outfile, "\tUnresolved hits: %6d (%4.1f%%)\n", unresolved_hits, (100.0*unresolved_hits)/num_stat_samples);
+       fprintf (outfile, "\t%6s %6s %s\n", "Hits", "%", "Method name");
+       i = 0;
+       u = 0;
+       while (i < count || u < ucount) {
+               if (i < count) {
+                       MethodDesc *m = cachedm [i];
+                       if (u < ucount) {
+                               UnmanagedSymbol *um = cachedus [u];
+                               if (um->sample_hits > m->sample_hits) {
+                                       if (!sym_percent (um->sample_hits))
+                                               break;
+                                       print_usym (um);
+                                       u++;
+                                       continue;
+                               }
+                       }
+                       if (!sym_percent (m->sample_hits))
+                               break;
+                       fprintf (outfile, "\t%6d %6.2f %s\n", m->sample_hits, m->sample_hits*100.0/num_stat_samples, m->name);
+                       i++;
+                       continue;
+               }
+               if (u < ucount) {
+                       UnmanagedSymbol *um = cachedus [u];
+                       if (!sym_percent (um->sample_hits))
+                               break;
+                       print_usym (um);
+                       u++;
+                       continue;
+               }
+       }
+}
+
+typedef struct _HeapClassDesc HeapClassDesc;
 typedef struct {
+       HeapClassDesc *klass;
+       uint64_t count;
+} HeapClassRevRef;
+
+struct _HeapClassDesc {
        ClassDesc *klass;
        int64_t count;
        int64_t total_size;
-} HeapClassDesc;
+       HeapClassRevRef *rev_hash;
+       int rev_hash_size;
+       int rev_count;
+       uintptr_t pinned_references;
+       uintptr_t root_references;
+};
+
+static int
+add_rev_class_hashed (HeapClassRevRef *rev_hash, uintptr_t size, HeapClassDesc *hklass, uint64_t value)
+{
+       uintptr_t i;
+       uintptr_t start_pos;
+       start_pos = (hklass->klass->klass >> 2) % size;
+       assert (start_pos < size);
+       i = start_pos;
+       do {
+               if (rev_hash [i].klass == hklass) {
+                       rev_hash [i].count += value;
+                       return 0;
+               } else if (!rev_hash [i].klass) {
+                       rev_hash [i].klass = hklass;
+                       rev_hash [i].count += value;
+                       start_pos = 0;
+                       for (i = 0; i < size; ++i)
+                               if (rev_hash [i].klass && rev_hash [i].klass->klass == hklass->klass)
+                                       start_pos ++;
+                       assert (start_pos == 1);
+                       return 1;
+               }
+               /* wrap around */
+               if (++i == size)
+                       i = 0;
+       } while (i != start_pos);
+       /* should not happen */
+       printf ("failed revref store\n");
+       return 0;
+}
+
+static void
+add_heap_class_rev (HeapClassDesc *from, HeapClassDesc *to)
+{
+       uintptr_t i;
+       if (to->rev_count * 2 >= to->rev_hash_size) {
+               HeapClassRevRef *n;
+               uintptr_t old_size = to->rev_hash_size;
+               to->rev_hash_size *= 2;
+               if (to->rev_hash_size == 0)
+                       to->rev_hash_size = 4;
+               n = calloc (sizeof (HeapClassRevRef) * to->rev_hash_size, 1);
+               for (i = 0; i < old_size; ++i) {
+                       if (to->rev_hash [i].klass)
+                               add_rev_class_hashed (n, to->rev_hash_size, to->rev_hash [i].klass, to->rev_hash [i].count);
+               }
+               if (to->rev_hash)
+                       free (to->rev_hash);
+               to->rev_hash = n;
+       }
+       to->rev_count += add_rev_class_hashed (to->rev_hash, to->rev_hash_size, from, 1);
+}
+
+typedef struct {
+       uintptr_t objaddr;
+       HeapClassDesc *hklass;
+       uintptr_t num_refs;
+       uintptr_t refs [0];
+} HeapObjectDesc;
 
 typedef struct _HeapShot HeapShot;
 struct _HeapShot {
@@ -242,27 +632,28 @@ struct _HeapShot {
        int hash_size;
        HeapClassDesc **class_hash;
        HeapClassDesc **sorted;
+       HeapObjectDesc **objects_hash;
+       uintptr_t objects_count;
+       uintptr_t objects_hash_size;
+       uintptr_t num_roots;
+       uintptr_t *roots;
+       uintptr_t *roots_extra;
+       int *roots_types;
 };
 
 static HeapShot *heap_shots = NULL;
-static HeapShot *current_heap_shot = NULL;
+static int num_heap_shots = 0;
 
 static HeapShot*
 new_heap_shot (uint64_t timestamp)
 {
-       HeapShot *p;
        HeapShot *hs = calloc (sizeof (HeapShot), 1);
        hs->hash_size = 4;
        hs->class_hash = calloc (sizeof (void*), hs->hash_size);
        hs->timestamp = timestamp;
-       /* append it to the list */
-       p = heap_shots;
-       while (p && p->next)
-               p = p->next;
-       if (p)
-               p->next = hs;
-       else
-               heap_shots = hs;
+       num_heap_shots++;
+       hs->next = heap_shots;
+       heap_shots = hs;
        return hs;
 }
 
@@ -287,22 +678,28 @@ heap_class_lookup (HeapShot *hs, ClassDesc *klass)
 }
 
 static int
-add_heap_hashed (HeapClassDesc **hash, int hsize, ClassDesc *klass, uint64_t size, uint64_t count)
+add_heap_hashed (HeapClassDesc **hash, HeapClassDesc **retv, uintptr_t hsize, ClassDesc *klass, uint64_t size, uint64_t count)
 {
-       int i;
-       unsigned int start_pos;
+       uintptr_t i;
+       uintptr_t start_pos;
        start_pos = ((uintptr_t)klass->klass >> 2) % hsize;
        i = start_pos;
        do {
                if (hash [i] && hash [i]->klass == klass) {
                        hash [i]->total_size += size;
                        hash [i]->count += count;
+                       *retv = hash [i];
                        return 0;
                } else if (!hash [i]) {
+                       if (*retv) {
+                               hash [i] = *retv;
+                               return 1;
+                       }
                        hash [i] = calloc (sizeof (HeapClassDesc), 1);
                        hash [i]->klass = klass;
                        hash [i]->total_size += size;
                        hash [i]->count += count;
+                       *retv = hash [i];
                        return 1;
                }
                /* wrap around */
@@ -314,9 +711,10 @@ add_heap_hashed (HeapClassDesc **hash, int hsize, ClassDesc *klass, uint64_t siz
        return 0;
 }
 
-static void
-add_heap_shot_obj (HeapShot *hs, ClassDesc *klass, uint64_t size)
+static HeapClassDesc*
+add_heap_shot_class (HeapShot *hs, ClassDesc *klass, uint64_t size)
 {
+       HeapClassDesc *res;
        int i;
        if (hs->class_count * 2 >= hs->hash_size) {
                HeapClassDesc **n;
@@ -326,16 +724,227 @@ add_heap_shot_obj (HeapShot *hs, ClassDesc *klass, uint64_t size)
                        hs->hash_size = 4;
                n = calloc (sizeof (void*) * hs->hash_size, 1);
                for (i = 0; i < old_size; ++i) {
+                       res = hs->class_hash [i];
                        if (hs->class_hash [i])
-                               add_heap_hashed (n, hs->hash_size, hs->class_hash [i]->klass, hs->class_hash [i]->total_size, hs->class_hash [i]->count);
+                               add_heap_hashed (n, &res, hs->hash_size, hs->class_hash [i]->klass, hs->class_hash [i]->total_size, hs->class_hash [i]->count);
                }
                if (hs->class_hash)
                        free (hs->class_hash);
                hs->class_hash = n;
        }
-       hs->class_count += add_heap_hashed (hs->class_hash, hs->hash_size, klass, size, 1);
+       res = NULL;
+       hs->class_count += add_heap_hashed (hs->class_hash, &res, hs->hash_size, klass, size, 1);
+       //if (res->count == 1)
+       //      printf ("added heap class: %s\n", res->klass->name);
+       return res;
 }
 
+static HeapObjectDesc*
+alloc_heap_obj (uintptr_t objaddr, HeapClassDesc *hklass, uintptr_t num_refs)
+{
+       HeapObjectDesc* ho = calloc (sizeof (HeapObjectDesc) + num_refs * sizeof (uintptr_t), 1);
+       ho->objaddr = objaddr;
+       ho->hklass = hklass;
+       ho->num_refs = num_refs;
+       return ho;
+}
+
+static uintptr_t
+heap_shot_find_obj_slot (HeapShot *hs, uintptr_t objaddr)
+{
+       uintptr_t i;
+       uintptr_t start_pos;
+       HeapObjectDesc **hash = hs->objects_hash;
+       start_pos = ((uintptr_t)objaddr >> 3) % hs->objects_hash_size;
+       i = start_pos;
+       do {
+               if (hash [i] && hash [i]->objaddr == objaddr) {
+                       return i;
+               } else if (!hash [i]) {
+                       break; /* fail */
+               }
+               /* wrap around */
+               if (++i == hs->objects_hash_size)
+                       i = 0;
+       } while (i != start_pos);
+       /* should not happen */
+       //printf ("failed heap obj slot\n");
+       return -1;
+}
+
+static HeapObjectDesc*
+heap_shot_obj_add_refs (HeapShot *hs, uintptr_t objaddr, uintptr_t num, uintptr_t *ref_offset)
+{
+       HeapObjectDesc **hash = hs->objects_hash;
+       uintptr_t i = heap_shot_find_obj_slot (hs, objaddr);
+       if (i >= 0) {
+               HeapObjectDesc* ho = alloc_heap_obj (objaddr, hash [i]->hklass, hash [i]->num_refs + num);
+               *ref_offset = hash [i]->num_refs;
+               memcpy (ho->refs, hash [i]->refs, hash [i]->num_refs * sizeof (uintptr_t));
+               free (hash [i]);
+               hash [i] = ho;
+               return ho;
+       }
+       /* should not happen */
+       printf ("failed heap obj update\n");
+       return NULL;
+       
+}
+
+static uintptr_t
+add_heap_hashed_obj (HeapObjectDesc **hash, uintptr_t hsize, HeapObjectDesc *obj)
+{
+       uintptr_t i;
+       uintptr_t start_pos;
+       start_pos = ((uintptr_t)obj->objaddr >> 3) % hsize;
+       i = start_pos;
+       do {
+               if (hash [i] && hash [i]->objaddr == obj->objaddr) {
+                       printf ("duplicate object!\n");
+                       return 0;
+               } else if (!hash [i]) {
+                       hash [i] = obj;
+                       return 1;
+               }
+               /* wrap around */
+               if (++i == hsize)
+                       i = 0;
+       } while (i != start_pos);
+       /* should not happen */
+       printf ("failed heap obj store\n");
+       return 0;
+}
+
+static void
+add_heap_shot_obj (HeapShot *hs, HeapObjectDesc *obj)
+{
+       uintptr_t i;
+       if (hs->objects_count * 2 >= hs->objects_hash_size) {
+               HeapObjectDesc **n;
+               uintptr_t old_size = hs->objects_hash_size;
+               hs->objects_hash_size *= 2;
+               if (hs->objects_hash_size == 0)
+                       hs->objects_hash_size = 4;
+               n = calloc (sizeof (void*) * hs->objects_hash_size, 1);
+               for (i = 0; i < old_size; ++i) {
+                       if (hs->objects_hash [i])
+                               add_heap_hashed_obj (n, hs->objects_hash_size, hs->objects_hash [i]);
+               }
+               if (hs->objects_hash)
+                       free (hs->objects_hash);
+               hs->objects_hash = n;
+       }
+       hs->objects_count += add_heap_hashed_obj (hs->objects_hash, hs->objects_hash_size, obj);
+}
+
+static void
+heap_shot_resolve_reverse_refs (HeapShot *hs)
+{
+       uintptr_t i;
+       for (i = 0; i < hs->objects_hash_size; ++i) {
+               uintptr_t r;
+               HeapObjectDesc *ho = hs->objects_hash [i];
+               if (!ho)
+                       continue;
+               for (r = 0; r < ho->num_refs; ++r) {
+                       uintptr_t oi = heap_shot_find_obj_slot (hs, ho->refs [r]);
+                       add_heap_class_rev (ho->hklass, hs->objects_hash [oi]->hklass);
+               }
+       }
+}
+
+#define MARK_GRAY 1
+#define MARK_BLACK 2
+
+static void
+heap_shot_mark_objects (HeapShot *hs)
+{
+       uintptr_t i, oi, r;
+       unsigned char *marks;
+       HeapObjectDesc *obj, *ref;
+       int marked_some;
+       uintptr_t num_marked = 0, num_unmarked;
+       for (i = 0; i < hs->num_roots; ++i) {
+               HeapClassDesc *cd;
+               oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
+               if (oi == -1) {
+                       continue;
+               }
+               obj = hs->objects_hash [oi];
+               cd = obj->hklass;
+               if (hs->roots_types [i] & MONO_PROFILE_GC_ROOT_PINNING)
+                       cd->pinned_references++;
+               cd->root_references++;
+       }
+       if (!debug)
+               return;
+       /* consistency checks: it seems not all the objects are walked in the heap in some cases */
+       marks = calloc (hs->objects_hash_size, 1);
+       if (!marks)
+               return;
+       for (i = 0; i < hs->num_roots; ++i) {
+               oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
+               if (oi == -1) {
+                       fprintf (outfile, "root type 0x%x for obj %p (%s) not found in heap\n", hs->roots_types [i], (void*)hs->roots [i], lookup_class (hs->roots_extra [i])->name);
+                       continue;
+               }
+               obj = hs->objects_hash [oi];
+               if (!marks [oi]) {
+                       marks [oi] = obj->num_refs? MARK_GRAY: MARK_BLACK;
+                       num_marked++;
+               }
+       }
+       marked_some = 1;
+       while (marked_some) {
+               marked_some = 0;
+               for (i = 0; i < hs->objects_hash_size; ++i) {
+                       if (marks [i] != MARK_GRAY)
+                               continue;
+                       marks [i] = MARK_BLACK;
+                       obj = hs->objects_hash [i];
+                       for (r = 0; r < obj->num_refs; ++r) {
+                               oi = heap_shot_find_obj_slot (hs, obj->refs [r]);
+                               if (oi == -1) {
+                                       fprintf (outfile, "referenced obj %p not found in heap\n", (void*)obj->refs [r]);
+                                       continue;
+                               }
+                               ref = hs->objects_hash [oi];
+                               if (!marks [oi]) {
+                                       marks [oi] = ref->num_refs? MARK_GRAY: MARK_BLACK;
+                               }
+                       }
+                       marked_some++;
+               }
+       }
+
+       num_unmarked = 0;
+       for (i = 0; i < hs->objects_hash_size; ++i) {
+               if (hs->objects_hash [i] && !marks [i]) {
+                       num_unmarked++;
+                       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);
+       free (marks);
+}
+
+static void
+heap_shot_free_objects (HeapShot *hs)
+{
+       uintptr_t i;
+       for (i = 0; i < hs->objects_hash_size; ++i) {
+               HeapObjectDesc *ho = hs->objects_hash [i];
+               if (ho)
+                       free (ho);
+       }
+       if (hs->objects_hash)
+               free (hs->objects_hash);
+       hs->objects_hash = NULL;
+       hs->objects_hash_size = 0;
+       hs->objects_count = 0;
+}
+
+
 struct _BackTrace {
        BackTrace *next;
        unsigned int hash;
@@ -407,7 +1016,7 @@ typedef struct _ThreadContext ThreadContext;
 
 typedef struct {
        FILE *file;
-#if defined (HAVE_ZLIB)
+#if defined (HAVE_SYS_ZLIB)
        gzFile *gzfile;
 #endif
        unsigned char *buf;
@@ -416,6 +1025,8 @@ typedef struct {
        int version_major;
        int version_minor;
        int timer_overhead;
+       int pid;
+       int port;
        uint64_t startup_time;
        ThreadContext *threads;
        ThreadContext *current;
@@ -434,6 +1045,13 @@ struct _ThreadContext {
        MonitorDesc *monitor;
        int stack_size;
        int stack_id;
+       HeapShot *current_heap_shot;
+       uintptr_t num_roots;
+       uintptr_t size_roots;
+       uintptr_t *roots;
+       uintptr_t *roots_extra;
+       int *roots_types;
+       uint64_t gc_start_times [3];
 };
 
 static void
@@ -449,12 +1067,20 @@ static int
 load_data (ProfContext *ctx, int size)
 {
        ensure_buffer (ctx, size);
-#if defined (HAVE_ZLIB)
-       if (ctx->gzfile)
-               return gzread (ctx->gzfile, ctx->buf, size) == size;
-       else
+#if defined (HAVE_SYS_ZLIB)
+       if (ctx->gzfile) {
+               int r = gzread (ctx->gzfile, ctx->buf, size);
+               if (r == 0)
+                       return size == 0? 1: 0;
+               return r == size;
+       } else 
 #endif
-               return fread (ctx->buf, size, 1, ctx->file);
+       {
+               int r = fread (ctx->buf, size, 1, ctx->file);
+               if (r == 0)
+                       return size == 0? 1: 0;
+               return r;
+       }
 }
 
 static ThreadContext*
@@ -578,6 +1204,23 @@ add_trace_methods (MethodDesc **methods, int count, TraceDesc *trace, uint64_t v
        return bt;
 }
 
+static void
+thread_add_root (ThreadContext *ctx, uintptr_t obj, int root_type, uintptr_t extra_info)
+{
+       if (ctx->num_roots == ctx->size_roots) {
+               int new_size = ctx->size_roots * 2;
+               if (!new_size)
+                       new_size = 4;
+               ctx->roots = realloc (ctx->roots, new_size * sizeof (uintptr_t));
+               ctx->roots_extra = realloc (ctx->roots_extra, new_size * sizeof (uintptr_t));
+               ctx->roots_types = realloc (ctx->roots_types, new_size * sizeof (int));
+               ctx->size_roots = new_size;
+       }
+       ctx->roots_types [ctx->num_roots] = root_type;
+       ctx->roots_extra [ctx->num_roots] = extra_info;
+       ctx->roots [ctx->num_roots++] = obj;
+}
+
 static int
 compare_callc (const void *a, const void *b)
 {
@@ -638,7 +1281,6 @@ pop_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
 }
 
 typedef struct {
-       uint64_t start_time; /* move this per-thread? */
        uint64_t total_time;
        uint64_t max_time;
        int count;
@@ -650,6 +1292,7 @@ static int gc_resizes;
 typedef struct {
        uint64_t created;
        uint64_t destroyed;
+       uint64_t live;
        uint64_t max_live;
        TraceDesc traces;
 } HandleInfo;
@@ -746,6 +1389,19 @@ get_handle_name (int htype)
        }
 }
 
+static const char*
+get_root_name (int rtype)
+{
+       switch (rtype & MONO_PROFILE_GC_ROOT_TYPEMASK) {
+       case MONO_PROFILE_GC_ROOT_STACK: return "stack";
+       case MONO_PROFILE_GC_ROOT_FINALIZER: return "finalizer";
+       case MONO_PROFILE_GC_ROOT_HANDLE: return "handle";
+       case MONO_PROFILE_GC_ROOT_OTHER: return "other";
+       case MONO_PROFILE_GC_ROOT_MISC: return "misc";
+       default: return "unknown";
+       }
+}
+
 static MethodDesc**
 decode_bt (MethodDesc** sframes, int *size, unsigned char *p, unsigned char **endp, intptr_t ptr_base)
 {
@@ -841,12 +1497,22 @@ decode_buffer (ProfContext *ctx)
        int len, i;
        ThreadContext *thread;
 
-       file_offset = ftell (ctx->file);
+#ifdef HAVE_SYS_ZLIB
+       if (ctx->gzfile)
+               file_offset = gztell (ctx->gzfile);
+       else
+#endif
+               file_offset = ftell (ctx->file);
        if (!load_data (ctx, 48))
                return 0;
        p = ctx->buf;
-       if (read_int32 (p) != BUF_ID)
+       if (read_int32 (p) != BUF_ID) {
+               fprintf (outfile, "Incorrect buffer id: 0x%x\n", read_int32 (p));
+               for (i = 0; i < 48; ++i) {
+                       fprintf (outfile, "0x%x%s", p [i], i % 8?" ":"\n");
+               }
                return 0;
+       }
        len = read_int32 (p + 4);
        time_base = read_int64 (p + 8);
        ptr_base = read_int64 (p + 16);
@@ -889,16 +1555,16 @@ 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\n", gen - 1, gc_event_name (ev), time_base);
+                                       fprintf (outfile, "gc event for gen%d: %s at %llu (thread: 0x%x)\n", gen, gc_event_name (ev), time_base, thread->thread_id);
                                if (gen > 2) {
                                        fprintf (outfile, "incorrect gc gen: %d\n", gen);
                                        break;
                                }
                                if (ev == MONO_GC_EVENT_START) {
-                                       gc_info [gen].start_time = time_base;
+                                       thread->gc_start_times [gen] = time_base;
                                        gc_info [gen].count++;
                                } else if (ev == MONO_GC_EVENT_END) {
-                                       tdiff = time_base - gc_info [gen].start_time;
+                                       tdiff = time_base - thread->gc_start_times [gen];
                                        gc_info [gen].total_time += tdiff;
                                        if (tdiff > gc_info [gen].max_time)
                                                gc_info [gen].max_time = tdiff;
@@ -922,10 +1588,11 @@ decode_buffer (ProfContext *ctx)
                                if (htype > 3)
                                        return 0;
                                handle_info [htype].created++;
+                               handle_info [htype].live++;
                                add_trace_thread (thread, &handle_info [htype].traces, 1);
                                /* FIXME: we don't take into account timing here */
-                               if (handle_info [htype].created > handle_info [htype].max_live)
-                                       handle_info [htype].max_live = handle_info [htype].created;
+                               if (handle_info [htype].live > handle_info [htype].max_live)
+                                       handle_info [htype].max_live = handle_info [htype].live;
                                if (num_tracked_objects)
                                        track_handle (OBJ_ADDR (objdiff), htype, handle);
                                if (debug)
@@ -935,7 +1602,8 @@ decode_buffer (ProfContext *ctx)
                                uint32_t handle = decode_uleb128 (p, &p);
                                if (htype > 3)
                                        return 0;
-                               handle_info [htype].created--;
+                               handle_info [htype].destroyed ++;
+                               handle_info [htype].live--;
                                if (debug)
                                        fprintf (outfile, "handle (%s) %u destroyed\n", get_handle_name (htype), handle);
                        }
@@ -1044,7 +1712,7 @@ decode_buffer (ProfContext *ctx)
                                intptr_t codediff = decode_sleb128 (p, &p);
                                int codelen = decode_uleb128 (p, &p);
                                if (debug)
-                                       fprintf (outfile, "jitted method %p (%s), size: %d\n", (void*)(method_base), p, codelen);
+                                       fprintf (outfile, "jitted method %p (%s), size: %d, code: %p\n", (void*)(method_base), p, codelen, (void*)(ptr_base + codediff));
                                add_method (method_base, (char*)p, ptr_base + codediff, codelen);
                                while (*p) p++;
                                p++;
@@ -1067,38 +1735,90 @@ decode_buffer (ProfContext *ctx)
                case TYPE_HEAP: {
                        int subtype = *p & 0xf0;
                        if (subtype == TYPE_HEAP_OBJECT) {
+                               HeapObjectDesc *ho;
                                int i;
                                intptr_t objdiff = decode_sleb128 (p + 1, &p);
                                intptr_t ptrdiff = decode_sleb128 (p, &p);
                                uint64_t size = decode_uleb128 (p, &p);
-                               int num = decode_uleb128 (p, &p);
+                               uintptr_t num = decode_uleb128 (p, &p);
+                               uintptr_t ref_offset;
+                               uintptr_t last_obj_offset = 0;
                                ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
+                               if (size) {
+                                       HeapClassDesc *hcd = add_heap_shot_class (thread->current_heap_shot, cd, size);
+                                       if (collect_traces) {
+                                               ho = alloc_heap_obj (OBJ_ADDR (objdiff), hcd, num);
+                                               add_heap_shot_obj (thread->current_heap_shot, ho);
+                                               ref_offset = 0;
+                                       }
+                               } else {
+                                       if (collect_traces)
+                                               ho = heap_shot_obj_add_refs (thread->current_heap_shot, OBJ_ADDR (objdiff), num, &ref_offset);
+                               }
                                for (i = 0; i < num; ++i) {
                                        /* FIXME: use object distance to measure how good
                                         * the GC is at keeping related objects close
                                         */
+                                       uintptr_t offset = ctx->data_version > 1? last_obj_offset + decode_uleb128 (p, &p): -1;
                                        intptr_t obj1diff = decode_sleb128 (p, &p);
+                                       last_obj_offset = offset;
+                                       if (collect_traces)
+                                               ho->refs [ref_offset + i] = OBJ_ADDR (obj1diff);
                                        if (num_tracked_objects)
                                                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);
-                               if (size)
-                                       add_heap_shot_obj (current_heap_shot, cd, size);
+                       } else if (subtype == TYPE_HEAP_ROOT) {
+                               uintptr_t num = decode_uleb128 (p + 1, &p);
+                               uintptr_t gc_num = decode_uleb128 (p, &p);
+                               int i;
+                               for (i = 0; i < num; ++i) {
+                                       intptr_t objdiff = decode_sleb128 (p, &p);
+                                       int root_type = decode_uleb128 (p, &p);
+                                       /* we just discard the extra info for now */
+                                       uintptr_t extra_info = decode_uleb128 (p, &p);
+                                       if (debug)
+                                               fprintf (outfile, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff), get_root_name (root_type));
+                                       if (collect_traces)
+                                               thread_add_root (thread, OBJ_ADDR (objdiff), root_type, extra_info);
+                               }
                        } else if (subtype == TYPE_HEAP_END) {
                                uint64_t tdiff = decode_uleb128 (p + 1, &p);
                                LOG_TIME (time_base, tdiff);
                                time_base += tdiff;
                                if (debug)
                                        fprintf (outfile, "heap shot end\n");
-                               current_heap_shot = NULL;
+                               if (collect_traces) {
+                                       HeapShot *hs = thread->current_heap_shot;
+                                       if (hs && thread->num_roots) {
+                                               /* transfer the root ownershipt to the heapshot */
+                                               hs->num_roots = thread->num_roots;
+                                               hs->roots = thread->roots;
+                                               hs->roots_extra = thread->roots_extra;
+                                               hs->roots_types = thread->roots_types;
+                                       } else {
+                                               free (thread->roots);
+                                               free (thread->roots_extra);
+                                               free (thread->roots_types);
+                                       }
+                                       thread->num_roots = 0;
+                                       thread->size_roots = 0;
+                                       thread->roots = NULL;
+                                       thread->roots_extra = NULL;
+                                       thread->roots_types = NULL;
+                                       heap_shot_resolve_reverse_refs (hs);
+                                       heap_shot_mark_objects (hs);
+                                       heap_shot_free_objects (hs);
+                               }
+                               thread->current_heap_shot = NULL;
                        } else if (subtype == TYPE_HEAP_START) {
                                uint64_t tdiff = decode_uleb128 (p + 1, &p);
                                LOG_TIME (time_base, tdiff);
                                time_base += tdiff;
                                if (debug)
                                        fprintf (outfile, "heap shot start\n");
-                               current_heap_shot = new_heap_shot (time_base);
+                               thread->current_heap_shot = new_heap_shot (time_base);
                        }
                        break;
                }
@@ -1209,8 +1929,52 @@ decode_buffer (ProfContext *ctx)
                        }
                        break;
                }
+               case TYPE_SAMPLE: {
+                       int subtype = *p & 0xf0;
+                       if (subtype == TYPE_SAMPLE_HIT) {
+                               int i;
+                               int sample_type = decode_uleb128 (p + 1, &p);
+                               uint64_t tstamp = decode_uleb128 (p, &p);
+                               int count = decode_uleb128 (p, &p);
+                               for (i = 0; i < count; ++i) {
+                                       uintptr_t ip = ptr_base + decode_sleb128 (p, &p);
+                                       add_stat_sample (sample_type, ip);
+                                       if (debug)
+                                               fprintf (outfile, "sample hit, type: %d at %p\n", sample_type, (void*)ip);
+                               }
+                       } else if (subtype == TYPE_SAMPLE_USYM) {
+                               /* un unmanaged symbol description */
+                               uintptr_t addr = ptr_base + decode_sleb128 (p + 1, &p);
+                               uintptr_t size = decode_uleb128 (p, &p);
+                               char *name;
+                               name = pstrdup ((char*)p);
+                               add_unmanaged_symbol (addr, name, size);
+                               if (debug)
+                                       fprintf (outfile, "unmanaged symbol %s at %p\n", name, (void*)addr);
+                               while (*p) p++;
+                               p++;
+                       } else if (subtype == TYPE_SAMPLE_UBIN) {
+                               /* 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);
+                               uintptr_t size = decode_uleb128 (p, &p);
+                               char *name;
+                               LOG_TIME (time_base, tdiff);
+                               time_base += tdiff;
+                               name = pstrdup ((char*)p);
+                               add_unmanaged_binary (addr, name, size);
+                               if (debug)
+                                       fprintf (outfile, "unmanaged binary %s at %p\n", name, (void*)addr);
+                               while (*p) p++;
+                               p++;
+                       } else {
+                               return 0;
+                       }
+                       break;
+               }
                default:
-                       fprintf (outfile, "unhandled profiler event: 0x%x\n", *p);
+                       fprintf (outfile, "unhandled profiler event: 0x%x at file offset: %llu + %d (len: %d\n)\n", *p, file_offset, p - ctx->buf, len);
                        exit (1);
                }
        }
@@ -1233,13 +1997,14 @@ load_file (char *name)
                printf ("Cannot open file: %s\n", name);
                exit (1);
        }
-#if defined (HAVE_ZLIB)
-       ctx->gzfile = gzdopen (fileno (ctx->file), "rb");
+#if defined (HAVE_SYS_ZLIB)
+       if (ctx->file != stdin)
+               ctx->gzfile = gzdopen (fileno (ctx->file), "rb");
 #endif
        if (!load_data (ctx, 32))
                return NULL;
        p = ctx->buf;
-       if (read_int32 (p) != LOG_HEADER_ID || p [6] != LOG_DATA_VERSION)
+       if (read_int32 (p) != LOG_HEADER_ID || p [6] > LOG_DATA_VERSION)
                return NULL;
        ctx->version_major = p [4];
        ctx->version_minor = p [5];
@@ -1251,6 +2016,8 @@ load_file (char *name)
                return NULL;
        ctx->startup_time = read_int64 (p + 8);
        ctx->timer_overhead = read_int32 (p + 16);
+       ctx->pid = read_int32 (p + 24);
+       ctx->port = read_int16 (p + 28);
        return ctx;
 }
 
@@ -1289,7 +2056,11 @@ dump_header (ProfContext *ctx)
        fprintf (outfile, "\tProfiler version: %d.%d\n", ctx->version_major, ctx->version_minor);
        fprintf (outfile, "\tData version: %d\n", ctx->data_version);
        fprintf (outfile, "\tMean timer overhead: %d nanoseconds\n", ctx->timer_overhead);
-       fprintf (outfile, "\tProgram startup: %s\n", t);
+       fprintf (outfile, "\tProgram startup: %s", t);
+       if (ctx->pid)
+               fprintf (outfile, "\tProgram ID: %d\n", ctx->pid);
+       if (ctx->port)
+               fprintf (outfile, "\tServer listening on: %d\n", ctx->port);
 }
 
 static void
@@ -1402,6 +2173,27 @@ dump_gcs (void)
        }
 }
 
+static void
+dump_jit (void)
+{
+       int i;
+       int code_size = 0;
+       int compiled_methods = 0;
+       MethodDesc* m;
+       fprintf (outfile, "\nJIT summary\n");
+       for (i = 0; i < HASH_SIZE; ++i) {
+               m = method_hash [i];
+               for (m = method_hash [i]; m; m = m->next) {
+                       if (!m->code)
+                               continue;
+                       compiled_methods++;
+                       code_size += m->len;
+               }
+       }
+       fprintf (outfile, "\tCompiled methods: %d\n", compiled_methods);
+       fprintf (outfile, "\tGenerated code size: %d\n", code_size);
+}
+
 static void
 dump_allocations (void)
 {
@@ -1547,6 +2339,32 @@ compare_heap_class (const void *a, const void *b)
        return 1;
 }
 
+static int
+compare_rev_class (const void *a, const void *b)
+{
+       const HeapClassRevRef *A = a;
+       const HeapClassRevRef *B = b;
+       if (B->count == A->count)
+               return 0;
+       if (B->count < A->count)
+               return -1;
+       return 1;
+}
+
+static void
+dump_rev_claases (HeapClassRevRef *revs, int count)
+{
+       int j;
+       if (!show_traces)
+               return;
+       if (!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);
+       }
+}
+
 static void
 heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
 {
@@ -1567,12 +2385,14 @@ 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\n",
-               hs_num, (hs->timestamp - startup_time)/1000000000.0, size, count, ccount);
+       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);
        if (!verbose && ccount > 30)
                ccount = 30;
        fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
        for (i = 0; i < ccount; ++i) {
+               HeapClassRevRef *rev_sorted;
+               int j, k;
                HeapClassDesc *ocd = NULL;
                cd = sorted [i];
                if (last_hs)
@@ -1585,21 +2405,54 @@ heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
                } else {
                        fprintf (outfile, "\n");
                }
+               if (!collect_traces)
+                       continue;
+               rev_sorted = malloc (cd->rev_count * sizeof (HeapClassRevRef));
+               k = 0;
+               for (j = 0; j < cd->rev_hash_size; ++j) {
+                       if (cd->rev_hash [j].klass)
+                               rev_sorted [k++] = cd->rev_hash [j];
+               }
+               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);
+               dump_rev_claases (rev_sorted, cd->rev_count);
+               free (rev_sorted);
        }
+       free (sorted);
+}
+
+static int
+compare_heap_shots (const void *a, const void *b)
+{
+       HeapShot *const*A = a;
+       HeapShot *const*B = b;
+       if ((*B)->timestamp == (*A)->timestamp)
+               return 0;
+       if ((*B)->timestamp > (*A)->timestamp)
+               return -1;
+       return 1;
 }
 
 static void
 dump_heap_shots (void)
 {
+       HeapShot **hs_sorted;
        HeapShot *hs;
        HeapShot *last_hs = NULL;
        int i;
        if (!heap_shots)
                return;
+       hs_sorted = malloc (num_heap_shots * sizeof (void*));
        fprintf (outfile, "\nHeap shot summary\n");
        i = 0;
-       for (hs = heap_shots; hs; hs = hs->next) {
-               heap_shot_summary (hs, i++, last_hs);
+       for (hs = heap_shots; hs; hs = hs->next)
+               hs_sorted [i++] = hs;
+       qsort (hs_sorted, num_heap_shots, sizeof (void*), compare_heap_shots);
+       for (i = 0; i < num_heap_shots; ++i) {
+               hs = hs_sorted [i];
+               heap_shot_summary (hs, i, last_hs);
                last_hs = hs;
        }
 }
@@ -1618,7 +2471,7 @@ flush_context (ProfContext *ctx)
        }
 }
 
-static const char *reports = "header,gc,alloc,call,metadata,exception,monitor,thread,heapshot";
+static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,heapshot";
 
 static const char*
 match_option (const char *p, const char *opt)
@@ -1653,6 +2506,11 @@ print_reports (ProfContext *ctx, const char *reps, int parse_only)
                                dump_gcs ();
                        continue;
                }
+               if ((opt = match_option (p, "jit")) != p) {
+                       if (!parse_only)
+                               dump_jit ();
+                       continue;
+               }
                if ((opt = match_option (p, "alloc")) != p) {
                        if (!parse_only)
                                dump_allocations ();
@@ -1683,6 +2541,11 @@ print_reports (ProfContext *ctx, const char *reps, int parse_only)
                                dump_heap_shots ();
                        continue;
                }
+               if ((opt = match_option (p, "sample")) != p) {
+                       if (!parse_only)
+                               dump_samples ();
+                       continue;
+               }
                return 0;
        }
        return 1;