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