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