[xbuild] Use the env var $MSBuildExtensionsPath before trying other paths.
[mono.git] / mono / profiler / decode.c
1 /*
2  * decode.c: mprof-report program source: decode and analyze the log profiler data
3  *
4  * Author:
5  *   Paolo Molaro (lupus@ximian.com)
6  *
7  * Copyright 2010 Novell, Inc (http://www.novell.com)
8  */
9 #include <config.h>
10 #include "utils.c"
11 #include "proflog.h"
12 #include <string.h>
13 #include <assert.h>
14 #include <stdio.h>
15 #if !defined(__APPLE__) && !defined(__FreeBSD__)
16 #include <malloc.h>
17 #endif
18 #include <unistd.h>
19 #include <stdlib.h>
20 #if defined (HAVE_SYS_ZLIB)
21 #include <zlib.h>
22 #endif
23 #include <mono/metadata/profiler.h>
24 #include <mono/metadata/object.h>
25 #include <mono/metadata/debug-helpers.h>
26
27 #define HASH_SIZE 9371
28 #define SMALL_HASH_SIZE 31
29
30 static int debug = 0;
31 static int collect_traces = 0;
32 static int show_traces = 0;
33 static int trace_max = 6;
34 static int verbose = 0;
35 static uintptr_t *tracked_objects = 0;
36 static int num_tracked_objects = 0;
37 static uintptr_t thread_filter = 0;
38 static uint64_t find_size = 0;
39 static const char* find_name = NULL;
40 static uint64_t time_from = 0;
41 static uint64_t time_to = 0xffffffffffffffffULL;
42 static uint64_t startup_time = 0;
43 static FILE* outfile = NULL;
44
45 static int32_t
46 read_int16 (unsigned char *p)
47 {
48         int32_t value = *p++;
49         value |= (*p++) << 8;
50         return value;
51 }
52
53 static int32_t
54 read_int32 (unsigned char *p)
55 {
56         int32_t value = *p++;
57         value |= (*p++) << 8;
58         value |= (*p++) << 16;
59         value |= (uint32_t)(*p++) << 24;
60         return value;
61 }
62
63 static int64_t
64 read_int64 (unsigned char *p)
65 {
66         uint64_t value = *p++;
67         value |= (*p++) << 8;
68         value |= (*p++) << 16;
69         value |= (uint64_t)(*p++) << 24;
70         value |= (uint64_t)(*p++) << 32;
71         value |= (uint64_t)(*p++) << 40;
72         value |= (uint64_t)(*p++) << 48;
73         value |= (uint64_t)(*p++) << 54;
74         return value;
75 }
76
77 static char*
78 pstrdup (const char *s)
79 {
80         int len = strlen (s) + 1;
81         char *p = malloc (len);
82         memcpy (p, s, len);
83         return p;
84 }
85
86 static int num_images;
87 typedef struct _ImageDesc ImageDesc;
88 struct _ImageDesc {
89         ImageDesc *next;
90         intptr_t image;
91         char *filename;
92 };
93
94 static ImageDesc* image_hash [SMALL_HASH_SIZE] = {0};
95
96 static void
97 add_image (intptr_t image, char *name)
98 {
99         int slot = ((image >> 2) & 0xffff) % SMALL_HASH_SIZE;
100         ImageDesc *cd = malloc (sizeof (ImageDesc));
101         cd->image = image;
102         cd->filename = pstrdup (name);
103         cd->next = image_hash [slot];
104         image_hash [slot] = cd;
105         num_images++;
106 }
107
108 typedef struct _BackTrace BackTrace;
109 typedef struct {
110         uint64_t count;
111         BackTrace *bt;
112 } CallContext;
113
114 typedef struct {
115         int count;
116         int size;
117         CallContext *traces;
118 } TraceDesc;
119
120 typedef struct _ClassDesc ClassDesc;
121 struct _ClassDesc {
122         ClassDesc *next;
123         intptr_t klass;
124         char *name;
125         intptr_t allocs;
126         uint64_t alloc_size;
127         TraceDesc traces;
128 };
129
130 static ClassDesc* class_hash [HASH_SIZE] = {0};
131 static int num_classes = 0;
132
133 static ClassDesc*
134 add_class (intptr_t klass, const char *name)
135 {
136         int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
137         ClassDesc *cd;
138         cd = class_hash [slot];
139         while (cd && cd->klass != klass)
140                 cd = cd->next;
141         /* we resolved an unknown class (unless we had the code unloaded) */
142         if (cd) {
143                 /*printf ("resolved unknown: %s\n", name);*/
144                 free (cd->name);
145                 cd->name = pstrdup (name);
146                 return cd;
147         }
148         cd = calloc (sizeof (ClassDesc), 1);
149         cd->klass = klass;
150         cd->name = pstrdup (name);
151         cd->next = class_hash [slot];
152         cd->allocs = 0;
153         cd->alloc_size = 0;
154         cd->traces.count = 0;
155         cd->traces.size = 0;
156         cd->traces.traces = NULL;
157         class_hash [slot] = cd;
158         num_classes++;
159         return cd;
160 }
161
162 static ClassDesc *
163 lookup_class (intptr_t klass)
164 {
165         int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
166         ClassDesc *cd = class_hash [slot];
167         while (cd && cd->klass != klass)
168                 cd = cd->next;
169         if (!cd) {
170                 char buf [128];
171                 snprintf (buf, sizeof (buf), "unresolved class %p", (void*)klass);
172                 return add_class (klass, buf);
173         }
174         return cd;
175 }
176
177 typedef struct _MethodDesc MethodDesc;
178 struct _MethodDesc {
179         MethodDesc *next;
180         intptr_t method;
181         char *name;
182         intptr_t code;
183         int len;
184         int recurse_count;
185         int sample_hits;
186         uint64_t calls;
187         uint64_t total_time;
188         uint64_t callee_time;
189         uint64_t self_time;
190         TraceDesc traces;
191 };
192
193 static MethodDesc* method_hash [HASH_SIZE] = {0};
194 static int num_methods = 0;
195
196 static MethodDesc*
197 add_method (intptr_t method, const char *name, intptr_t code, int len)
198 {
199         int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
200         MethodDesc *cd;
201         cd = method_hash [slot];
202         while (cd && cd->method != method)
203                 cd = cd->next;
204         /* we resolved an unknown method (unless we had the code unloaded) */
205         if (cd) {
206                 cd->code = code;
207                 cd->len = len;
208                 /*printf ("resolved unknown: %s\n", name);*/
209                 free (cd->name);
210                 cd->name = pstrdup (name);
211                 return cd;
212         }
213         cd = calloc (sizeof (MethodDesc), 1);
214         cd->method = method;
215         cd->name = pstrdup (name);
216         cd->code = code;
217         cd->len = len;
218         cd->calls = 0;
219         cd->total_time = 0;
220         cd->traces.count = 0;
221         cd->traces.size = 0;
222         cd->traces.traces = NULL;
223         cd->next = method_hash [slot];
224         method_hash [slot] = cd;
225         num_methods++;
226         return cd;
227 }
228
229 static MethodDesc *
230 lookup_method (intptr_t method)
231 {
232         int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
233         MethodDesc *cd = method_hash [slot];
234         while (cd && cd->method != method)
235                 cd = cd->next;
236         if (!cd) {
237                 char buf [128];
238                 snprintf (buf, sizeof (buf), "unknown method %p", (void*)method);
239                 return add_method (method, buf, 0, 0);
240         }
241         return cd;
242 }
243
244 static int num_stat_samples = 0;
245 static int size_stat_samples = 0;
246 uintptr_t *stat_samples = NULL;
247 int *stat_sample_desc = NULL;
248
249 static void
250 add_stat_sample (int type, uintptr_t ip) {
251         if (num_stat_samples == size_stat_samples) {
252                 size_stat_samples *= 2;
253                 if (!size_stat_samples)
254                 size_stat_samples = 32;
255                 stat_samples = realloc (stat_samples, size_stat_samples * sizeof (uintptr_t));
256                 stat_sample_desc = realloc (stat_sample_desc, size_stat_samples * sizeof (int));
257         }
258         stat_samples [num_stat_samples] = ip;
259         stat_sample_desc [num_stat_samples++] = type;
260 }
261
262 static MethodDesc*
263 lookup_method_by_ip (uintptr_t ip)
264 {
265         int i;
266         MethodDesc* m;
267         /* dumb */
268         for (i = 0; i < HASH_SIZE; ++i) {
269                 m = method_hash [i];
270                 while (m) {
271                         //printf ("checking %p against %p-%p\n", (void*)ip, (void*)(m->code), (void*)(m->code + m->len));
272                         if (ip >= (uintptr_t)m->code && ip < (uintptr_t)m->code + m->len) {
273                                 return m;
274                         }
275                         m = m->next;
276                 }
277         }
278         return NULL;
279 }
280
281 static int
282 compare_method_samples (const void *a, const void *b)
283 {
284         MethodDesc *const*A = a;
285         MethodDesc *const*B = b;
286         if ((*A)->sample_hits == (*B)->sample_hits)
287                 return 0;
288         if ((*B)->sample_hits < (*A)->sample_hits)
289                 return -1;
290         return 1;
291 }
292
293 typedef struct _UnmanagedSymbol UnmanagedSymbol;
294 struct _UnmanagedSymbol {
295         UnmanagedSymbol *parent;
296         char *name;
297         int is_binary;
298         uintptr_t addr;
299         uintptr_t size;
300         uintptr_t sample_hits;
301 };
302
303 static UnmanagedSymbol **usymbols = NULL;
304 static int usymbols_size = 0;
305 static int usymbols_num = 0;
306
307 static int
308 compare_usymbol_addr (const void *a, const void *b)
309 {
310         UnmanagedSymbol *const*A = a;
311         UnmanagedSymbol *const*B = b;
312         if ((*B)->addr == (*A)->addr)
313                 return 0;
314         if ((*B)->addr > (*A)->addr)
315                 return -1;
316         return 1;
317 }
318
319 static int
320 compare_usymbol_samples (const void *a, const void *b)
321 {
322         UnmanagedSymbol *const*A = a;
323         UnmanagedSymbol *const*B = b;
324         if ((*B)->sample_hits == (*A)->sample_hits)
325                 return 0;
326         if ((*B)->sample_hits < (*A)->sample_hits)
327                 return -1;
328         return 1;
329 }
330
331 static void
332 add_unmanaged_symbol (uintptr_t addr, char *name, uintptr_t size)
333 {
334         UnmanagedSymbol *sym;
335         if (usymbols_num == usymbols_size) {
336                 int new_size = usymbols_size * 2;
337                 if (!new_size)
338                         new_size = 16;
339                 usymbols = realloc (usymbols, sizeof (void*) * new_size);
340                 usymbols_size = new_size;
341         }
342         sym = calloc (sizeof (UnmanagedSymbol), 1);
343         sym->addr = addr;
344         sym->name = name;
345         sym->size = size;
346         usymbols [usymbols_num++] = sym;
347 }
348
349 /* only valid after the symbols are sorted */
350 static UnmanagedSymbol*
351 lookup_unmanaged_symbol (uintptr_t addr)
352 {
353         int r = usymbols_num - 1;
354         int l = 0;
355         UnmanagedSymbol *sym;
356         int last_best = -1;
357         while (r >= l) {
358                 int m = (l + r) / 2;
359                 sym = usymbols [m];
360                 if (addr == sym->addr)
361                         return sym;
362                 if (addr < sym->addr) {
363                         r = m - 1;
364                 } else if (addr > sym->addr) {
365                         l = m + 1;
366                         last_best = m;
367                 }
368         }
369         if (last_best >= 0 && (addr - usymbols [last_best]->addr) < 4096)
370                 return usymbols [last_best];
371         return NULL;
372 }
373
374 /* we use the same structure for binaries */
375 static UnmanagedSymbol **ubinaries = NULL;
376 static int ubinaries_size = 0;
377 static int ubinaries_num = 0;
378
379 static void
380 add_unmanaged_binary (uintptr_t addr, char *name, uintptr_t size)
381 {
382         UnmanagedSymbol *sym;
383         if (ubinaries_num == ubinaries_size) {
384                 int new_size = ubinaries_size * 2;
385                 if (!new_size)
386                         new_size = 16;
387                 ubinaries = realloc (ubinaries, sizeof (void*) * new_size);
388                 ubinaries_size = new_size;
389         }
390         sym = calloc (sizeof (UnmanagedSymbol), 1);
391         sym->addr = addr;
392         sym->name = name;
393         sym->size = size;
394         sym->is_binary = 1;
395         ubinaries [ubinaries_num++] = sym;
396 }
397
398 static UnmanagedSymbol*
399 lookup_unmanaged_binary (uintptr_t addr)
400 {
401         int i;
402         for (i = 0; i < ubinaries_num; ++i) {
403                 UnmanagedSymbol *ubin = ubinaries [i];
404                 if (addr >= ubin->addr && addr < ubin->addr + ubin->size) {
405                         return ubin;
406                 }
407         }
408         return NULL;
409 }
410
411 static const char*
412 sample_type_name (int type)
413 {
414         switch (type) {
415         case SAMPLE_CYCLES: return "cycles";
416         case SAMPLE_INSTRUCTIONS: return "instructions retired";
417         case SAMPLE_CACHE_MISSES: return "cache misses";
418         case SAMPLE_CACHE_REFS: return "cache references";
419         case SAMPLE_BRANCHES: return "executed branches";
420         case SAMPLE_BRANCH_MISSES: return "unpredicted branches";
421         }
422         return "unknown";
423 }
424
425 static void
426 set_usym_parent (UnmanagedSymbol** cachedus, int count)
427 {
428         int i;
429         for (i = 0; i < count; ++i) {
430                 UnmanagedSymbol *ubin = lookup_unmanaged_binary (cachedus [i]->addr);
431                 if (ubin == cachedus [i])
432                         continue;
433                 cachedus [i]->parent = ubin;
434         }
435 }
436
437 static void
438 print_usym (UnmanagedSymbol* um)
439 {
440         if (um->parent)
441                 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);
442         else
443                 fprintf (outfile, "\t%6d %6.2f %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name);
444 }
445
446 static int
447 sym_percent (uintptr_t sample_hits)
448 {
449         double pc;
450         if (verbose)
451                 return 1;
452         pc = sample_hits*100.0/num_stat_samples;
453         return pc >= 0.1;
454 }
455
456 static void
457 dump_samples (void)
458 {
459         int i, u;
460         int count = 0, msize = 0;
461         int unmanaged_hits = 0;
462         int unresolved_hits = 0;
463         MethodDesc** cachedm = NULL;
464         int ucount = 0, usize = 0;
465         UnmanagedSymbol** cachedus = NULL;
466         if (!num_stat_samples)
467                 return;
468         qsort (usymbols, usymbols_num, sizeof (UnmanagedSymbol*), compare_usymbol_addr);
469         for (i = 0; i < num_stat_samples; ++i) {
470                 MethodDesc *m = lookup_method_by_ip (stat_samples [i]);
471                 if (m) {
472                         if (!m->sample_hits) {
473                                 if (count == msize) {
474                                         msize *= 2;
475                                         if (!msize)
476                                                 msize = 4;
477                                         cachedm = realloc (cachedm, sizeof (void*) * msize);
478                                 }
479                                 cachedm [count++] = m;
480                         }
481                         m->sample_hits++;
482                 } else {
483                         UnmanagedSymbol *usym = lookup_unmanaged_symbol (stat_samples [i]);
484                         if (!usym) {
485                                 unresolved_hits++;
486                                 //printf ("unmanaged hit at %p\n", (void*)stat_samples [i]);
487                                 usym = lookup_unmanaged_binary (stat_samples [i]);
488                         }
489                         if (usym) {
490                                 if (!usym->sample_hits) {
491                                         if (ucount == usize) {
492                                                 usize *= 2;
493                                                 if (!usize)
494                                                         usize = 4;
495                                                 cachedus = realloc (cachedus, sizeof (void*) * usize);
496                                         }
497                                         cachedus [ucount++] = usym;
498                                 }
499                                 usym->sample_hits++;
500                         }
501                         unmanaged_hits++;
502                 }
503         }
504         qsort (cachedm, count, sizeof (MethodDesc*), compare_method_samples);
505         qsort (cachedus, ucount, sizeof (UnmanagedSymbol*), compare_usymbol_samples);
506         set_usym_parent (cachedus, ucount);
507         fprintf (outfile, "\nStatistical samples summary\n");
508         fprintf (outfile, "\tSample type: %s\n", sample_type_name (stat_sample_desc [0]));
509         fprintf (outfile, "\tUnmanaged hits:  %6d (%4.1f%%)\n", unmanaged_hits, (100.0*unmanaged_hits)/num_stat_samples);
510         fprintf (outfile, "\tManaged hits:    %6d (%4.1f%%)\n", num_stat_samples - unmanaged_hits, (100.0*(num_stat_samples-unmanaged_hits))/num_stat_samples);
511         fprintf (outfile, "\tUnresolved hits: %6d (%4.1f%%)\n", unresolved_hits, (100.0*unresolved_hits)/num_stat_samples);
512         fprintf (outfile, "\t%6s %6s %s\n", "Hits", "%", "Method name");
513         i = 0;
514         u = 0;
515         while (i < count || u < ucount) {
516                 if (i < count) {
517                         MethodDesc *m = cachedm [i];
518                         if (u < ucount) {
519                                 UnmanagedSymbol *um = cachedus [u];
520                                 if (um->sample_hits > m->sample_hits) {
521                                         if (!sym_percent (um->sample_hits))
522                                                 break;
523                                         print_usym (um);
524                                         u++;
525                                         continue;
526                                 }
527                         }
528                         if (!sym_percent (m->sample_hits))
529                                 break;
530                         fprintf (outfile, "\t%6d %6.2f %s\n", m->sample_hits, m->sample_hits*100.0/num_stat_samples, m->name);
531                         i++;
532                         continue;
533                 }
534                 if (u < ucount) {
535                         UnmanagedSymbol *um = cachedus [u];
536                         if (!sym_percent (um->sample_hits))
537                                 break;
538                         print_usym (um);
539                         u++;
540                         continue;
541                 }
542         }
543 }
544
545 typedef struct _HeapClassDesc HeapClassDesc;
546 typedef struct {
547         HeapClassDesc *klass;
548         uint64_t count;
549 } HeapClassRevRef;
550
551 struct _HeapClassDesc {
552         ClassDesc *klass;
553         int64_t count;
554         int64_t total_size;
555         HeapClassRevRef *rev_hash;
556         int rev_hash_size;
557         int rev_count;
558         uintptr_t pinned_references;
559         uintptr_t root_references;
560 };
561
562 static int
563 add_rev_class_hashed (HeapClassRevRef *rev_hash, uintptr_t size, HeapClassDesc *hklass, uint64_t value)
564 {
565         uintptr_t i;
566         uintptr_t start_pos;
567         start_pos = (hklass->klass->klass >> 2) % size;
568         assert (start_pos < size);
569         i = start_pos;
570         do {
571                 if (rev_hash [i].klass == hklass) {
572                         rev_hash [i].count += value;
573                         return 0;
574                 } else if (!rev_hash [i].klass) {
575                         rev_hash [i].klass = hklass;
576                         rev_hash [i].count += value;
577                         start_pos = 0;
578                         for (i = 0; i < size; ++i)
579                                 if (rev_hash [i].klass && rev_hash [i].klass->klass == hklass->klass)
580                                         start_pos ++;
581                         assert (start_pos == 1);
582                         return 1;
583                 }
584                 /* wrap around */
585                 if (++i == size)
586                         i = 0;
587         } while (i != start_pos);
588         /* should not happen */
589         printf ("failed revref store\n");
590         return 0;
591 }
592
593 static void
594 add_heap_class_rev (HeapClassDesc *from, HeapClassDesc *to)
595 {
596         uintptr_t i;
597         if (to->rev_count * 2 >= to->rev_hash_size) {
598                 HeapClassRevRef *n;
599                 uintptr_t old_size = to->rev_hash_size;
600                 to->rev_hash_size *= 2;
601                 if (to->rev_hash_size == 0)
602                         to->rev_hash_size = 4;
603                 n = calloc (sizeof (HeapClassRevRef) * to->rev_hash_size, 1);
604                 for (i = 0; i < old_size; ++i) {
605                         if (to->rev_hash [i].klass)
606                                 add_rev_class_hashed (n, to->rev_hash_size, to->rev_hash [i].klass, to->rev_hash [i].count);
607                 }
608                 if (to->rev_hash)
609                         free (to->rev_hash);
610                 to->rev_hash = n;
611         }
612         to->rev_count += add_rev_class_hashed (to->rev_hash, to->rev_hash_size, from, 1);
613 }
614
615 typedef struct {
616         uintptr_t objaddr;
617         HeapClassDesc *hklass;
618         uintptr_t num_refs;
619         uintptr_t refs [0];
620 } HeapObjectDesc;
621
622 typedef struct _HeapShot HeapShot;
623 struct _HeapShot {
624         HeapShot *next;
625         uint64_t timestamp;
626         int class_count;
627         int hash_size;
628         HeapClassDesc **class_hash;
629         HeapClassDesc **sorted;
630         HeapObjectDesc **objects_hash;
631         uintptr_t objects_count;
632         uintptr_t objects_hash_size;
633         uintptr_t num_roots;
634         uintptr_t *roots;
635         uintptr_t *roots_extra;
636         int *roots_types;
637 };
638
639 static HeapShot *heap_shots = NULL;
640 static int num_heap_shots = 0;
641
642 static HeapShot*
643 new_heap_shot (uint64_t timestamp)
644 {
645         HeapShot *hs = calloc (sizeof (HeapShot), 1);
646         hs->hash_size = 4;
647         hs->class_hash = calloc (sizeof (void*), hs->hash_size);
648         hs->timestamp = timestamp;
649         num_heap_shots++;
650         hs->next = heap_shots;
651         heap_shots = hs;
652         return hs;
653 }
654
655 static HeapClassDesc*
656 heap_class_lookup (HeapShot *hs, ClassDesc *klass)
657 {
658         int i;
659         unsigned int start_pos;
660         start_pos = ((uintptr_t)klass->klass >> 2) % hs->hash_size;
661         i = start_pos;
662         do {
663                 HeapClassDesc* cd = hs->class_hash [i];
664                 if (!cd)
665                         return NULL;
666                 if (cd->klass == klass)
667                         return cd;
668                 /* wrap around */
669                 if (++i == hs->hash_size)
670                         i = 0;
671         } while (i != start_pos);
672         return NULL;
673 }
674
675 static int
676 add_heap_hashed (HeapClassDesc **hash, HeapClassDesc **retv, uintptr_t hsize, ClassDesc *klass, uint64_t size, uint64_t count)
677 {
678         uintptr_t i;
679         uintptr_t start_pos;
680         start_pos = ((uintptr_t)klass->klass >> 2) % hsize;
681         i = start_pos;
682         do {
683                 if (hash [i] && hash [i]->klass == klass) {
684                         hash [i]->total_size += size;
685                         hash [i]->count += count;
686                         *retv = hash [i];
687                         return 0;
688                 } else if (!hash [i]) {
689                         if (*retv) {
690                                 hash [i] = *retv;
691                                 return 1;
692                         }
693                         hash [i] = calloc (sizeof (HeapClassDesc), 1);
694                         hash [i]->klass = klass;
695                         hash [i]->total_size += size;
696                         hash [i]->count += count;
697                         *retv = hash [i];
698                         return 1;
699                 }
700                 /* wrap around */
701                 if (++i == hsize)
702                         i = 0;
703         } while (i != start_pos);
704         /* should not happen */
705         printf ("failed heap class store\n");
706         return 0;
707 }
708
709 static HeapClassDesc*
710 add_heap_shot_class (HeapShot *hs, ClassDesc *klass, uint64_t size)
711 {
712         HeapClassDesc *res;
713         int i;
714         if (hs->class_count * 2 >= hs->hash_size) {
715                 HeapClassDesc **n;
716                 int old_size = hs->hash_size;
717                 hs->hash_size *= 2;
718                 if (hs->hash_size == 0)
719                         hs->hash_size = 4;
720                 n = calloc (sizeof (void*) * hs->hash_size, 1);
721                 for (i = 0; i < old_size; ++i) {
722                         res = hs->class_hash [i];
723                         if (hs->class_hash [i])
724                                 add_heap_hashed (n, &res, hs->hash_size, hs->class_hash [i]->klass, hs->class_hash [i]->total_size, hs->class_hash [i]->count);
725                 }
726                 if (hs->class_hash)
727                         free (hs->class_hash);
728                 hs->class_hash = n;
729         }
730         res = NULL;
731         hs->class_count += add_heap_hashed (hs->class_hash, &res, hs->hash_size, klass, size, 1);
732         //if (res->count == 1)
733         //      printf ("added heap class: %s\n", res->klass->name);
734         return res;
735 }
736
737 static HeapObjectDesc*
738 alloc_heap_obj (uintptr_t objaddr, HeapClassDesc *hklass, uintptr_t num_refs)
739 {
740         HeapObjectDesc* ho = calloc (sizeof (HeapObjectDesc) + num_refs * sizeof (uintptr_t), 1);
741         ho->objaddr = objaddr;
742         ho->hklass = hklass;
743         ho->num_refs = num_refs;
744         return ho;
745 }
746
747 static uintptr_t
748 heap_shot_find_obj_slot (HeapShot *hs, uintptr_t objaddr)
749 {
750         uintptr_t i;
751         uintptr_t start_pos;
752         HeapObjectDesc **hash = hs->objects_hash;
753         start_pos = ((uintptr_t)objaddr >> 3) % hs->objects_hash_size;
754         i = start_pos;
755         do {
756                 if (hash [i] && hash [i]->objaddr == objaddr) {
757                         return i;
758                 } else if (!hash [i]) {
759                         break; /* fail */
760                 }
761                 /* wrap around */
762                 if (++i == hs->objects_hash_size)
763                         i = 0;
764         } while (i != start_pos);
765         /* should not happen */
766         //printf ("failed heap obj slot\n");
767         return -1;
768 }
769
770 static HeapObjectDesc*
771 heap_shot_obj_add_refs (HeapShot *hs, uintptr_t objaddr, uintptr_t num, uintptr_t *ref_offset)
772 {
773         HeapObjectDesc **hash = hs->objects_hash;
774         uintptr_t i = heap_shot_find_obj_slot (hs, objaddr);
775         if (i >= 0) {
776                 HeapObjectDesc* ho = alloc_heap_obj (objaddr, hash [i]->hklass, hash [i]->num_refs + num);
777                 *ref_offset = hash [i]->num_refs;
778                 memcpy (ho->refs, hash [i]->refs, hash [i]->num_refs * sizeof (uintptr_t));
779                 free (hash [i]);
780                 hash [i] = ho;
781                 return ho;
782         }
783         /* should not happen */
784         printf ("failed heap obj update\n");
785         return NULL;
786         
787 }
788
789 static uintptr_t
790 add_heap_hashed_obj (HeapObjectDesc **hash, uintptr_t hsize, HeapObjectDesc *obj)
791 {
792         uintptr_t i;
793         uintptr_t start_pos;
794         start_pos = ((uintptr_t)obj->objaddr >> 3) % hsize;
795         i = start_pos;
796         do {
797                 if (hash [i] && hash [i]->objaddr == obj->objaddr) {
798                         printf ("duplicate object!\n");
799                         return 0;
800                 } else if (!hash [i]) {
801                         hash [i] = obj;
802                         return 1;
803                 }
804                 /* wrap around */
805                 if (++i == hsize)
806                         i = 0;
807         } while (i != start_pos);
808         /* should not happen */
809         printf ("failed heap obj store\n");
810         return 0;
811 }
812
813 static void
814 add_heap_shot_obj (HeapShot *hs, HeapObjectDesc *obj)
815 {
816         uintptr_t i;
817         if (hs->objects_count * 2 >= hs->objects_hash_size) {
818                 HeapObjectDesc **n;
819                 uintptr_t old_size = hs->objects_hash_size;
820                 hs->objects_hash_size *= 2;
821                 if (hs->objects_hash_size == 0)
822                         hs->objects_hash_size = 4;
823                 n = calloc (sizeof (void*) * hs->objects_hash_size, 1);
824                 for (i = 0; i < old_size; ++i) {
825                         if (hs->objects_hash [i])
826                                 add_heap_hashed_obj (n, hs->objects_hash_size, hs->objects_hash [i]);
827                 }
828                 if (hs->objects_hash)
829                         free (hs->objects_hash);
830                 hs->objects_hash = n;
831         }
832         hs->objects_count += add_heap_hashed_obj (hs->objects_hash, hs->objects_hash_size, obj);
833 }
834
835 static void
836 heap_shot_resolve_reverse_refs (HeapShot *hs)
837 {
838         uintptr_t i;
839         for (i = 0; i < hs->objects_hash_size; ++i) {
840                 uintptr_t r;
841                 HeapObjectDesc *ho = hs->objects_hash [i];
842                 if (!ho)
843                         continue;
844                 for (r = 0; r < ho->num_refs; ++r) {
845                         uintptr_t oi = heap_shot_find_obj_slot (hs, ho->refs [r]);
846                         add_heap_class_rev (ho->hklass, hs->objects_hash [oi]->hklass);
847                 }
848         }
849 }
850
851 #define MARK_GRAY 1
852 #define MARK_BLACK 2
853
854 static void
855 heap_shot_mark_objects (HeapShot *hs)
856 {
857         uintptr_t i, oi, r;
858         unsigned char *marks;
859         HeapObjectDesc *obj, *ref;
860         int marked_some;
861         uintptr_t num_marked = 0, num_unmarked;
862         for (i = 0; i < hs->num_roots; ++i) {
863                 HeapClassDesc *cd;
864                 oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
865                 if (oi == -1) {
866                         continue;
867                 }
868                 obj = hs->objects_hash [oi];
869                 cd = obj->hklass;
870                 if (hs->roots_types [i] & MONO_PROFILE_GC_ROOT_PINNING)
871                         cd->pinned_references++;
872                 cd->root_references++;
873         }
874         if (!debug)
875                 return;
876         /* consistency checks: it seems not all the objects are walked in the heap in some cases */
877         marks = calloc (hs->objects_hash_size, 1);
878         if (!marks)
879                 return;
880         for (i = 0; i < hs->num_roots; ++i) {
881                 oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
882                 if (oi == -1) {
883                         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);
884                         continue;
885                 }
886                 obj = hs->objects_hash [oi];
887                 if (!marks [oi]) {
888                         marks [oi] = obj->num_refs? MARK_GRAY: MARK_BLACK;
889                         num_marked++;
890                 }
891         }
892         marked_some = 1;
893         while (marked_some) {
894                 marked_some = 0;
895                 for (i = 0; i < hs->objects_hash_size; ++i) {
896                         if (marks [i] != MARK_GRAY)
897                                 continue;
898                         marks [i] = MARK_BLACK;
899                         obj = hs->objects_hash [i];
900                         for (r = 0; r < obj->num_refs; ++r) {
901                                 oi = heap_shot_find_obj_slot (hs, obj->refs [r]);
902                                 if (oi == -1) {
903                                         fprintf (outfile, "referenced obj %p not found in heap\n", (void*)obj->refs [r]);
904                                         continue;
905                                 }
906                                 ref = hs->objects_hash [oi];
907                                 if (!marks [oi]) {
908                                         marks [oi] = ref->num_refs? MARK_GRAY: MARK_BLACK;
909                                 }
910                         }
911                         marked_some++;
912                 }
913         }
914
915         num_unmarked = 0;
916         for (i = 0; i < hs->objects_hash_size; ++i) {
917                 if (hs->objects_hash [i] && !marks [i]) {
918                         num_unmarked++;
919                         fprintf (outfile, "object %p (%s) unmarked\n", (void*)hs->objects_hash [i], hs->objects_hash [i]->hklass->klass->name);
920                 }
921         }
922         fprintf (outfile, "Total unmarked: %d/%d\n", num_unmarked, hs->objects_count);
923         free (marks);
924 }
925
926 static void
927 heap_shot_free_objects (HeapShot *hs)
928 {
929         uintptr_t i;
930         for (i = 0; i < hs->objects_hash_size; ++i) {
931                 HeapObjectDesc *ho = hs->objects_hash [i];
932                 if (ho)
933                         free (ho);
934         }
935         if (hs->objects_hash)
936                 free (hs->objects_hash);
937         hs->objects_hash = NULL;
938         hs->objects_hash_size = 0;
939         hs->objects_count = 0;
940 }
941
942
943 struct _BackTrace {
944         BackTrace *next;
945         unsigned int hash;
946         int count;
947         int id;
948         MethodDesc *methods [1];
949 };
950
951 static BackTrace *backtrace_hash [HASH_SIZE];
952 static BackTrace **backtraces = NULL;
953 static int num_backtraces = 0;
954 static int next_backtrace = 0;
955
956 static int
957 hash_backtrace (int count, MethodDesc **methods)
958 {
959         int hash = count;
960         int i;
961         for (i = 0; i < count; ++i) {
962                 hash = (hash << 5) - hash + methods [i]->method;
963         }
964         return hash;
965 }
966
967 static int
968 compare_backtrace (BackTrace *bt, int count, MethodDesc **methods)
969 {
970         int i;
971         if (bt->count != count)
972                 return 0;
973         for (i = 0; i < count; ++i)
974                 if (methods [i] != bt->methods [i])
975                         return 0;
976         return 1;
977 }
978
979 static BackTrace*
980 add_backtrace (int count, MethodDesc **methods)
981 {
982         int hash = hash_backtrace (count, methods);
983         int slot = (hash & 0xffff) % HASH_SIZE;
984         BackTrace *bt = backtrace_hash [slot];
985         while (bt) {
986                 if (bt->hash == hash && compare_backtrace (bt, count, methods))
987                         return bt;
988                 bt = bt->next;
989         }
990         bt = malloc (sizeof (BackTrace) + ((count - 1) * sizeof (void*)));
991         bt->next = backtrace_hash [slot];
992         backtrace_hash [slot] = bt;
993         if (next_backtrace == num_backtraces) {
994                 num_backtraces *= 2;
995                 if (!num_backtraces)
996                         num_backtraces = 16;
997                 backtraces = realloc (backtraces, sizeof (void*) * num_backtraces);
998         }
999         bt->id = next_backtrace++;
1000         backtraces [bt->id] = bt;
1001         bt->count = count;
1002         bt->hash = hash;
1003         for (slot = 0; slot < count; ++slot)
1004                 bt->methods [slot] = methods [slot];
1005
1006         return bt;
1007 }
1008
1009 typedef struct _MonitorDesc MonitorDesc;
1010 typedef struct _ThreadContext ThreadContext;
1011
1012 typedef struct {
1013         FILE *file;
1014 #if defined (HAVE_SYS_ZLIB)
1015         gzFile *gzfile;
1016 #endif
1017         unsigned char *buf;
1018         int size;
1019         int data_version;
1020         int version_major;
1021         int version_minor;
1022         int timer_overhead;
1023         int pid;
1024         int port;
1025         uint64_t startup_time;
1026         ThreadContext *threads;
1027         ThreadContext *current;
1028 } ProfContext;
1029
1030 struct _ThreadContext {
1031         ThreadContext *next;
1032         intptr_t thread_id;
1033         char *name;
1034         /* emulated stack */
1035         MethodDesc **stack;
1036         uint64_t *time_stack;
1037         uint64_t *callee_time_stack;
1038         uint64_t last_time;
1039         uint64_t contention_start;
1040         MonitorDesc *monitor;
1041         int stack_size;
1042         int stack_id;
1043         HeapShot *current_heap_shot;
1044         uintptr_t num_roots;
1045         uintptr_t size_roots;
1046         uintptr_t *roots;
1047         uintptr_t *roots_extra;
1048         int *roots_types;
1049         uint64_t gc_start_times [3];
1050 };
1051
1052 static void
1053 ensure_buffer (ProfContext *ctx, int size)
1054 {
1055         if (ctx->size < size) {
1056                 ctx->buf = realloc (ctx->buf, size);
1057                 ctx->size = size;
1058         }
1059 }
1060
1061 static int
1062 load_data (ProfContext *ctx, int size)
1063 {
1064         ensure_buffer (ctx, size);
1065 #if defined (HAVE_SYS_ZLIB)
1066         if (ctx->gzfile) {
1067                 int r = gzread (ctx->gzfile, ctx->buf, size);
1068                 if (r == 0)
1069                         return size == 0? 1: 0;
1070                 return r == size;
1071         } else 
1072 #endif
1073         {
1074                 int r = fread (ctx->buf, size, 1, ctx->file);
1075                 if (r == 0)
1076                         return size == 0? 1: 0;
1077                 return r;
1078         }
1079 }
1080
1081 static ThreadContext*
1082 get_thread (ProfContext *ctx, intptr_t thread_id)
1083 {
1084         ThreadContext *thread;
1085         if (ctx->current && ctx->current->thread_id == thread_id)
1086                 return ctx->current;
1087         thread = ctx->threads;
1088         while (thread) {
1089                 if (thread->thread_id == thread_id) {
1090                         return thread;
1091                 }
1092                 thread = thread->next;
1093         }
1094         thread = calloc (sizeof (ThreadContext), 1);
1095         thread->next = ctx->threads;
1096         ctx->threads = thread;
1097         thread->thread_id = thread_id;
1098         thread->last_time = 0;
1099         thread->stack_id = 0;
1100         thread->stack_size = 32;
1101         thread->stack = malloc (thread->stack_size * sizeof (void*));
1102         thread->time_stack = malloc (thread->stack_size * sizeof (uint64_t));
1103         thread->callee_time_stack = malloc (thread->stack_size * sizeof (uint64_t));
1104         return thread;
1105 }
1106
1107 static ThreadContext*
1108 load_thread (ProfContext *ctx, intptr_t thread_id)
1109 {
1110         ThreadContext *thread = get_thread (ctx, thread_id);
1111         ctx->current = thread;
1112         return thread;
1113 }
1114
1115 static void
1116 ensure_thread_stack (ThreadContext *thread)
1117 {
1118         if (thread->stack_id == thread->stack_size) {
1119                 thread->stack_size *= 2;
1120                 thread->stack = realloc (thread->stack, thread->stack_size * sizeof (void*));
1121                 thread->time_stack = realloc (thread->time_stack, thread->stack_size * sizeof (uint64_t));
1122                 thread->callee_time_stack = realloc (thread->callee_time_stack, thread->stack_size * sizeof (uint64_t));
1123         }
1124 }
1125
1126 static int
1127 add_trace_hashed (CallContext *traces, int size, BackTrace *bt, uint64_t value)
1128 {
1129         int i;
1130         unsigned int start_pos;
1131         start_pos = bt->hash % size;
1132         i = start_pos;
1133         do {
1134                 if (traces [i].bt == bt) {
1135                         traces [i].count += value;
1136                         return 0;
1137                 } else if (!traces [i].bt) {
1138                         traces [i].bt = bt;
1139                         traces [i].count += value;
1140                         return 1;
1141                 }
1142                 /* wrap around */
1143                 if (++i == size)
1144                         i = 0;
1145         } while (i != start_pos);
1146         /* should not happen */
1147         printf ("failed trace store\n");
1148         return 0;
1149 }
1150
1151 static void
1152 add_trace_bt (BackTrace *bt, TraceDesc *trace, uint64_t value)
1153 {
1154         int i;
1155         if (!collect_traces)
1156                 return;
1157         if (trace->count * 2 >= trace->size) {
1158                 CallContext *n;
1159                 int old_size = trace->size;
1160                 trace->size *= 2;
1161                 if (trace->size == 0)
1162                         trace->size = 4;
1163                 n = calloc (sizeof (CallContext) * trace->size, 1);
1164                 for (i = 0; i < old_size; ++i) {
1165                         if (trace->traces [i].bt)
1166                                 add_trace_hashed (n, trace->size, trace->traces [i].bt, trace->traces [i].count);
1167                 }
1168                 if (trace->traces)
1169                         free (trace->traces);
1170                 trace->traces = n;
1171         }
1172         trace->count += add_trace_hashed (trace->traces, trace->size, bt, value);
1173 }
1174
1175 static BackTrace*
1176 add_trace_thread (ThreadContext *thread, TraceDesc *trace, uint64_t value)
1177 {
1178         BackTrace *bt;
1179         int count = thread->stack_id;
1180         if (!collect_traces)
1181                 return NULL;
1182         if (count > trace_max)
1183                 count = trace_max;
1184         bt = add_backtrace (count, thread->stack + thread->stack_id - count);
1185         add_trace_bt (bt, trace, value);
1186         return bt;
1187 }
1188
1189 static BackTrace*
1190 add_trace_methods (MethodDesc **methods, int count, TraceDesc *trace, uint64_t value)
1191 {
1192         BackTrace *bt;
1193         if (!collect_traces)
1194                 return NULL;
1195         if (count > trace_max)
1196                 count = trace_max;
1197         bt = add_backtrace (count, methods);
1198         add_trace_bt (bt, trace, value);
1199         return bt;
1200 }
1201
1202 static void
1203 thread_add_root (ThreadContext *ctx, uintptr_t obj, int root_type, uintptr_t extra_info)
1204 {
1205         if (ctx->num_roots == ctx->size_roots) {
1206                 int new_size = ctx->size_roots * 2;
1207                 if (!new_size)
1208                         new_size = 4;
1209                 ctx->roots = realloc (ctx->roots, new_size * sizeof (uintptr_t));
1210                 ctx->roots_extra = realloc (ctx->roots_extra, new_size * sizeof (uintptr_t));
1211                 ctx->roots_types = realloc (ctx->roots_types, new_size * sizeof (int));
1212                 ctx->size_roots = new_size;
1213         }
1214         ctx->roots_types [ctx->num_roots] = root_type;
1215         ctx->roots_extra [ctx->num_roots] = extra_info;
1216         ctx->roots [ctx->num_roots++] = obj;
1217 }
1218
1219 static int
1220 compare_callc (const void *a, const void *b)
1221 {
1222         const CallContext *A = a;
1223         const CallContext *B = b;
1224         if (B->count == A->count)
1225                 return 0;
1226         if (B->count < A->count)
1227                 return -1;
1228         return 1;
1229 }
1230
1231 static void
1232 sort_context_array (TraceDesc* traces)
1233 {
1234         int i, j;
1235         for (i = 0, j = 0; i < traces->size; ++i) {
1236                 if (traces->traces [i].bt) {
1237                         traces->traces [j].bt = traces->traces [i].bt;
1238                         traces->traces [j].count = traces->traces [i].count;
1239                         j++;
1240                 }
1241         }
1242         qsort (traces->traces, traces->count, sizeof (CallContext), compare_callc);
1243 }
1244
1245 static void
1246 push_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
1247 {
1248         ensure_thread_stack (thread);
1249         thread->time_stack [thread->stack_id] = timestamp;
1250         thread->callee_time_stack [thread->stack_id] = 0;
1251         thread->stack [thread->stack_id++] = method;
1252         method->recurse_count++;
1253 }
1254
1255 static void
1256 pop_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
1257 {
1258         method->recurse_count--;
1259         if (thread->stack_id > 0 && thread->stack [thread->stack_id - 1] == method) {
1260                 uint64_t tdiff;
1261                 thread->stack_id--;
1262                 method->calls++;
1263                 if (timestamp < thread->time_stack [thread->stack_id])
1264                         fprintf (outfile, "time went backwards for %s\n", method->name);
1265                 tdiff = timestamp - thread->time_stack [thread->stack_id];
1266                 if (thread->callee_time_stack [thread->stack_id] > tdiff)
1267                         fprintf (outfile, "callee time bigger for %s\n", method->name);
1268                 method->self_time += tdiff - thread->callee_time_stack [thread->stack_id];
1269                 method->callee_time += thread->callee_time_stack [thread->stack_id];
1270                 if (thread->stack_id)
1271                         thread->callee_time_stack [thread->stack_id - 1] += tdiff;
1272                 //fprintf (outfile, "method %s took %d\n", method->name, (int)(tdiff/1000));
1273         } else {
1274                 fprintf (outfile, "unmatched leave at stack pos: %d for method %s\n", thread->stack_id, method->name);
1275         }
1276 }
1277
1278 typedef struct {
1279         uint64_t total_time;
1280         uint64_t max_time;
1281         int count;
1282 } GCDesc;
1283 static GCDesc gc_info [3];
1284 static uint64_t max_heap_size;
1285 static uint64_t gc_object_moves;
1286 static int gc_resizes;
1287 typedef struct {
1288         uint64_t created;
1289         uint64_t destroyed;
1290         uint64_t max_live;
1291         TraceDesc traces;
1292 } HandleInfo;
1293 static HandleInfo handle_info [4];
1294
1295 static const char*
1296 gc_event_name (int ev)
1297 {
1298         switch (ev) {
1299         case MONO_GC_EVENT_START: return "start";
1300         case MONO_GC_EVENT_MARK_START: return "mark start";
1301         case MONO_GC_EVENT_MARK_END: return "mark end";
1302         case MONO_GC_EVENT_RECLAIM_START: return "reclaim start";
1303         case MONO_GC_EVENT_RECLAIM_END: return "reclaim end";
1304         case MONO_GC_EVENT_END: return "end";
1305         case MONO_GC_EVENT_PRE_STOP_WORLD: return "pre stop";
1306         case MONO_GC_EVENT_POST_STOP_WORLD: return "post stop";
1307         case MONO_GC_EVENT_PRE_START_WORLD: return "pre start";
1308         case MONO_GC_EVENT_POST_START_WORLD: return "post start";
1309         default:
1310                 return "unknown";
1311         }
1312 }
1313
1314 static uint64_t clause_summary [MONO_EXCEPTION_CLAUSE_FAULT + 1];
1315 static uint64_t throw_count = 0;
1316 static TraceDesc exc_traces;
1317
1318 static const char*
1319 clause_name (int type)
1320 {
1321         switch (type) {
1322         case MONO_EXCEPTION_CLAUSE_NONE: return "catch";
1323         case MONO_EXCEPTION_CLAUSE_FILTER: return "filter";
1324         case MONO_EXCEPTION_CLAUSE_FINALLY: return "finally";
1325         case MONO_EXCEPTION_CLAUSE_FAULT: return "fault";
1326         default: return "invalid";
1327         }
1328 }
1329
1330 static uint64_t monitor_contention;
1331 static uint64_t monitor_failed;
1332 static uint64_t monitor_acquired;
1333
1334 struct _MonitorDesc {
1335         MonitorDesc *next;
1336         uintptr_t objid;
1337         uintptr_t contentions;
1338         uint64_t wait_time;
1339         uint64_t max_wait_time;
1340         TraceDesc traces;
1341 };
1342
1343 static MonitorDesc* monitor_hash [SMALL_HASH_SIZE] = {0};
1344 static int num_monitors = 0;
1345
1346 static MonitorDesc*
1347 lookup_monitor (uintptr_t objid)
1348 {
1349         int slot = ((objid >> 3) & 0xffff) % SMALL_HASH_SIZE;
1350         MonitorDesc *cd = monitor_hash [slot];
1351         while (cd && cd->objid != objid)
1352                 cd = cd->next;
1353         if (!cd) {
1354                 cd = calloc (sizeof (MonitorDesc), 1);
1355                 cd->objid = objid;
1356                 cd->next = monitor_hash [slot];
1357                 monitor_hash [slot] = cd;
1358                 num_monitors++;
1359         }
1360         return cd;
1361 }
1362
1363 static const char*
1364 monitor_ev_name (int ev)
1365 {
1366         switch (ev) {
1367         case MONO_PROFILER_MONITOR_CONTENTION: return "contended";
1368         case MONO_PROFILER_MONITOR_DONE: return "acquired";
1369         case MONO_PROFILER_MONITOR_FAIL: return "not taken";
1370         default: return "invalid";
1371         }
1372 }
1373
1374 static const char*
1375 get_handle_name (int htype)
1376 {
1377         switch (htype) {
1378         case 0: return "weak";
1379         case 1: return "weaktrack";
1380         case 2: return "normal";
1381         case 3: return "pinned";
1382         default: return "unknown";
1383         }
1384 }
1385
1386 static const char*
1387 get_root_name (int rtype)
1388 {
1389         switch (rtype & MONO_PROFILE_GC_ROOT_TYPEMASK) {
1390         case MONO_PROFILE_GC_ROOT_STACK: return "stack";
1391         case MONO_PROFILE_GC_ROOT_FINALIZER: return "finalizer";
1392         case MONO_PROFILE_GC_ROOT_HANDLE: return "handle";
1393         case MONO_PROFILE_GC_ROOT_OTHER: return "other";
1394         case MONO_PROFILE_GC_ROOT_MISC: return "misc";
1395         default: return "unknown";
1396         }
1397 }
1398
1399 static MethodDesc**
1400 decode_bt (MethodDesc** sframes, int *size, unsigned char *p, unsigned char **endp, intptr_t ptr_base)
1401 {
1402         MethodDesc **frames;
1403         int i;
1404         int flags = decode_uleb128 (p, &p);
1405         int count = decode_uleb128 (p, &p);
1406         if (flags != 0)
1407                 return NULL;
1408         if (count > *size)
1409                 frames = malloc (count * sizeof (void*));
1410         else
1411                 frames = sframes;
1412         for (i = 0; i < count; ++i) {
1413                 intptr_t ptrdiff = decode_sleb128 (p, &p);
1414                 frames [i] = lookup_method (ptr_base + ptrdiff);
1415         }
1416         *size = count;
1417         *endp = p;
1418         return frames;
1419 }
1420
1421 static void
1422 tracked_creation (uintptr_t obj, ClassDesc *cd, uint64_t size, BackTrace *bt, uint64_t timestamp)
1423 {
1424         int i;
1425         for (i = 0; i < num_tracked_objects; ++i) {
1426                 if (tracked_objects [i] != obj)
1427                         continue;
1428                 fprintf (outfile, "Object %p created (%s, %llu bytes) at %.3f secs.\n", (void*)obj, cd->name, size, (timestamp - startup_time)/1000000000.0);
1429                 if (bt && bt->count) {
1430                         int k;
1431                         for (k = 0; k < bt->count; ++k)
1432                                 fprintf (outfile, "\t%s\n", bt->methods [k]->name);
1433                 }
1434         }
1435 }
1436
1437 static void
1438 track_handle (uintptr_t obj, int htype, uint32_t handle)
1439 {
1440         int i;
1441         for (i = 0; i < num_tracked_objects; ++i) {
1442                 if (tracked_objects [i] == obj)
1443                         fprintf (outfile, "Object %p referenced from handle %u\n", (void*)obj, handle);
1444         }
1445 }
1446
1447 static void
1448 track_move (uintptr_t src, uintptr_t dst)
1449 {
1450         int i;
1451         for (i = 0; i < num_tracked_objects; ++i) {
1452                 if (tracked_objects [i] == src)
1453                         fprintf (outfile, "Object %p moved to %p\n", (void*)src, (void*)dst);
1454                 else if (tracked_objects [i] == dst)
1455                         fprintf (outfile, "Object %p moved from %p\n", (void*)dst, (void*)src);
1456         }
1457 }
1458
1459 static void
1460 track_obj_reference (uintptr_t obj, uintptr_t parent, ClassDesc *cd)
1461 {
1462         int i;
1463         for (i = 0; i < num_tracked_objects; ++i) {
1464                 if (tracked_objects [i] == obj) 
1465                         fprintf (outfile, "Object %p referenced from %p (%s).\n", (void*)obj, (void*)parent, cd->name);
1466         }
1467 }
1468
1469 static void
1470 found_object (uintptr_t obj)
1471 {
1472         num_tracked_objects ++;
1473         tracked_objects = realloc (tracked_objects, num_tracked_objects * sizeof (tracked_objects [0]));
1474         tracked_objects [num_tracked_objects - 1] = obj;
1475 }
1476
1477 #define OBJ_ADDR(diff) ((obj_base + diff) << 3)
1478 #define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
1479
1480 static int
1481 decode_buffer (ProfContext *ctx)
1482 {
1483         unsigned char *p;
1484         unsigned char *end;
1485         intptr_t thread_id;
1486         intptr_t ptr_base;
1487         intptr_t obj_base;
1488         intptr_t method_base;
1489         uint64_t time_base;
1490         uint64_t file_offset;
1491         int len, i;
1492         ThreadContext *thread;
1493
1494 #ifdef HAVE_SYS_ZLIB
1495         if (ctx->gzfile)
1496                 file_offset = gztell (ctx->gzfile);
1497         else
1498 #endif
1499                 file_offset = ftell (ctx->file);
1500         if (!load_data (ctx, 48))
1501                 return 0;
1502         p = ctx->buf;
1503         if (read_int32 (p) != BUF_ID) {
1504                 fprintf (outfile, "Incorrect buffer id: 0x%x\n", read_int32 (p));
1505                 for (i = 0; i < 48; ++i) {
1506                         fprintf (outfile, "0x%x%s", p [i], i % 8?" ":"\n");
1507                 }
1508                 return 0;
1509         }
1510         len = read_int32 (p + 4);
1511         time_base = read_int64 (p + 8);
1512         ptr_base = read_int64 (p + 16);
1513         obj_base = read_int64 (p + 24);
1514         thread_id = read_int64 (p + 32);
1515         method_base = read_int64 (p + 40);
1516         if (debug)
1517                 fprintf (outfile, "buf: thread:%x, len: %d, time: %llu, file offset: %llu\n", thread_id, len, time_base, file_offset);
1518         thread = load_thread (ctx, thread_id);
1519         if (!load_data (ctx, len))
1520                 return 0;
1521         if (!startup_time) {
1522                 startup_time = time_base;
1523                 if (time_from) {
1524                         time_from += startup_time;
1525                         time_to += startup_time;
1526                 }
1527                 if (!thread->name)
1528                         thread->name = pstrdup ("Main");
1529         }
1530         for (i = 0; i < thread->stack_id; ++i)
1531                 thread->stack [i]->recurse_count++;
1532         p = ctx->buf;
1533         end = p + len;
1534         while (p < end) {
1535                 switch (*p & 0xf) {
1536                 case TYPE_GC: {
1537                         int subtype = *p & 0xf0;
1538                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
1539                         LOG_TIME (time_base, tdiff);
1540                         time_base += tdiff;
1541                         if (subtype == TYPE_GC_RESIZE) {
1542                                 uint64_t new_size = decode_uleb128 (p, &p);
1543                                 if (debug)
1544                                         fprintf (outfile, "gc heap resized to %llu\n", new_size);
1545                                 gc_resizes++;
1546                                 if (new_size > max_heap_size)
1547                                         max_heap_size = new_size;
1548                         } else if (subtype == TYPE_GC_EVENT) {
1549                                 uint64_t ev = decode_uleb128 (p, &p);
1550                                 int gen = decode_uleb128 (p, &p);
1551                                 if (debug)
1552                                         fprintf (outfile, "gc event for gen%d: %s at %llu (thread: 0x%x)\n", gen, gc_event_name (ev), time_base, thread->thread_id);
1553                                 if (gen > 2) {
1554                                         fprintf (outfile, "incorrect gc gen: %d\n", gen);
1555                                         break;
1556                                 }
1557                                 if (ev == MONO_GC_EVENT_START) {
1558                                         thread->gc_start_times [gen] = time_base;
1559                                         gc_info [gen].count++;
1560                                 } else if (ev == MONO_GC_EVENT_END) {
1561                                         tdiff = time_base - thread->gc_start_times [gen];
1562                                         gc_info [gen].total_time += tdiff;
1563                                         if (tdiff > gc_info [gen].max_time)
1564                                                 gc_info [gen].max_time = tdiff;
1565                                 }
1566                         } else if (subtype == TYPE_GC_MOVE) {
1567                                 int j, num = decode_uleb128 (p, &p);
1568                                 gc_object_moves += num / 2;
1569                                 for (j = 0; j < num; j += 2) {
1570                                         intptr_t obj1diff = decode_sleb128 (p, &p);
1571                                         intptr_t obj2diff = decode_sleb128 (p, &p);
1572                                         if (num_tracked_objects)
1573                                                 track_move (OBJ_ADDR (obj1diff), OBJ_ADDR (obj2diff));
1574                                         if (debug) {
1575                                                 fprintf (outfile, "moved obj %p to %p\n", (void*)OBJ_ADDR (obj1diff), (void*)OBJ_ADDR (obj2diff));
1576                                         }
1577                                 }
1578                         } else if (subtype == TYPE_GC_HANDLE_CREATED) {
1579                                 int htype = decode_uleb128 (p, &p);
1580                                 uint32_t handle = decode_uleb128 (p, &p);
1581                                 intptr_t objdiff = decode_sleb128 (p, &p);
1582                                 if (htype > 3)
1583                                         return 0;
1584                                 handle_info [htype].created++;
1585                                 add_trace_thread (thread, &handle_info [htype].traces, 1);
1586                                 /* FIXME: we don't take into account timing here */
1587                                 if (handle_info [htype].created > handle_info [htype].max_live)
1588                                         handle_info [htype].max_live = handle_info [htype].created;
1589                                 if (num_tracked_objects)
1590                                         track_handle (OBJ_ADDR (objdiff), htype, handle);
1591                                 if (debug)
1592                                         fprintf (outfile, "handle (%s) %u created for object %p\n", get_handle_name (htype), handle, (void*)OBJ_ADDR (objdiff));
1593                         } else if (subtype == TYPE_GC_HANDLE_DESTROYED) {
1594                                 int htype = decode_uleb128 (p, &p);
1595                                 uint32_t handle = decode_uleb128 (p, &p);
1596                                 if (htype > 3)
1597                                         return 0;
1598                                 handle_info [htype].created--;
1599                                 if (debug)
1600                                         fprintf (outfile, "handle (%s) %u destroyed\n", get_handle_name (htype), handle);
1601                         }
1602                         break;
1603                 }
1604                 case TYPE_METADATA: {
1605                         int error = *p & TYPE_LOAD_ERR;
1606                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
1607                         int mtype = *p++;
1608                         intptr_t ptrdiff = decode_sleb128 (p, &p);
1609                         LOG_TIME (time_base, tdiff);
1610                         time_base += tdiff;
1611                         if (mtype == TYPE_CLASS) {
1612                                 intptr_t imptrdiff = decode_sleb128 (p, &p);
1613                                 uint64_t flags = decode_uleb128 (p, &p);
1614                                 if (flags) {
1615                                         fprintf (outfile, "non-zero flags in class\n");
1616                                         return 0;
1617                                 }
1618                                 if (debug)
1619                                         fprintf (outfile, "loaded class %p (%s in %p) at %llu\n", (void*)(ptr_base + ptrdiff), p, (void*)(ptr_base + imptrdiff), time_base);
1620                                 if (!error)
1621                                         add_class (ptr_base + ptrdiff, (char*)p);
1622                                 while (*p) p++;
1623                                 p++;
1624                         } else if (mtype == TYPE_IMAGE) {
1625                                 uint64_t flags = decode_uleb128 (p, &p);
1626                                 if (flags) {
1627                                         fprintf (outfile, "non-zero flags in image\n");
1628                                         return 0;
1629                                 }
1630                                 if (debug)
1631                                         fprintf (outfile, "loaded image %p (%s) at %llu\n", (void*)(ptr_base + ptrdiff), p, time_base);
1632                                 if (!error)
1633                                         add_image (ptr_base + ptrdiff, (char*)p);
1634                                 while (*p) p++;
1635                                 p++;
1636                         } else if (mtype == TYPE_THREAD) {
1637                                 ThreadContext *nt;
1638                                 uint64_t flags = decode_uleb128 (p, &p);
1639                                 if (flags) {
1640                                         fprintf (outfile, "non-zero flags in thread\n");
1641                                         return 0;
1642                                 }
1643                                 nt = get_thread (ctx, ptr_base * ptrdiff);
1644                                 nt->name = pstrdup ((char*)p);
1645                                 if (debug)
1646                                         fprintf (outfile, "thread %p named: %s\n", (void*)(ptr_base + ptrdiff), p);
1647                                 while (*p) p++;
1648                                 p++;
1649                         }
1650                         break;
1651                 }
1652                 case TYPE_ALLOC: {
1653                         int has_bt = *p & TYPE_ALLOC_BT;
1654                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
1655                         intptr_t ptrdiff = decode_sleb128 (p, &p);
1656                         intptr_t objdiff = decode_sleb128 (p, &p);
1657                         uint64_t len;
1658                         int num_bt = 0;
1659                         MethodDesc* sframes [8];
1660                         MethodDesc** frames = sframes;
1661                         ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
1662                         len = decode_uleb128 (p, &p);
1663                         LOG_TIME (time_base, tdiff);
1664                         time_base += tdiff;
1665                         if (debug)
1666                                 fprintf (outfile, "alloced object %p, size %llu (%s) at %llu\n", (void*)OBJ_ADDR (objdiff), len, lookup_class (ptr_base + ptrdiff)->name, time_base);
1667                         if (has_bt) {
1668                                 num_bt = 8;
1669                                 frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
1670                                 if (!frames) {
1671                                         fprintf (outfile, "Cannot load backtrace\n");
1672                                         return 0;
1673                                 }
1674                         }
1675                         if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
1676                                 BackTrace *bt;
1677                                 cd->allocs++;
1678                                 cd->alloc_size += len;
1679                                 if (has_bt)
1680                                         bt = add_trace_methods (frames, num_bt, &cd->traces, len);
1681                                 else
1682                                         bt = add_trace_thread (thread, &cd->traces, len);
1683                                 if (find_size && len >= find_size) {
1684                                         if (!find_name || strstr (cd->name, find_name))
1685                                                 found_object (OBJ_ADDR (objdiff));
1686                                 } else if (!find_size && find_name && strstr (cd->name, find_name)) {
1687                                         found_object (OBJ_ADDR (objdiff));
1688                                 }
1689                                 if (num_tracked_objects)
1690                                         tracked_creation (OBJ_ADDR (objdiff), cd, len, bt, time_base);
1691                         }
1692                         if (frames != sframes)
1693                                 free (frames);
1694                         break;
1695                 }
1696                 case TYPE_METHOD: {
1697                         int subtype = *p & 0xf0;
1698                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
1699                         int64_t ptrdiff = decode_sleb128 (p, &p);
1700                         LOG_TIME (time_base, tdiff);
1701                         time_base += tdiff;
1702                         method_base += ptrdiff;
1703                         if (subtype == TYPE_JIT) {
1704                                 intptr_t codediff = decode_sleb128 (p, &p);
1705                                 int codelen = decode_uleb128 (p, &p);
1706                                 if (debug)
1707                                         fprintf (outfile, "jitted method %p (%s), size: %d, code: %p\n", (void*)(method_base), p, codelen, (void*)(ptr_base + codediff));
1708                                 add_method (method_base, (char*)p, ptr_base + codediff, codelen);
1709                                 while (*p) p++;
1710                                 p++;
1711                         } else {
1712                                 MethodDesc *method;
1713                                 if ((thread_filter && thread_filter != thread->thread_id))
1714                                         break;
1715                                 method = lookup_method (method_base);
1716                                 if (subtype == TYPE_ENTER) {
1717                                         add_trace_thread (thread, &method->traces, 1);
1718                                         push_method (thread, method, time_base);
1719                                 } else {
1720                                         pop_method (thread, method, time_base);
1721                                 }
1722                                 if (debug)
1723                                         fprintf (outfile, "%s method %s\n", subtype == TYPE_ENTER? "enter": subtype == TYPE_EXC_LEAVE? "exleave": "leave", method->name);
1724                         }
1725                         break;
1726                 }
1727                 case TYPE_HEAP: {
1728                         int subtype = *p & 0xf0;
1729                         if (subtype == TYPE_HEAP_OBJECT) {
1730                                 HeapObjectDesc *ho;
1731                                 int i;
1732                                 intptr_t objdiff = decode_sleb128 (p + 1, &p);
1733                                 intptr_t ptrdiff = decode_sleb128 (p, &p);
1734                                 uint64_t size = decode_uleb128 (p, &p);
1735                                 uintptr_t num = decode_uleb128 (p, &p);
1736                                 uintptr_t ref_offset;
1737                                 uintptr_t last_obj_offset = 0;
1738                                 ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
1739                                 if (size) {
1740                                         HeapClassDesc *hcd = add_heap_shot_class (thread->current_heap_shot, cd, size);
1741                                         if (collect_traces) {
1742                                                 ho = alloc_heap_obj (OBJ_ADDR (objdiff), hcd, num);
1743                                                 add_heap_shot_obj (thread->current_heap_shot, ho);
1744                                                 ref_offset = 0;
1745                                         }
1746                                 } else {
1747                                         if (collect_traces)
1748                                                 ho = heap_shot_obj_add_refs (thread->current_heap_shot, OBJ_ADDR (objdiff), num, &ref_offset);
1749                                 }
1750                                 for (i = 0; i < num; ++i) {
1751                                         /* FIXME: use object distance to measure how good
1752                                          * the GC is at keeping related objects close
1753                                          */
1754                                         uintptr_t offset = ctx->data_version > 1? last_obj_offset + decode_uleb128 (p, &p): -1;
1755                                         intptr_t obj1diff = decode_sleb128 (p, &p);
1756                                         last_obj_offset = offset;
1757                                         if (collect_traces)
1758                                                 ho->refs [ref_offset + i] = OBJ_ADDR (obj1diff);
1759                                         if (num_tracked_objects)
1760                                                 track_obj_reference (OBJ_ADDR (obj1diff), OBJ_ADDR (objdiff), cd);
1761                                 }
1762                                 if (debug && size)
1763                                         fprintf (outfile, "traced object %p, size %llu (%s), refs: %d\n", (void*)OBJ_ADDR (objdiff), size, cd->name, num);
1764                         } else if (subtype == TYPE_HEAP_ROOT) {
1765                                 uintptr_t num = decode_uleb128 (p + 1, &p);
1766                                 uintptr_t gc_num = decode_uleb128 (p, &p);
1767                                 int i;
1768                                 for (i = 0; i < num; ++i) {
1769                                         intptr_t objdiff = decode_sleb128 (p, &p);
1770                                         int root_type = decode_uleb128 (p, &p);
1771                                         /* we just discard the extra info for now */
1772                                         uintptr_t extra_info = decode_uleb128 (p, &p);
1773                                         if (debug)
1774                                                 fprintf (outfile, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff), get_root_name (root_type));
1775                                         if (collect_traces)
1776                                                 thread_add_root (thread, OBJ_ADDR (objdiff), root_type, extra_info);
1777                                 }
1778                         } else if (subtype == TYPE_HEAP_END) {
1779                                 uint64_t tdiff = decode_uleb128 (p + 1, &p);
1780                                 LOG_TIME (time_base, tdiff);
1781                                 time_base += tdiff;
1782                                 if (debug)
1783                                         fprintf (outfile, "heap shot end\n");
1784                                 if (collect_traces) {
1785                                         HeapShot *hs = thread->current_heap_shot;
1786                                         if (hs && thread->num_roots) {
1787                                                 /* transfer the root ownershipt to the heapshot */
1788                                                 hs->num_roots = thread->num_roots;
1789                                                 hs->roots = thread->roots;
1790                                                 hs->roots_extra = thread->roots_extra;
1791                                                 hs->roots_types = thread->roots_types;
1792                                         } else {
1793                                                 free (thread->roots);
1794                                                 free (thread->roots_extra);
1795                                                 free (thread->roots_types);
1796                                         }
1797                                         thread->num_roots = 0;
1798                                         thread->size_roots = 0;
1799                                         thread->roots = NULL;
1800                                         thread->roots_extra = NULL;
1801                                         thread->roots_types = NULL;
1802                                         heap_shot_resolve_reverse_refs (hs);
1803                                         heap_shot_mark_objects (hs);
1804                                         heap_shot_free_objects (hs);
1805                                 }
1806                                 thread->current_heap_shot = NULL;
1807                         } else if (subtype == TYPE_HEAP_START) {
1808                                 uint64_t tdiff = decode_uleb128 (p + 1, &p);
1809                                 LOG_TIME (time_base, tdiff);
1810                                 time_base += tdiff;
1811                                 if (debug)
1812                                         fprintf (outfile, "heap shot start\n");
1813                                 thread->current_heap_shot = new_heap_shot (time_base);
1814                         }
1815                         break;
1816                 }
1817                 case TYPE_MONITOR: {
1818                         int event = (*p >> 4) & 0x3;
1819                         int has_bt = *p & TYPE_MONITOR_BT;
1820                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
1821                         intptr_t objdiff = decode_sleb128 (p, &p);
1822                         MethodDesc* sframes [8];
1823                         MethodDesc** frames = sframes;
1824                         int record;
1825                         int num_bt = 0;
1826                         LOG_TIME (time_base, tdiff);
1827                         time_base += tdiff;
1828                         record = (!thread_filter || thread_filter == thread->thread_id);
1829                         if (event == MONO_PROFILER_MONITOR_CONTENTION) {
1830                                 MonitorDesc *mdesc = lookup_monitor (OBJ_ADDR (objdiff));
1831                                 if (record) {
1832                                         monitor_contention++;
1833                                         mdesc->contentions++;
1834                                         thread->monitor = mdesc;
1835                                         thread->contention_start = time_base;
1836                                 }
1837                                 if (has_bt) {
1838                                         num_bt = 8;
1839                                         frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
1840                                         if (!frames) {
1841                                                 fprintf (outfile, "Cannot load backtrace\n");
1842                                                 return 0;
1843                                         }
1844                                         if (record)
1845                                                 add_trace_methods (frames, num_bt, &mdesc->traces, 1);
1846                                 } else {
1847                                         if (record)
1848                                                 add_trace_thread (thread, &mdesc->traces, 1);
1849                                 }
1850                         } else if (event == MONO_PROFILER_MONITOR_FAIL) {
1851                                 if (record) {
1852                                         monitor_failed++;
1853                                         if (thread->monitor && thread->contention_start) {
1854                                                 uint64_t wait_time = time_base - thread->contention_start;
1855                                                 if (wait_time > thread->monitor->max_wait_time)
1856                                                         thread->monitor->max_wait_time = wait_time;
1857                                                 thread->monitor->wait_time += wait_time;
1858                                                 thread->monitor = NULL;
1859                                                 thread->contention_start = 0;
1860                                         }
1861                                 }
1862                         } else if (event == MONO_PROFILER_MONITOR_DONE) {
1863                                 if (record) {
1864                                         monitor_acquired++;
1865                                         if (thread->monitor && thread->contention_start) {
1866                                                 uint64_t wait_time = time_base - thread->contention_start;
1867                                                 if (wait_time > thread->monitor->max_wait_time)
1868                                                         thread->monitor->max_wait_time = wait_time;
1869                                                 thread->monitor->wait_time += wait_time;
1870                                                 thread->monitor = NULL;
1871                                                 thread->contention_start = 0;
1872                                         }
1873                                 }
1874                         }
1875                         if (debug)
1876                                 fprintf (outfile, "monitor %s for object %p\n", monitor_ev_name (event), (void*)OBJ_ADDR (objdiff));
1877                         if (frames != sframes)
1878                                 free (frames);
1879                         break;
1880                 }
1881                 case TYPE_EXCEPTION: {
1882                         int subtype = *p & 0x70;
1883                         int has_bt = *p & TYPE_EXCEPTION_BT;
1884                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
1885                         MethodDesc* sframes [8];
1886                         MethodDesc** frames = sframes;
1887                         int record;
1888                         LOG_TIME (time_base, tdiff);
1889                         time_base += tdiff;
1890                         record = (!thread_filter || thread_filter == thread->thread_id);
1891                         if (subtype == TYPE_CLAUSE) {
1892                                 int clause_type = decode_uleb128 (p, &p);
1893                                 int clause_num = decode_uleb128 (p, &p);
1894                                 int64_t ptrdiff = decode_sleb128 (p, &p);
1895                                 method_base += ptrdiff;
1896                                 if (record)
1897                                         clause_summary [clause_type]++;
1898                                 if (debug)
1899                                         fprintf (outfile, "clause %s (%d) in method %s\n", clause_name (clause_type), clause_num, lookup_method (method_base)->name);
1900                         } else {
1901                                 intptr_t objdiff = decode_sleb128 (p, &p);
1902                                 if (record)
1903                                         throw_count++;
1904                                 if (has_bt) {
1905                                         has_bt = 8;
1906                                         frames = decode_bt (sframes, &has_bt, p, &p, ptr_base);
1907                                         if (!frames) {
1908                                                 fprintf (outfile, "Cannot load backtrace\n");
1909                                                 return 0;
1910                                         }
1911                                         if (record)
1912                                                 add_trace_methods (frames, has_bt, &exc_traces, 1);
1913                                 } else {
1914                                         if (record)
1915                                                 add_trace_thread (thread, &exc_traces, 1);
1916                                 }
1917                                 if (frames != sframes)
1918                                         free (frames);
1919                                 if (debug)
1920                                         fprintf (outfile, "throw %p\n", (void*)OBJ_ADDR (objdiff));
1921                         }
1922                         break;
1923                 }
1924                 case TYPE_SAMPLE: {
1925                         int subtype = *p & 0xf0;
1926                         if (subtype == TYPE_SAMPLE_HIT) {
1927                                 int i;
1928                                 int sample_type = decode_uleb128 (p + 1, &p);
1929                                 uint64_t tstamp = decode_uleb128 (p, &p);
1930                                 int count = decode_uleb128 (p, &p);
1931                                 for (i = 0; i < count; ++i) {
1932                                         uintptr_t ip = ptr_base + decode_sleb128 (p, &p);
1933                                         add_stat_sample (sample_type, ip);
1934                                         if (debug)
1935                                                 fprintf (outfile, "sample hit, type: %d at %p\n", sample_type, (void*)ip);
1936                                 }
1937                         } else if (subtype == TYPE_SAMPLE_USYM) {
1938                                 /* un unmanaged symbol description */
1939                                 uintptr_t addr = ptr_base + decode_sleb128 (p + 1, &p);
1940                                 uintptr_t size = decode_uleb128 (p, &p);
1941                                 char *name;
1942                                 name = pstrdup ((char*)p);
1943                                 add_unmanaged_symbol (addr, name, size);
1944                                 if (debug)
1945                                         fprintf (outfile, "unmanaged symbol %s at %p\n", name, (void*)addr);
1946                                 while (*p) p++;
1947                                 p++;
1948                         } else if (subtype == TYPE_SAMPLE_UBIN) {
1949                                 /* un unmanaged binary loaded in memory */
1950                                 uint64_t tdiff = decode_uleb128 (p + 1, &p);
1951                                 uintptr_t addr = decode_sleb128 (p, &p);
1952                                 uint64_t offset = decode_uleb128 (p, &p);
1953                                 uintptr_t size = decode_uleb128 (p, &p);
1954                                 char *name;
1955                                 LOG_TIME (time_base, tdiff);
1956                                 time_base += tdiff;
1957                                 name = pstrdup ((char*)p);
1958                                 add_unmanaged_binary (addr, name, size);
1959                                 if (debug)
1960                                         fprintf (outfile, "unmanaged binary %s at %p\n", name, (void*)addr);
1961                                 while (*p) p++;
1962                                 p++;
1963                         } else {
1964                                 return 0;
1965                         }
1966                         break;
1967                 }
1968                 default:
1969                         fprintf (outfile, "unhandled profiler event: 0x%x at file offset: %llu + %d (len: %d\n)\n", *p, file_offset, p - ctx->buf, len);
1970                         exit (1);
1971                 }
1972         }
1973         thread->last_time = time_base;
1974         for (i = 0; i < thread->stack_id; ++i)
1975                 thread->stack [i]->recurse_count = 0;
1976         return 1;
1977 }
1978
1979 static ProfContext*
1980 load_file (char *name)
1981 {
1982         unsigned char *p;
1983         ProfContext *ctx = calloc (sizeof (ProfContext), 1);
1984         if (strcmp (name, "-") == 0)
1985                 ctx->file = stdin;
1986         else
1987                 ctx->file = fopen (name, "rb");
1988         if (!ctx->file) {
1989                 printf ("Cannot open file: %s\n", name);
1990                 exit (1);
1991         }
1992 #if defined (HAVE_SYS_ZLIB)
1993         if (ctx->file != stdin)
1994                 ctx->gzfile = gzdopen (fileno (ctx->file), "rb");
1995 #endif
1996         if (!load_data (ctx, 32))
1997                 return NULL;
1998         p = ctx->buf;
1999         if (read_int32 (p) != LOG_HEADER_ID || p [6] > LOG_DATA_VERSION)
2000                 return NULL;
2001         ctx->version_major = p [4];
2002         ctx->version_minor = p [5];
2003         ctx->data_version = p [6];
2004         /* reading 64 bit files on 32 bit systems not supported yet */
2005         if (p [7] > sizeof (void*))
2006                 return NULL;
2007         if (read_int32 (p + 20)) /* flags must be 0 */
2008                 return NULL;
2009         ctx->startup_time = read_int64 (p + 8);
2010         ctx->timer_overhead = read_int32 (p + 16);
2011         ctx->pid = read_int32 (p + 24);
2012         ctx->port = read_int16 (p + 28);
2013         return ctx;
2014 }
2015
2016 enum {
2017         ALLOC_SORT_BYTES,
2018         ALLOC_SORT_COUNT
2019 };
2020 static int alloc_sort_mode = ALLOC_SORT_BYTES;
2021
2022 static int
2023 compare_class (const void *a, const void *b)
2024 {
2025         ClassDesc *const*A = a;
2026         ClassDesc *const*B = b;
2027         uint64_t vala, valb;
2028         if (alloc_sort_mode == ALLOC_SORT_BYTES) {
2029                 vala = (*A)->alloc_size;
2030                 valb = (*B)->alloc_size;
2031         } else {
2032                 vala = (*A)->allocs;
2033                 valb = (*B)->allocs;
2034         }
2035         if (valb == vala)
2036                 return 0;
2037         if (valb < vala)
2038                 return -1;
2039         return 1;
2040 }
2041
2042 static void
2043 dump_header (ProfContext *ctx)
2044 {
2045         time_t st = ctx->startup_time / 1000;
2046         char *t = ctime (&st);
2047         fprintf (outfile, "\nMono log profiler data\n");
2048         fprintf (outfile, "\tProfiler version: %d.%d\n", ctx->version_major, ctx->version_minor);
2049         fprintf (outfile, "\tData version: %d\n", ctx->data_version);
2050         fprintf (outfile, "\tMean timer overhead: %d nanoseconds\n", ctx->timer_overhead);
2051         fprintf (outfile, "\tProgram startup: %s", t);
2052         if (ctx->pid)
2053                 fprintf (outfile, "\tProgram ID: %d\n", ctx->pid);
2054         if (ctx->port)
2055                 fprintf (outfile, "\tServer listening on: %d\n", ctx->port);
2056 }
2057
2058 static void
2059 dump_traces (TraceDesc *traces, const char *desc)
2060 {
2061         int j;
2062         if (!show_traces)
2063                 return;
2064         if (!traces->count)
2065                 return;
2066         sort_context_array (traces);
2067         for (j = 0; j < traces->count; ++j) {
2068                 int k;
2069                 BackTrace *bt;
2070                 bt = traces->traces [j].bt;
2071                 if (!bt->count)
2072                         continue;
2073                 fprintf (outfile, "\t%llu %s from:\n", traces->traces [j].count, desc);
2074                 for (k = 0; k < bt->count; ++k)
2075                         fprintf (outfile, "\t\t%s\n", bt->methods [k]->name);
2076         }
2077 }
2078
2079 static void
2080 dump_threads (ProfContext *ctx)
2081 {
2082         ThreadContext *thread;
2083         fprintf (outfile, "\nThread summary\n");
2084         for (thread = ctx->threads; thread; thread = thread->next) {
2085                 fprintf (outfile, "\tThread: %p, name: \"%s\"\n", (void*)thread->thread_id, thread->name? thread->name: "");
2086         }
2087 }
2088
2089 static void
2090 dump_exceptions (void)
2091 {
2092         int i;
2093         fprintf (outfile, "\nException summary\n");
2094         fprintf (outfile, "\tThrows: %llu\n", throw_count);
2095         dump_traces (&exc_traces, "throws");
2096         for (i = 0; i <= MONO_EXCEPTION_CLAUSE_FAULT; ++i) {
2097                 if (!clause_summary [i])
2098                         continue;
2099                 fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), clause_summary [i]);
2100         }
2101 }
2102
2103 static int
2104 compare_monitor (const void *a, const void *b)
2105 {
2106         MonitorDesc *const*A = a;
2107         MonitorDesc *const*B = b;
2108         if ((*B)->wait_time == (*A)->wait_time)
2109                 return 0;
2110         if ((*B)->wait_time < (*A)->wait_time)
2111                 return -1;
2112         return 1;
2113 }
2114
2115 static void
2116 dump_monitors (void)
2117 {
2118         MonitorDesc **monitors;
2119         int i, j;
2120         if (!num_monitors)
2121                 return;
2122         monitors = malloc (sizeof (void*) * num_monitors);
2123         for (i = 0, j = 0; i < SMALL_HASH_SIZE; ++i) {
2124                 MonitorDesc *mdesc = monitor_hash [i];
2125                 while (mdesc) {
2126                         monitors [j++] = mdesc;
2127                         mdesc = mdesc->next;
2128                 }
2129         }
2130         qsort (monitors, num_monitors, sizeof (void*), compare_monitor);
2131         fprintf (outfile, "\nMonitor lock summary\n");
2132         for (i = 0; i < num_monitors; ++i) {
2133                 MonitorDesc *mdesc = monitors [i];
2134                 fprintf (outfile, "\tLock object %p: %d contentions\n", (void*)mdesc->objid, (int)mdesc->contentions);
2135                 fprintf (outfile, "\t\t%.6f secs total wait time, %.6f max, %.6f average\n",
2136                         mdesc->wait_time/1000000000.0, mdesc->max_wait_time/1000000000.0, mdesc->wait_time/1000000000.0/mdesc->contentions);
2137                 dump_traces (&mdesc->traces, "contentions");
2138         }
2139         fprintf (outfile, "\tLock contentions: %llu\n", monitor_contention);
2140         fprintf (outfile, "\tLock acquired: %llu\n", monitor_acquired);
2141         fprintf (outfile, "\tLock failures: %llu\n", monitor_failed);
2142 }
2143
2144 static void
2145 dump_gcs (void)
2146 {
2147         int i;
2148         fprintf (outfile, "\nGC summary\n");
2149         fprintf (outfile, "\tGC resizes: %d\n", gc_resizes);
2150         fprintf (outfile, "\tMax heap size: %llu\n", max_heap_size);
2151         fprintf (outfile, "\tObject moves: %llu\n", gc_object_moves);
2152         for (i = 0; i < 3; ++i) {
2153                 if (!gc_info [i].count)
2154                         continue;
2155                 fprintf (outfile, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
2156                         i, gc_info [i].count, gc_info [i].max_time / 1000, gc_info [i].total_time / 1000,
2157                         gc_info [i].total_time / gc_info [i].count / 1000);
2158         }
2159         for (i = 0; i < 3; ++i) {
2160                 if (!handle_info [i].max_live)
2161                         continue;
2162                 fprintf (outfile, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
2163                         get_handle_name (i), handle_info [i].created, handle_info [i].destroyed, handle_info [i].max_live);
2164                 dump_traces (&handle_info [i].traces, "created");
2165         }
2166 }
2167
2168 static void
2169 dump_jit (void)
2170 {
2171         int i;
2172         int code_size = 0;
2173         int compiled_methods = 0;
2174         MethodDesc* m;
2175         fprintf (outfile, "\nJIT summary\n");
2176         for (i = 0; i < HASH_SIZE; ++i) {
2177                 m = method_hash [i];
2178                 for (m = method_hash [i]; m; m = m->next) {
2179                         if (!m->code)
2180                                 continue;
2181                         compiled_methods++;
2182                         code_size += m->len;
2183                 }
2184         }
2185         fprintf (outfile, "\tCompiled methods: %d\n", compiled_methods);
2186         fprintf (outfile, "\tGenerated code size: %d\n", code_size);
2187 }
2188
2189 static void
2190 dump_allocations (void)
2191 {
2192         int i, c;
2193         intptr_t allocs = 0;
2194         uint64_t size = 0;
2195         int header_done = 0;
2196         ClassDesc **classes = malloc (num_classes * sizeof (void*));
2197         ClassDesc *cd;
2198         c = 0;
2199         for (i = 0; i < HASH_SIZE; ++i) {
2200                 cd = class_hash [i];
2201                 while (cd) {
2202                         classes [c++] = cd;
2203                         cd = cd->next;
2204                 }
2205         }
2206         qsort (classes, num_classes, sizeof (void*), compare_class);
2207         for (i = 0; i < num_classes; ++i) {
2208                 cd = classes [i];
2209                 if (!cd->allocs)
2210                         continue;
2211                 allocs += cd->allocs;
2212                 size += cd->alloc_size;
2213                 if (!header_done++) {
2214                         fprintf (outfile, "\nAllocation summary\n");
2215                         fprintf (outfile, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
2216                 }
2217                 fprintf (outfile, "%10llu %10d %8llu %s\n", cd->alloc_size, cd->allocs, cd->alloc_size / cd->allocs, cd->name);
2218                 dump_traces (&cd->traces, "bytes");
2219         }
2220         if (allocs)
2221                 fprintf (outfile, "Total memory allocated: %llu bytes in %d objects\n", size, allocs);
2222 }
2223
2224 enum {
2225         METHOD_SORT_TOTAL,
2226         METHOD_SORT_SELF,
2227         METHOD_SORT_CALLS
2228 };
2229
2230 static int method_sort_mode = METHOD_SORT_TOTAL;
2231
2232 static int
2233 compare_method (const void *a, const void *b)
2234 {
2235         MethodDesc *const*A = a;
2236         MethodDesc *const*B = b;
2237         uint64_t vala, valb;
2238         if (method_sort_mode == METHOD_SORT_SELF) {
2239                 vala = (*A)->self_time;
2240                 valb = (*B)->self_time;
2241         } else if (method_sort_mode == METHOD_SORT_CALLS) {
2242                 vala = (*A)->calls;
2243                 valb = (*B)->calls;
2244         } else {
2245                 vala = (*A)->total_time;
2246                 valb = (*B)->total_time;
2247         }
2248         if (vala == valb)
2249                 return 0;
2250         if (valb < vala)
2251                 return -1;
2252         return 1;
2253 }
2254
2255 static void
2256 dump_metadata (void)
2257 {
2258         fprintf (outfile, "\nMetadata summary\n");
2259         fprintf (outfile, "\tLoaded images: %d\n", num_images);
2260         if (verbose) {
2261                 ImageDesc *image;
2262                 int i;
2263                 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
2264                         image = image_hash [i];
2265                         while (image) {
2266                                 fprintf (outfile, "\t\t%s\n", image->filename);
2267                                 image = image->next;
2268                         }
2269                 }
2270         }
2271
2272 }
2273
2274 static void
2275 dump_methods (void)
2276 {
2277         int i, c;
2278         uint64_t calls = 0;
2279         int header_done = 0;
2280         MethodDesc **methods = malloc (num_methods * sizeof (void*));
2281         MethodDesc *cd;
2282         c = 0;
2283         for (i = 0; i < HASH_SIZE; ++i) {
2284                 cd = method_hash [i];
2285                 while (cd) {
2286                         cd->total_time = cd->self_time + cd->callee_time;
2287                         methods [c++] = cd;
2288                         cd = cd->next;
2289                 }
2290         }
2291         qsort (methods, num_methods, sizeof (void*), compare_method);
2292         for (i = 0; i < num_methods; ++i) {
2293                 uint64_t msecs;
2294                 uint64_t smsecs;
2295                 cd = methods [i];
2296                 if (!cd->calls)
2297                         continue;
2298                 calls += cd->calls;
2299                 msecs = cd->total_time / 1000000;
2300                 smsecs = (cd->total_time - cd->callee_time) / 1000000;
2301                 if (!msecs && !verbose)
2302                         continue;
2303                 if (!header_done++) {
2304                         fprintf (outfile, "\nMethod call summary\n");
2305                         fprintf (outfile, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
2306                 }
2307                 fprintf (outfile, "%8llu %8llu %10llu %s\n", msecs, smsecs, cd->calls, cd->name);
2308                 dump_traces (&cd->traces, "calls");
2309         }
2310         if (calls)
2311                 fprintf (outfile, "Total calls: %llu\n", calls);
2312 }
2313
2314 static int
2315 compare_heap_class (const void *a, const void *b)
2316 {
2317         HeapClassDesc *const*A = a;
2318         HeapClassDesc *const*B = b;
2319         uint64_t vala, valb;
2320         if (alloc_sort_mode == ALLOC_SORT_BYTES) {
2321                 vala = (*A)->total_size;
2322                 valb = (*B)->total_size;
2323         } else {
2324                 vala = (*A)->count;
2325                 valb = (*B)->count;
2326         }
2327         if (valb == vala)
2328                 return 0;
2329         if (valb < vala)
2330                 return -1;
2331         return 1;
2332 }
2333
2334 static int
2335 compare_rev_class (const void *a, const void *b)
2336 {
2337         const HeapClassRevRef *A = a;
2338         const HeapClassRevRef *B = b;
2339         if (B->count == A->count)
2340                 return 0;
2341         if (B->count < A->count)
2342                 return -1;
2343         return 1;
2344 }
2345
2346 static void
2347 dump_rev_claases (HeapClassRevRef *revs, int count)
2348 {
2349         int j;
2350         if (!show_traces)
2351                 return;
2352         if (!count)
2353                 return;
2354         for (j = 0; j < count; ++j) {
2355                 HeapClassDesc *cd = revs [j].klass;
2356                 fprintf (outfile, "\t\t%llu references from: %s\n", revs [j].count, cd->klass->name);
2357         }
2358 }
2359
2360 static void
2361 heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
2362 {
2363         uint64_t size = 0;
2364         uint64_t count = 0;
2365         int ccount = 0;
2366         int i;
2367         HeapClassDesc *cd;
2368         HeapClassDesc **sorted;
2369         sorted = malloc (sizeof (void*) * hs->class_count);
2370         for (i = 0; i < hs->hash_size; ++i) {
2371                 cd = hs->class_hash [i];
2372                 if (!cd)
2373                         continue;
2374                 count += cd->count;
2375                 size += cd->total_size;
2376                 sorted [ccount++] = cd;
2377         }
2378         hs->sorted = sorted;
2379         qsort (sorted, ccount, sizeof (void*), compare_heap_class);
2380         fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %d\n",
2381                 hs_num, (hs->timestamp - startup_time)/1000000000.0, size, count, ccount, hs->num_roots);
2382         if (!verbose && ccount > 30)
2383                 ccount = 30;
2384         fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
2385         for (i = 0; i < ccount; ++i) {
2386                 HeapClassRevRef *rev_sorted;
2387                 int j, k;
2388                 HeapClassDesc *ocd = NULL;
2389                 cd = sorted [i];
2390                 if (last_hs)
2391                         ocd = heap_class_lookup (last_hs, cd->klass);
2392                 fprintf (outfile, "\t%10llu %10llu %8llu %s", cd->total_size, cd->count, cd->total_size / cd->count, cd->klass->name);
2393                 if (ocd) {
2394                         int64_t bdiff = cd->total_size - ocd->total_size;
2395                         int64_t cdiff = cd->count - ocd->count;
2396                         fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", bdiff, cdiff);
2397                 } else {
2398                         fprintf (outfile, "\n");
2399                 }
2400                 if (!collect_traces)
2401                         continue;
2402                 rev_sorted = malloc (cd->rev_count * sizeof (HeapClassRevRef));
2403                 k = 0;
2404                 for (j = 0; j < cd->rev_hash_size; ++j) {
2405                         if (cd->rev_hash [j].klass)
2406                                 rev_sorted [k++] = cd->rev_hash [j];
2407                 }
2408                 assert (cd->rev_count == k);
2409                 qsort (rev_sorted, cd->rev_count, sizeof (HeapClassRevRef), compare_rev_class);
2410                 if (cd->root_references)
2411                         fprintf (outfile, "\t\t%d root references (%d pinning)\n", cd->root_references, cd->pinned_references);
2412                 dump_rev_claases (rev_sorted, cd->rev_count);
2413                 free (rev_sorted);
2414         }
2415         free (sorted);
2416 }
2417
2418 static int
2419 compare_heap_shots (const void *a, const void *b)
2420 {
2421         HeapShot *const*A = a;
2422         HeapShot *const*B = b;
2423         if ((*B)->timestamp == (*A)->timestamp)
2424                 return 0;
2425         if ((*B)->timestamp > (*A)->timestamp)
2426                 return -1;
2427         return 1;
2428 }
2429
2430 static void
2431 dump_heap_shots (void)
2432 {
2433         HeapShot **hs_sorted;
2434         HeapShot *hs;
2435         HeapShot *last_hs = NULL;
2436         int i;
2437         if (!heap_shots)
2438                 return;
2439         hs_sorted = malloc (num_heap_shots * sizeof (void*));
2440         fprintf (outfile, "\nHeap shot summary\n");
2441         i = 0;
2442         for (hs = heap_shots; hs; hs = hs->next)
2443                 hs_sorted [i++] = hs;
2444         qsort (hs_sorted, num_heap_shots, sizeof (void*), compare_heap_shots);
2445         for (i = 0; i < num_heap_shots; ++i) {
2446                 hs = hs_sorted [i];
2447                 heap_shot_summary (hs, i, last_hs);
2448                 last_hs = hs;
2449         }
2450 }
2451
2452 static void
2453 flush_context (ProfContext *ctx)
2454 {
2455         ThreadContext *thread;
2456         /* FIXME: sometimes there are leftovers: indagate */
2457         for (thread = ctx->threads; thread; thread = thread->next) {
2458                 while (thread->stack_id) {
2459                         if (debug)
2460                                 fprintf (outfile, "thread %p has %d items on stack\n", (void*)thread->thread_id, thread->stack_id);
2461                         pop_method (thread, thread->stack [thread->stack_id - 1], thread->last_time);
2462                 }
2463         }
2464 }
2465
2466 static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,heapshot";
2467
2468 static const char*
2469 match_option (const char *p, const char *opt)
2470 {
2471         int len = strlen (opt);
2472         if (strncmp (p, opt, len) == 0) {
2473                 if (p [len] == ',')
2474                         len++;
2475                 return p + len;
2476         }
2477         return p;
2478 }
2479
2480 static int
2481 print_reports (ProfContext *ctx, const char *reps, int parse_only)
2482 {
2483         const char *opt;
2484         const char *p;
2485         for (p = reps; *p; p = opt) {
2486                 if ((opt = match_option (p, "header")) != p) {
2487                         if (!parse_only)
2488                                 dump_header (ctx);
2489                         continue;
2490                 }
2491                 if ((opt = match_option (p, "thread")) != p) {
2492                         if (!parse_only)
2493                                 dump_threads (ctx);
2494                         continue;
2495                 }
2496                 if ((opt = match_option (p, "gc")) != p) {
2497                         if (!parse_only)
2498                                 dump_gcs ();
2499                         continue;
2500                 }
2501                 if ((opt = match_option (p, "jit")) != p) {
2502                         if (!parse_only)
2503                                 dump_jit ();
2504                         continue;
2505                 }
2506                 if ((opt = match_option (p, "alloc")) != p) {
2507                         if (!parse_only)
2508                                 dump_allocations ();
2509                         continue;
2510                 }
2511                 if ((opt = match_option (p, "call")) != p) {
2512                         if (!parse_only)
2513                                 dump_methods ();
2514                         continue;
2515                 }
2516                 if ((opt = match_option (p, "metadata")) != p) {
2517                         if (!parse_only)
2518                                 dump_metadata ();
2519                         continue;
2520                 }
2521                 if ((opt = match_option (p, "exception")) != p) {
2522                         if (!parse_only)
2523                                 dump_exceptions ();
2524                         continue;
2525                 }
2526                 if ((opt = match_option (p, "monitor")) != p) {
2527                         if (!parse_only)
2528                                 dump_monitors ();
2529                         continue;
2530                 }
2531                 if ((opt = match_option (p, "heapshot")) != p) {
2532                         if (!parse_only)
2533                                 dump_heap_shots ();
2534                         continue;
2535                 }
2536                 if ((opt = match_option (p, "sample")) != p) {
2537                         if (!parse_only)
2538                                 dump_samples ();
2539                         continue;
2540                 }
2541                 return 0;
2542         }
2543         return 1;
2544 }
2545
2546 static int
2547 add_find_spec (const char *p)
2548 {
2549         if (p [0] == 'S' && p [1] == ':') {
2550                 char *vale;
2551                 find_size = strtoul (p + 2, &vale, 10);
2552                 return 1;
2553         } else if (p [0] == 'T' && p [1] == ':') {
2554                 find_name = p + 2;
2555                 return 1;
2556         }
2557         return 0;
2558 }
2559
2560 static void
2561 usage (void)
2562 {
2563         printf ("Mono log profiler report version %d.%d\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR);
2564         printf ("Usage: mprof-report [OPTIONS] FILENAME\n");
2565         printf ("FILENAME can be '-' to read from standard input.\n");
2566         printf ("Options:\n");
2567         printf ("\t--help               display this help\n");
2568         printf ("\t--out=FILE           write to FILE instead of stdout\n");
2569         printf ("\t--traces             collect and show backtraces\n"); 
2570         printf ("\t--maxframes=NUM      limit backtraces to NUM entries\n");
2571         printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
2572         printf ("\t                     %s\n", reports);
2573         printf ("\t--method-sort=MODE   sort methods according to MODE: total, self, calls\n");
2574         printf ("\t--alloc-sort=MODE    sort allocations according to MODE: bytes, count\n");
2575         printf ("\t--track=OB1[,OB2...] track what happens to objects OBJ1, O2 etc.\n");
2576         printf ("\t--find=FINDSPEC      find and track objects matching FINFSPEC, where FINDSPEC is:\n");
2577         printf ("\t                     S:minimum_size or T:partial_name\n");
2578         printf ("\t--thread=THREADID    consider just the data for thread THREADID\n");
2579         printf ("\t--time=FROM-TO       consider data FROM seconds from startup up to TO seconds\n");
2580         printf ("\t--verbose            increase verbosity level\n");
2581         printf ("\t--debug              display decoding debug info for mprof-report devs\n");
2582 }
2583
2584 int
2585 main (int argc, char *argv[])
2586 {
2587         ProfContext *ctx;
2588         int i;
2589         outfile = stdout;
2590         for (i = 1; i < argc; ++i) {
2591                 if (strcmp ("--debug", argv [i]) == 0) {
2592                         debug++;
2593                 } else if (strcmp ("--help", argv [i]) == 0) {
2594                         usage ();
2595                         return 0;
2596                 } else if (strncmp ("--alloc-sort=", argv [i], 13) == 0) {
2597                         const char *val = argv [i] + 13;
2598                         if (strcmp (val, "bytes") == 0) {
2599                                 alloc_sort_mode = ALLOC_SORT_BYTES;
2600                         } else if (strcmp (val, "count") == 0) {
2601                                 alloc_sort_mode = ALLOC_SORT_COUNT;
2602                         } else {
2603                                 usage ();
2604                                 return 1;
2605                         }
2606                 } else if (strncmp ("--method-sort=", argv [i], 14) == 0) {
2607                         const char *val = argv [i] + 14;
2608                         if (strcmp (val, "total") == 0) {
2609                                 method_sort_mode = METHOD_SORT_TOTAL;
2610                         } else if (strcmp (val, "self") == 0) {
2611                                 method_sort_mode = METHOD_SORT_SELF;
2612                         } else if (strcmp (val, "calls") == 0) {
2613                                 method_sort_mode = METHOD_SORT_CALLS;
2614                         } else {
2615                                 usage ();
2616                                 return 1;
2617                         }
2618                 } else if (strncmp ("--reports=", argv [i], 10) == 0) {
2619                         const char *val = argv [i] + 10;
2620                         if (!print_reports (NULL, val, 1)) {
2621                                 usage ();
2622                                 return 1;
2623                         }
2624                         reports = val;
2625                 } else if (strncmp ("--out=", argv [i], 6) == 0) {
2626                         const char *val = argv [i] + 6;
2627                         outfile = fopen (val, "w");
2628                         if (!outfile) {
2629                                 printf ("Cannot open output file: %s\n", val);
2630                                 return 1;
2631                         }
2632                 } else if (strncmp ("--maxframes=", argv [i], 12) == 0) {
2633                         const char *val = argv [i] + 12;
2634                         char *vale;
2635                         trace_max = strtoul (val, &vale, 10);
2636                 } else if (strncmp ("--find=", argv [i], 7) == 0) {
2637                         const char *val = argv [i] + 7;
2638                         if (!add_find_spec (val)) {
2639                                 usage ();
2640                                 return 1;
2641                         }
2642                 } else if (strncmp ("--track=", argv [i], 8) == 0) {
2643                         const char *val = argv [i] + 8;
2644                         char *vale;
2645                         while (*val) {
2646                                 uintptr_t tracked_obj;
2647                                 if (*val == ',') {
2648                                         val++;
2649                                         continue;
2650                                 }
2651                                 tracked_obj = strtoul (val, &vale, 0);
2652                                 found_object (tracked_obj);
2653                                 val = vale;
2654                         }
2655                 } else if (strncmp ("--thread=", argv [i], 9) == 0) {
2656                         const char *val = argv [i] + 9;
2657                         char *vale;
2658                         thread_filter = strtoul (val, &vale, 0);
2659                 } else if (strncmp ("--time=", argv [i], 7) == 0) {
2660                         char *val = pstrdup (argv [i] + 7);
2661                         double from_secs, to_secs;
2662                         char *top = strchr (val, '-');
2663                         if (!top) {
2664                                 usage ();
2665                                 return 1;
2666                         }
2667                         *top++ = 0;
2668                         from_secs = atof (val);
2669                         to_secs = atof (top);
2670                         free (val);
2671                         if (from_secs > to_secs) {
2672                                 usage ();
2673                                 return 1;
2674                         }
2675                         time_from = from_secs * 1000000000;
2676                         time_to = to_secs * 1000000000;
2677                 } else if (strcmp ("--verbose", argv [i]) == 0) {
2678                         verbose++;
2679                 } else if (strcmp ("--traces", argv [i]) == 0) {
2680                         show_traces = 1;
2681                         collect_traces = 1;
2682                 } else {
2683                         break;
2684                 }
2685         }
2686         if (i >= argc) {
2687                 usage ();
2688                 return 2;
2689         }
2690         ctx = load_file (argv [i]);
2691         if (!ctx) {
2692                 printf ("Not a log profiler data file (or unsupported version).\n");
2693                 return 1;
2694         }
2695         while (decode_buffer (ctx));
2696         flush_context (ctx);
2697         if (num_tracked_objects)
2698                 return 0;
2699         print_reports (ctx, reports, 0);
2700         return 0;
2701 }
2702