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