Merge pull request #1624 from esdrubal/getprocesstimes
[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         const char *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         const char *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 (strcmp (csection->value, counter->section) == 0) {
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 (const char *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 (strcmp (csection->value, counter->section) == 0) {
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", 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", 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", 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 static int num_jit_helpers = 0;
1900 static int jit_helpers_code_size = 0;
1901
1902 static const char*
1903 code_buffer_desc (int type)
1904 {
1905         switch (type) {
1906         case MONO_PROFILER_CODE_BUFFER_METHOD:
1907                 return "method";
1908         case MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE:
1909                 return "method trampoline";
1910         case MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE:
1911                 return "unbox trampoline";
1912         case MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE:
1913                 return "imt trampoline";
1914         case MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE:
1915                 return "generics trampoline";
1916         case MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE:
1917                 return "specific trampoline";
1918         case MONO_PROFILER_CODE_BUFFER_HELPER:
1919                 return "misc helper";
1920         case MONO_PROFILER_CODE_BUFFER_MONITOR:
1921                 return "monitor/lock";
1922         case MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE:
1923                 return "delegate invoke";
1924         case MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING:
1925                 return "exception handling";
1926         default:
1927                 return "unspecified";
1928         }
1929 }
1930
1931 #define OBJ_ADDR(diff) ((obj_base + diff) << 3)
1932 #define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
1933
1934 static int
1935 decode_buffer (ProfContext *ctx)
1936 {
1937         unsigned char *p;
1938         unsigned char *end;
1939         intptr_t thread_id;
1940         intptr_t ptr_base;
1941         intptr_t obj_base;
1942         intptr_t method_base;
1943         uint64_t time_base;
1944         uint64_t file_offset;
1945         int len, i;
1946         ThreadContext *thread;
1947
1948 #ifdef HAVE_SYS_ZLIB
1949         if (ctx->gzfile)
1950                 file_offset = gztell (ctx->gzfile);
1951         else
1952 #endif
1953                 file_offset = ftell (ctx->file);
1954         if (!load_data (ctx, 48))
1955                 return 0;
1956         p = ctx->buf;
1957         if (read_int32 (p) != BUF_ID) {
1958                 fprintf (outfile, "Incorrect buffer id: 0x%x\n", read_int32 (p));
1959                 for (i = 0; i < 48; ++i) {
1960                         fprintf (outfile, "0x%x%s", p [i], i % 8?" ":"\n");
1961                 }
1962                 return 0;
1963         }
1964         len = read_int32 (p + 4);
1965         time_base = read_int64 (p + 8);
1966         ptr_base = read_int64 (p + 16);
1967         obj_base = read_int64 (p + 24);
1968         thread_id = read_int64 (p + 32);
1969         method_base = read_int64 (p + 40);
1970         if (debug)
1971                 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);
1972         thread = load_thread (ctx, thread_id);
1973         if (!load_data (ctx, len))
1974                 return 0;
1975         if (!startup_time) {
1976                 startup_time = time_base;
1977                 if (use_time_filter) {
1978                         time_from += startup_time;
1979                         time_to += startup_time;
1980                 }
1981                 if (!thread->name)
1982                         thread->name = pstrdup ("Main");
1983         }
1984         for (i = 0; i < thread->stack_id; ++i)
1985                 thread->stack [i]->recurse_count++;
1986         p = ctx->buf;
1987         end = p + len;
1988         while (p < end) {
1989                 switch (*p & 0xf) {
1990                 case TYPE_GC: {
1991                         int subtype = *p & 0xf0;
1992                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
1993                         LOG_TIME (time_base, tdiff);
1994                         time_base += tdiff;
1995                         if (subtype == TYPE_GC_RESIZE) {
1996                                 uint64_t new_size = decode_uleb128 (p, &p);
1997                                 if (debug)
1998                                         fprintf (outfile, "gc heap resized to %llu\n", (unsigned long long) new_size);
1999                                 gc_resizes++;
2000                                 if (new_size > max_heap_size)
2001                                         max_heap_size = new_size;
2002                         } else if (subtype == TYPE_GC_EVENT) {
2003                                 uint64_t ev = decode_uleb128 (p, &p);
2004                                 int gen = decode_uleb128 (p, &p);
2005                                 if (debug)
2006                                         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);
2007                                 if (gen > 2) {
2008                                         fprintf (outfile, "incorrect gc gen: %d\n", gen);
2009                                         break;
2010                                 }
2011                                 if (ev == MONO_GC_EVENT_START) {
2012                                         thread->gc_start_times [gen] = time_base;
2013                                         gc_info [gen].count++;
2014                                 } else if (ev == MONO_GC_EVENT_END) {
2015                                         tdiff = time_base - thread->gc_start_times [gen];
2016                                         gc_info [gen].total_time += tdiff;
2017                                         if (tdiff > gc_info [gen].max_time)
2018                                                 gc_info [gen].max_time = tdiff;
2019                                 }
2020                         } else if (subtype == TYPE_GC_MOVE) {
2021                                 int j, num = decode_uleb128 (p, &p);
2022                                 gc_object_moves += num / 2;
2023                                 for (j = 0; j < num; j += 2) {
2024                                         intptr_t obj1diff = decode_sleb128 (p, &p);
2025                                         intptr_t obj2diff = decode_sleb128 (p, &p);
2026                                         if (num_tracked_objects)
2027                                                 track_move (OBJ_ADDR (obj1diff), OBJ_ADDR (obj2diff));
2028                                         if (debug) {
2029                                                 fprintf (outfile, "moved obj %p to %p\n", (void*)OBJ_ADDR (obj1diff), (void*)OBJ_ADDR (obj2diff));
2030                                         }
2031                                 }
2032                         } else if (subtype == TYPE_GC_HANDLE_CREATED) {
2033                                 int htype = decode_uleb128 (p, &p);
2034                                 uint32_t handle = decode_uleb128 (p, &p);
2035                                 intptr_t objdiff = decode_sleb128 (p, &p);
2036                                 if (htype > 3)
2037                                         return 0;
2038                                 handle_info [htype].created++;
2039                                 handle_info [htype].live++;
2040                                 add_trace_thread (thread, &handle_info [htype].traces, 1);
2041                                 /* FIXME: we don't take into account timing here */
2042                                 if (handle_info [htype].live > handle_info [htype].max_live)
2043                                         handle_info [htype].max_live = handle_info [htype].live;
2044                                 if (num_tracked_objects)
2045                                         track_handle (OBJ_ADDR (objdiff), htype, handle);
2046                                 if (debug)
2047                                         fprintf (outfile, "handle (%s) %u created for object %p\n", get_handle_name (htype), handle, (void*)OBJ_ADDR (objdiff));
2048                         } else if (subtype == TYPE_GC_HANDLE_DESTROYED) {
2049                                 int htype = decode_uleb128 (p, &p);
2050                                 uint32_t handle = decode_uleb128 (p, &p);
2051                                 if (htype > 3)
2052                                         return 0;
2053                                 handle_info [htype].destroyed ++;
2054                                 handle_info [htype].live--;
2055                                 if (debug)
2056                                         fprintf (outfile, "handle (%s) %u destroyed\n", get_handle_name (htype), handle);
2057                         }
2058                         break;
2059                 }
2060                 case TYPE_METADATA: {
2061                         int error = *p & TYPE_LOAD_ERR;
2062                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2063                         int mtype = *p++;
2064                         intptr_t ptrdiff = decode_sleb128 (p, &p);
2065                         LOG_TIME (time_base, tdiff);
2066                         time_base += tdiff;
2067                         if (mtype == TYPE_CLASS) {
2068                                 intptr_t imptrdiff = decode_sleb128 (p, &p);
2069                                 uint64_t flags = decode_uleb128 (p, &p);
2070                                 if (flags) {
2071                                         fprintf (outfile, "non-zero flags in class\n");
2072                                         return 0;
2073                                 }
2074                                 if (debug)
2075                                         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);
2076                                 if (!error)
2077                                         add_class (ptr_base + ptrdiff, (char*)p);
2078                                 while (*p) p++;
2079                                 p++;
2080                         } else if (mtype == TYPE_IMAGE) {
2081                                 uint64_t flags = decode_uleb128 (p, &p);
2082                                 if (flags) {
2083                                         fprintf (outfile, "non-zero flags in image\n");
2084                                         return 0;
2085                                 }
2086                                 if (debug)
2087                                         fprintf (outfile, "loaded image %p (%s) at %llu\n", (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
2088                                 if (!error)
2089                                         add_image (ptr_base + ptrdiff, (char*)p);
2090                                 while (*p) p++;
2091                                 p++;
2092                         } else if (mtype == TYPE_THREAD) {
2093                                 ThreadContext *nt;
2094                                 uint64_t flags = decode_uleb128 (p, &p);
2095                                 if (flags) {
2096                                         fprintf (outfile, "non-zero flags in thread\n");
2097                                         return 0;
2098                                 }
2099                                 nt = get_thread (ctx, ptr_base + ptrdiff);
2100                                 nt->name = pstrdup ((char*)p);
2101                                 if (debug)
2102                                         fprintf (outfile, "thread %p named: %s\n", (void*)(ptr_base + ptrdiff), p);
2103                                 while (*p) p++;
2104                                 p++;
2105                         }
2106                         break;
2107                 }
2108                 case TYPE_ALLOC: {
2109                         int has_bt = *p & TYPE_ALLOC_BT;
2110                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2111                         intptr_t ptrdiff = decode_sleb128 (p, &p);
2112                         intptr_t objdiff = decode_sleb128 (p, &p);
2113                         uint64_t len;
2114                         int num_bt = 0;
2115                         MethodDesc* sframes [8];
2116                         MethodDesc** frames = sframes;
2117                         ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
2118                         len = decode_uleb128 (p, &p);
2119                         LOG_TIME (time_base, tdiff);
2120                         time_base += tdiff;
2121                         if (debug)
2122                                 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);
2123                         if (has_bt) {
2124                                 num_bt = 8;
2125                                 frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
2126                                 if (!frames) {
2127                                         fprintf (outfile, "Cannot load backtrace\n");
2128                                         return 0;
2129                                 }
2130                         }
2131                         if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2132                                 BackTrace *bt;
2133                                 cd->allocs++;
2134                                 cd->alloc_size += len;
2135                                 if (has_bt)
2136                                         bt = add_trace_methods (frames, num_bt, &cd->traces, len);
2137                                 else
2138                                         bt = add_trace_thread (thread, &cd->traces, len);
2139                                 if (find_size && len >= find_size) {
2140                                         if (!find_name || strstr (cd->name, find_name))
2141                                                 found_object (OBJ_ADDR (objdiff));
2142                                 } else if (!find_size && find_name && strstr (cd->name, find_name)) {
2143                                         found_object (OBJ_ADDR (objdiff));
2144                                 }
2145                                 if (num_tracked_objects)
2146                                         tracked_creation (OBJ_ADDR (objdiff), cd, len, bt, time_base);
2147                         }
2148                         if (frames != sframes)
2149                                 free (frames);
2150                         break;
2151                 }
2152                 case TYPE_METHOD: {
2153                         int subtype = *p & 0xf0;
2154                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2155                         int64_t ptrdiff = decode_sleb128 (p, &p);
2156                         LOG_TIME (time_base, tdiff);
2157                         time_base += tdiff;
2158                         method_base += ptrdiff;
2159                         if (subtype == TYPE_JIT) {
2160                                 intptr_t codediff = decode_sleb128 (p, &p);
2161                                 int codelen = decode_uleb128 (p, &p);
2162                                 MethodDesc *jitted_method;
2163                                 if (debug)
2164                                         fprintf (outfile, "jitted method %p (%s), size: %d, code: %p\n", (void*)(method_base), p, codelen, (void*)(ptr_base + codediff));
2165                                 jitted_method = add_method (method_base, (char*)p, ptr_base + codediff, codelen);
2166                                 if (!(time_base >= time_from && time_base < time_to))
2167                                         jitted_method->ignore_jit = 1;
2168                                 while (*p) p++;
2169                                 p++;
2170                         } else {
2171                                 MethodDesc *method;
2172                                 if ((thread_filter && thread_filter != thread->thread_id))
2173                                         break;
2174                                 if (!(time_base >= time_from && time_base < time_to))
2175                                         break;
2176                                 method = lookup_method (method_base);
2177                                 if (subtype == TYPE_ENTER) {
2178                                         add_trace_thread (thread, &method->traces, 1);
2179                                         push_method (thread, method, time_base);
2180                                 } else {
2181                                         pop_method (thread, method, time_base);
2182                                 }
2183                                 if (debug)
2184                                         fprintf (outfile, "%s method %s\n", subtype == TYPE_ENTER? "enter": subtype == TYPE_EXC_LEAVE? "exleave": "leave", method->name);
2185                         }
2186                         break;
2187                 }
2188                 case TYPE_HEAP: {
2189                         int subtype = *p & 0xf0;
2190                         if (subtype == TYPE_HEAP_OBJECT) {
2191                                 HeapObjectDesc *ho = NULL;
2192                                 int i;
2193                                 intptr_t objdiff = decode_sleb128 (p + 1, &p);
2194                                 intptr_t ptrdiff = decode_sleb128 (p, &p);
2195                                 uint64_t size = decode_uleb128 (p, &p);
2196                                 uintptr_t num = decode_uleb128 (p, &p);
2197                                 uintptr_t ref_offset = 0;
2198                                 uintptr_t last_obj_offset = 0;
2199                                 ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
2200                                 if (size) {
2201                                         HeapClassDesc *hcd = add_heap_shot_class (thread->current_heap_shot, cd, size);
2202                                         if (collect_traces) {
2203                                                 ho = alloc_heap_obj (OBJ_ADDR (objdiff), hcd, num);
2204                                                 add_heap_shot_obj (thread->current_heap_shot, ho);
2205                                                 ref_offset = 0;
2206                                         }
2207                                 } else {
2208                                         if (collect_traces)
2209                                                 ho = heap_shot_obj_add_refs (thread->current_heap_shot, OBJ_ADDR (objdiff), num, &ref_offset);
2210                                 }
2211                                 for (i = 0; i < num; ++i) {
2212                                         /* FIXME: use object distance to measure how good
2213                                          * the GC is at keeping related objects close
2214                                          */
2215                                         uintptr_t offset = ctx->data_version > 1? last_obj_offset + decode_uleb128 (p, &p): -1;
2216                                         intptr_t obj1diff = decode_sleb128 (p, &p);
2217                                         last_obj_offset = offset;
2218                                         if (collect_traces)
2219                                                 ho->refs [ref_offset + i] = OBJ_ADDR (obj1diff);
2220                                         if (num_tracked_objects)
2221                                                 track_obj_reference (OBJ_ADDR (obj1diff), OBJ_ADDR (objdiff), cd);
2222                                 }
2223                                 if (debug && size)
2224                                         fprintf (outfile, "traced object %p, size %llu (%s), refs: %zd\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) size, cd->name, num);
2225                         } else if (subtype == TYPE_HEAP_ROOT) {
2226                                 uintptr_t num = decode_uleb128 (p + 1, &p);
2227                                 uintptr_t gc_num G_GNUC_UNUSED = decode_uleb128 (p, &p);
2228                                 int i;
2229                                 for (i = 0; i < num; ++i) {
2230                                         intptr_t objdiff = decode_sleb128 (p, &p);
2231                                         int root_type = decode_uleb128 (p, &p);
2232                                         /* we just discard the extra info for now */
2233                                         uintptr_t extra_info = decode_uleb128 (p, &p);
2234                                         if (debug)
2235                                                 fprintf (outfile, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff), get_root_name (root_type));
2236                                         if (collect_traces)
2237                                                 thread_add_root (thread, OBJ_ADDR (objdiff), root_type, extra_info);
2238                                 }
2239                         } else if (subtype == TYPE_HEAP_END) {
2240                                 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2241                                 LOG_TIME (time_base, tdiff);
2242                                 time_base += tdiff;
2243                                 if (debug)
2244                                         fprintf (outfile, "heap shot end\n");
2245                                 if (collect_traces) {
2246                                         HeapShot *hs = thread->current_heap_shot;
2247                                         if (hs && thread->num_roots) {
2248                                                 /* transfer the root ownershipt to the heapshot */
2249                                                 hs->num_roots = thread->num_roots;
2250                                                 hs->roots = thread->roots;
2251                                                 hs->roots_extra = thread->roots_extra;
2252                                                 hs->roots_types = thread->roots_types;
2253                                         } else {
2254                                                 free (thread->roots);
2255                                                 free (thread->roots_extra);
2256                                                 free (thread->roots_types);
2257                                         }
2258                                         thread->num_roots = 0;
2259                                         thread->size_roots = 0;
2260                                         thread->roots = NULL;
2261                                         thread->roots_extra = NULL;
2262                                         thread->roots_types = NULL;
2263                                         heap_shot_resolve_reverse_refs (hs);
2264                                         heap_shot_mark_objects (hs);
2265                                         heap_shot_free_objects (hs);
2266                                 }
2267                                 thread->current_heap_shot = NULL;
2268                         } else if (subtype == TYPE_HEAP_START) {
2269                                 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2270                                 LOG_TIME (time_base, tdiff);
2271                                 time_base += tdiff;
2272                                 if (debug)
2273                                         fprintf (outfile, "heap shot start\n");
2274                                 thread->current_heap_shot = new_heap_shot (time_base);
2275                         }
2276                         break;
2277                 }
2278                 case TYPE_MONITOR: {
2279                         int event = (*p >> 4) & 0x3;
2280                         int has_bt = *p & TYPE_MONITOR_BT;
2281                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2282                         intptr_t objdiff = decode_sleb128 (p, &p);
2283                         MethodDesc* sframes [8];
2284                         MethodDesc** frames = sframes;
2285                         int record;
2286                         int num_bt = 0;
2287                         LOG_TIME (time_base, tdiff);
2288                         time_base += tdiff;
2289                         record = (!thread_filter || thread_filter == thread->thread_id);
2290                         if (!(time_base >= time_from && time_base < time_to))
2291                                 record = 0;
2292                         if (event == MONO_PROFILER_MONITOR_CONTENTION) {
2293                                 MonitorDesc *mdesc = lookup_monitor (OBJ_ADDR (objdiff));
2294                                 if (record) {
2295                                         monitor_contention++;
2296                                         mdesc->contentions++;
2297                                         thread->monitor = mdesc;
2298                                         thread->contention_start = time_base;
2299                                 }
2300                                 if (has_bt) {
2301                                         num_bt = 8;
2302                                         frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
2303                                         if (!frames) {
2304                                                 fprintf (outfile, "Cannot load backtrace\n");
2305                                                 return 0;
2306                                         }
2307                                         if (record)
2308                                                 add_trace_methods (frames, num_bt, &mdesc->traces, 1);
2309                                 } else {
2310                                         if (record)
2311                                                 add_trace_thread (thread, &mdesc->traces, 1);
2312                                 }
2313                         } else if (event == MONO_PROFILER_MONITOR_FAIL) {
2314                                 if (record) {
2315                                         monitor_failed++;
2316                                         if (thread->monitor && thread->contention_start) {
2317                                                 uint64_t wait_time = time_base - thread->contention_start;
2318                                                 if (wait_time > thread->monitor->max_wait_time)
2319                                                         thread->monitor->max_wait_time = wait_time;
2320                                                 thread->monitor->wait_time += wait_time;
2321                                                 thread->monitor = NULL;
2322                                                 thread->contention_start = 0;
2323                                         }
2324                                 }
2325                         } else if (event == MONO_PROFILER_MONITOR_DONE) {
2326                                 if (record) {
2327                                         monitor_acquired++;
2328                                         if (thread->monitor && thread->contention_start) {
2329                                                 uint64_t wait_time = time_base - thread->contention_start;
2330                                                 if (wait_time > thread->monitor->max_wait_time)
2331                                                         thread->monitor->max_wait_time = wait_time;
2332                                                 thread->monitor->wait_time += wait_time;
2333                                                 thread->monitor = NULL;
2334                                                 thread->contention_start = 0;
2335                                         }
2336                                 }
2337                         }
2338                         if (debug)
2339                                 fprintf (outfile, "monitor %s for object %p\n", monitor_ev_name (event), (void*)OBJ_ADDR (objdiff));
2340                         if (frames != sframes)
2341                                 free (frames);
2342                         break;
2343                 }
2344                 case TYPE_EXCEPTION: {
2345                         int subtype = *p & 0x70;
2346                         int has_bt = *p & TYPE_EXCEPTION_BT;
2347                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2348                         MethodDesc* sframes [8];
2349                         MethodDesc** frames = sframes;
2350                         int record;
2351                         LOG_TIME (time_base, tdiff);
2352                         time_base += tdiff;
2353                         record = (!thread_filter || thread_filter == thread->thread_id);
2354                         if (!(time_base >= time_from && time_base < time_to))
2355                                 record = 0;
2356                         if (subtype == TYPE_CLAUSE) {
2357                                 int clause_type = decode_uleb128 (p, &p);
2358                                 int clause_num = decode_uleb128 (p, &p);
2359                                 int64_t ptrdiff = decode_sleb128 (p, &p);
2360                                 method_base += ptrdiff;
2361                                 if (record)
2362                                         clause_summary [clause_type]++;
2363                                 if (debug)
2364                                         fprintf (outfile, "clause %s (%d) in method %s\n", clause_name (clause_type), clause_num, lookup_method (method_base)->name);
2365                         } else {
2366                                 intptr_t objdiff = decode_sleb128 (p, &p);
2367                                 if (record)
2368                                         throw_count++;
2369                                 if (has_bt) {
2370                                         has_bt = 8;
2371                                         frames = decode_bt (sframes, &has_bt, p, &p, ptr_base);
2372                                         if (!frames) {
2373                                                 fprintf (outfile, "Cannot load backtrace\n");
2374                                                 return 0;
2375                                         }
2376                                         if (record)
2377                                                 add_trace_methods (frames, has_bt, &exc_traces, 1);
2378                                 } else {
2379                                         if (record)
2380                                                 add_trace_thread (thread, &exc_traces, 1);
2381                                 }
2382                                 if (frames != sframes)
2383                                         free (frames);
2384                                 if (debug)
2385                                         fprintf (outfile, "throw %p\n", (void*)OBJ_ADDR (objdiff));
2386                         }
2387                         break;
2388                 }
2389                 case TYPE_RUNTIME: {
2390                         int subtype = *p & 0xf0;
2391                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2392                         LOG_TIME (time_base, tdiff);
2393                         time_base += tdiff;
2394                         if (subtype == TYPE_JITHELPER) {
2395                                 int type = decode_uleb128 (p, &p);
2396                                 intptr_t codediff = decode_sleb128 (p, &p);
2397                                 int codelen = decode_uleb128 (p, &p);
2398                                 const char *name;
2399                                 if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
2400                                         name = (void*)p;
2401                                         while (*p) p++;
2402                                                 p++;
2403                                 } else {
2404                                         name = code_buffer_desc (type);
2405                                 }
2406                                 num_jit_helpers++;
2407                                 jit_helpers_code_size += codelen;
2408                                 if (debug)
2409                                         fprintf (outfile, "jit helper %s, size: %d, code: %p\n", name, codelen, (void*)(ptr_base + codediff));
2410                         }
2411                         break;
2412                 }
2413                 case TYPE_SAMPLE: {
2414                         int subtype = *p & 0xf0;
2415                         if (subtype == TYPE_SAMPLE_HIT) {
2416                                 int i;
2417                                 int sample_type = decode_uleb128 (p + 1, &p);
2418                                 uint64_t tstamp = decode_uleb128 (p, &p);
2419                                 int count = decode_uleb128 (p, &p);
2420                                 for (i = 0; i < count; ++i) {
2421                                         uintptr_t ip = ptr_base + decode_sleb128 (p, &p);
2422                                         if ((tstamp >= time_from && tstamp < time_to))
2423                                                 add_stat_sample (sample_type, ip);
2424                                         if (debug)
2425                                                 fprintf (outfile, "sample hit, type: %d at %p\n", sample_type, (void*)ip);
2426                                 }
2427                                 if (ctx->data_version > 5) {
2428                                         count = decode_uleb128 (p, &p);
2429                                         for (i = 0; i < count; ++i) {
2430                                                 MethodDesc *method;
2431                                                 int64_t ptrdiff = decode_sleb128 (p, &p);
2432                                                 int il_offset = decode_sleb128 (p, &p);
2433                                                 int native_offset = decode_sleb128 (p, &p);
2434                                                 method_base += ptrdiff;
2435                                                 method = lookup_method (method_base);
2436                                                 if (debug)
2437                                                         fprintf (outfile, "sample hit bt %d: %s at IL offset %d (native: %d)\n", i, method->name, il_offset, native_offset);
2438                                         }
2439                                 }
2440                         } else if (subtype == TYPE_SAMPLE_USYM) {
2441                                 /* un unmanaged symbol description */
2442                                 uintptr_t addr = ptr_base + decode_sleb128 (p + 1, &p);
2443                                 uintptr_t size = decode_uleb128 (p, &p);
2444                                 char *name;
2445                                 name = pstrdup ((char*)p);
2446                                 add_unmanaged_symbol (addr, name, size);
2447                                 if (debug)
2448                                         fprintf (outfile, "unmanaged symbol %s at %p\n", name, (void*)addr);
2449                                 while (*p) p++;
2450                                 p++;
2451                         } else if (subtype == TYPE_SAMPLE_UBIN) {
2452                                 /* un unmanaged binary loaded in memory */
2453                                 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2454                                 uintptr_t addr = decode_sleb128 (p, &p);
2455                                 uint64_t offset G_GNUC_UNUSED = decode_uleb128 (p, &p);
2456                                 uintptr_t size = decode_uleb128 (p, &p);
2457                                 char *name;
2458                                 LOG_TIME (time_base, tdiff);
2459                                 time_base += tdiff;
2460                                 name = pstrdup ((char*)p);
2461                                 add_unmanaged_binary (addr, name, size);
2462                                 if (debug)
2463                                         fprintf (outfile, "unmanaged binary %s at %p\n", name, (void*)addr);
2464                                 while (*p) p++;
2465                                 p++;
2466                         } else if (subtype == TYPE_SAMPLE_COUNTERS_DESC) {
2467                                 uint64_t i, len = decode_uleb128 (p + 1, &p);
2468                                 for (i = 0; i < len; i++) {
2469                                         uint64_t type, unit, variance, index;
2470                                         uint64_t section = decode_uleb128 (p, &p);
2471                                         char *section_str, *name;
2472                                         if (section != MONO_COUNTER_PERFCOUNTERS) {
2473                                                 section_str = (char*) section_name (section);
2474                                         } else {
2475                                                 section_str = pstrdup ((char*)p);
2476                                                 while (*p++);
2477                                         }
2478                                         name = pstrdup ((char*)p);
2479                                         while (*p++);
2480                                         type = decode_uleb128 (p, &p);
2481                                         unit = decode_uleb128 (p, &p);
2482                                         variance = decode_uleb128 (p, &p);
2483                                         index = decode_uleb128 (p, &p);
2484                                         add_counter (section_str, name, (int)type, (int)unit, (int)variance, (int)index);
2485                                 }
2486                         } else if (subtype == TYPE_SAMPLE_COUNTERS) {
2487                                 int i;
2488                                 CounterValue *value, *previous = NULL;
2489                                 CounterList *list;
2490                                 uint64_t timestamp = decode_uleb128 (p + 1, &p);
2491                                 uint64_t time_between = timestamp / 1000 * 1000 * 1000 * 1000 + startup_time;
2492                                 while (1) {
2493                                         uint64_t type, index = decode_uleb128 (p, &p);
2494                                         if (index == 0)
2495                                                 break;
2496
2497                                         for (list = counters; list; list = list->next) {
2498                                                 if (list->counter->index == (int)index) {
2499                                                         previous = list->counter->values_last;
2500                                                         break;
2501                                                 }
2502                                         }
2503
2504                                         type = decode_uleb128 (p, &p);
2505
2506                                         value = calloc (1, sizeof (CounterValue));
2507                                         value->timestamp = timestamp;
2508
2509                                         switch (type) {
2510                                         case MONO_COUNTER_INT:
2511 #if SIZEOF_VOID_P == 4
2512                                         case MONO_COUNTER_WORD:
2513 #endif
2514                                                 value->buffer = malloc (sizeof (int32_t));
2515                                                 *(int32_t*)value->buffer = (int32_t)decode_sleb128 (p, &p) + (previous ? (*(int32_t*)previous->buffer) : 0);
2516                                                 break;
2517                                         case MONO_COUNTER_UINT:
2518                                                 value->buffer = malloc (sizeof (uint32_t));
2519                                                 *(uint32_t*)value->buffer = (uint32_t)decode_uleb128 (p, &p) + (previous ? (*(uint32_t*)previous->buffer) : 0);
2520                                                 break;
2521                                         case MONO_COUNTER_LONG:
2522 #if SIZEOF_VOID_P == 8
2523                                         case MONO_COUNTER_WORD:
2524 #endif
2525                                         case MONO_COUNTER_TIME_INTERVAL:
2526                                                 value->buffer = malloc (sizeof (int64_t));
2527                                                 *(int64_t*)value->buffer = (int64_t)decode_sleb128 (p, &p) + (previous ? (*(int64_t*)previous->buffer) : 0);
2528                                                 break;
2529                                         case MONO_COUNTER_ULONG:
2530                                                 value->buffer = malloc (sizeof (uint64_t));
2531                                                 *(uint64_t*)value->buffer = (uint64_t)decode_uleb128 (p, &p) + (previous ? (*(uint64_t*)previous->buffer) : 0);
2532                                                 break;
2533                                         case MONO_COUNTER_DOUBLE:
2534                                                 value->buffer = malloc (sizeof (double));
2535 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
2536                                                 for (i = 0; i < sizeof (double); i++)
2537 #else
2538                                                 for (i = sizeof (double) - 1; i >= 0; i--)
2539 #endif
2540                                                         value->buffer[i] = *p++;
2541                                                 break;
2542                                         case MONO_COUNTER_STRING:
2543                                                 if (*p++ == 0) {
2544                                                         value->buffer = NULL;
2545                                                 } else {
2546                                                         value->buffer = (unsigned char*) pstrdup ((char*)p);
2547                                                         while (*p++);
2548                                                 }
2549                                                 break;
2550                                         }
2551                                         if (time_between >= time_from && time_between <= time_to)
2552                                                 add_counter_value (index, value);
2553                                 }
2554                         } else {
2555                                 return 0;
2556                         }
2557                         break;
2558                 }
2559                 default:
2560                         fprintf (outfile, "unhandled profiler event: 0x%x at file offset: %llu + %lld (len: %d\n)\n", *p, (unsigned long long) file_offset, (long long) (p - ctx->buf), len);
2561                         exit (1);
2562                 }
2563         }
2564         thread->last_time = time_base;
2565         for (i = 0; i < thread->stack_id; ++i)
2566                 thread->stack [i]->recurse_count = 0;
2567         return 1;
2568 }
2569
2570 static ProfContext*
2571 load_file (char *name)
2572 {
2573         unsigned char *p;
2574         ProfContext *ctx = calloc (sizeof (ProfContext), 1);
2575         if (strcmp (name, "-") == 0)
2576                 ctx->file = stdin;
2577         else
2578                 ctx->file = fopen (name, "rb");
2579         if (!ctx->file) {
2580                 printf ("Cannot open file: %s\n", name);
2581                 exit (1);
2582         }
2583 #if defined (HAVE_SYS_ZLIB)
2584         if (ctx->file != stdin)
2585                 ctx->gzfile = gzdopen (fileno (ctx->file), "rb");
2586 #endif
2587         if (!load_data (ctx, 32))
2588                 return NULL;
2589         p = ctx->buf;
2590         if (read_int32 (p) != LOG_HEADER_ID || p [6] > LOG_DATA_VERSION)
2591                 return NULL;
2592         ctx->version_major = p [4];
2593         ctx->version_minor = p [5];
2594         ctx->data_version = p [6];
2595         /* reading 64 bit files on 32 bit systems not supported yet */
2596         if (p [7] > sizeof (void*))
2597                 return NULL;
2598         if (read_int32 (p + 20)) /* flags must be 0 */
2599                 return NULL;
2600         ctx->startup_time = read_int64 (p + 8);
2601         ctx->timer_overhead = read_int32 (p + 16);
2602         ctx->pid = read_int32 (p + 24);
2603         ctx->port = read_int16 (p + 28);
2604         return ctx;
2605 }
2606
2607 enum {
2608         ALLOC_SORT_BYTES,
2609         ALLOC_SORT_COUNT
2610 };
2611 static int alloc_sort_mode = ALLOC_SORT_BYTES;
2612
2613 static int
2614 compare_class (const void *a, const void *b)
2615 {
2616         ClassDesc *const*A = a;
2617         ClassDesc *const*B = b;
2618         uint64_t vala, valb;
2619         if (alloc_sort_mode == ALLOC_SORT_BYTES) {
2620                 vala = (*A)->alloc_size;
2621                 valb = (*B)->alloc_size;
2622         } else {
2623                 vala = (*A)->allocs;
2624                 valb = (*B)->allocs;
2625         }
2626         if (valb == vala)
2627                 return 0;
2628         if (valb < vala)
2629                 return -1;
2630         return 1;
2631 }
2632
2633 static void
2634 dump_header (ProfContext *ctx)
2635 {
2636         time_t st = ctx->startup_time / 1000;
2637         char *t = ctime (&st);
2638         fprintf (outfile, "\nMono log profiler data\n");
2639         fprintf (outfile, "\tProfiler version: %d.%d\n", ctx->version_major, ctx->version_minor);
2640         fprintf (outfile, "\tData version: %d\n", ctx->data_version);
2641         fprintf (outfile, "\tMean timer overhead: %d nanoseconds\n", ctx->timer_overhead);
2642         fprintf (outfile, "\tProgram startup: %s", t);
2643         if (ctx->pid)
2644                 fprintf (outfile, "\tProgram ID: %d\n", ctx->pid);
2645         if (ctx->port)
2646                 fprintf (outfile, "\tServer listening on: %d\n", ctx->port);
2647 }
2648
2649 static void
2650 dump_traces (TraceDesc *traces, const char *desc)
2651 {
2652         int j;
2653         if (!show_traces)
2654                 return;
2655         if (!traces->count)
2656                 return;
2657         sort_context_array (traces);
2658         for (j = 0; j < traces->count; ++j) {
2659                 int k;
2660                 BackTrace *bt;
2661                 bt = traces->traces [j].bt;
2662                 if (!bt->count)
2663                         continue;
2664                 fprintf (outfile, "\t%llu %s from:\n", (unsigned long long) traces->traces [j].count, desc);
2665                 for (k = 0; k < bt->count; ++k)
2666                         fprintf (outfile, "\t\t%s\n", bt->methods [k]->name);
2667         }
2668 }
2669
2670 static void
2671 dump_threads (ProfContext *ctx)
2672 {
2673         ThreadContext *thread;
2674         fprintf (outfile, "\nThread summary\n");
2675         for (thread = ctx->threads; thread; thread = thread->next) {
2676                 fprintf (outfile, "\tThread: %p, name: \"%s\"\n", (void*)thread->thread_id, thread->name? thread->name: "");
2677         }
2678 }
2679
2680 static void
2681 dump_exceptions (void)
2682 {
2683         int i;
2684         fprintf (outfile, "\nException summary\n");
2685         fprintf (outfile, "\tThrows: %llu\n", (unsigned long long) throw_count);
2686         dump_traces (&exc_traces, "throws");
2687         for (i = 0; i <= MONO_EXCEPTION_CLAUSE_FAULT; ++i) {
2688                 if (!clause_summary [i])
2689                         continue;
2690                 fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), (unsigned long long) clause_summary [i]);
2691         }
2692 }
2693
2694 static int
2695 compare_monitor (const void *a, const void *b)
2696 {
2697         MonitorDesc *const*A = a;
2698         MonitorDesc *const*B = b;
2699         if ((*B)->wait_time == (*A)->wait_time)
2700                 return 0;
2701         if ((*B)->wait_time < (*A)->wait_time)
2702                 return -1;
2703         return 1;
2704 }
2705
2706 static void
2707 dump_monitors (void)
2708 {
2709         MonitorDesc **monitors;
2710         int i, j;
2711         if (!num_monitors)
2712                 return;
2713         monitors = malloc (sizeof (void*) * num_monitors);
2714         for (i = 0, j = 0; i < SMALL_HASH_SIZE; ++i) {
2715                 MonitorDesc *mdesc = monitor_hash [i];
2716                 while (mdesc) {
2717                         monitors [j++] = mdesc;
2718                         mdesc = mdesc->next;
2719                 }
2720         }
2721         qsort (monitors, num_monitors, sizeof (void*), compare_monitor);
2722         fprintf (outfile, "\nMonitor lock summary\n");
2723         for (i = 0; i < num_monitors; ++i) {
2724                 MonitorDesc *mdesc = monitors [i];
2725                 fprintf (outfile, "\tLock object %p: %d contentions\n", (void*)mdesc->objid, (int)mdesc->contentions);
2726                 fprintf (outfile, "\t\t%.6f secs total wait time, %.6f max, %.6f average\n",
2727                         mdesc->wait_time/1000000000.0, mdesc->max_wait_time/1000000000.0, mdesc->wait_time/1000000000.0/mdesc->contentions);
2728                 dump_traces (&mdesc->traces, "contentions");
2729         }
2730         fprintf (outfile, "\tLock contentions: %llu\n", (unsigned long long) monitor_contention);
2731         fprintf (outfile, "\tLock acquired: %llu\n", (unsigned long long) monitor_acquired);
2732         fprintf (outfile, "\tLock failures: %llu\n", (unsigned long long) monitor_failed);
2733 }
2734
2735 static void
2736 dump_gcs (void)
2737 {
2738         int i;
2739         fprintf (outfile, "\nGC summary\n");
2740         fprintf (outfile, "\tGC resizes: %d\n", gc_resizes);
2741         fprintf (outfile, "\tMax heap size: %llu\n", (unsigned long long) max_heap_size);
2742         fprintf (outfile, "\tObject moves: %llu\n", (unsigned long long) gc_object_moves);
2743         for (i = 0; i < 3; ++i) {
2744                 if (!gc_info [i].count)
2745                         continue;
2746                 fprintf (outfile, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
2747                         i, gc_info [i].count,
2748                         (unsigned long long) (gc_info [i].max_time / 1000),
2749                         (unsigned long long) (gc_info [i].total_time / 1000),
2750                         (unsigned long long) (gc_info [i].total_time / gc_info [i].count / 1000));
2751         }
2752         for (i = 0; i < 3; ++i) {
2753                 if (!handle_info [i].max_live)
2754                         continue;
2755                 fprintf (outfile, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
2756                         get_handle_name (i),
2757                         (unsigned long long) (handle_info [i].created),
2758                         (unsigned long long) (handle_info [i].destroyed),
2759                         (unsigned long long) (handle_info [i].max_live));
2760                 dump_traces (&handle_info [i].traces, "created");
2761         }
2762 }
2763
2764 static void
2765 dump_jit (void)
2766 {
2767         int i;
2768         int code_size = 0;
2769         int compiled_methods = 0;
2770         MethodDesc* m;
2771         fprintf (outfile, "\nJIT summary\n");
2772         for (i = 0; i < HASH_SIZE; ++i) {
2773                 m = method_hash [i];
2774                 for (m = method_hash [i]; m; m = m->next) {
2775                         if (!m->code || m->ignore_jit)
2776                                 continue;
2777                         compiled_methods++;
2778                         code_size += m->len;
2779                 }
2780         }
2781         fprintf (outfile, "\tCompiled methods: %d\n", compiled_methods);
2782         fprintf (outfile, "\tGenerated code size: %d\n", code_size);
2783         fprintf (outfile, "\tJIT helpers: %d\n", num_jit_helpers);
2784         fprintf (outfile, "\tJIT helpers code size: %d\n", jit_helpers_code_size);
2785 }
2786
2787 static void
2788 dump_allocations (void)
2789 {
2790         int i, c;
2791         intptr_t allocs = 0;
2792         uint64_t size = 0;
2793         int header_done = 0;
2794         ClassDesc **classes = malloc (num_classes * sizeof (void*));
2795         ClassDesc *cd;
2796         c = 0;
2797         for (i = 0; i < HASH_SIZE; ++i) {
2798                 cd = class_hash [i];
2799                 while (cd) {
2800                         classes [c++] = cd;
2801                         cd = cd->next;
2802                 }
2803         }
2804         qsort (classes, num_classes, sizeof (void*), compare_class);
2805         for (i = 0; i < num_classes; ++i) {
2806                 cd = classes [i];
2807                 if (!cd->allocs)
2808                         continue;
2809                 allocs += cd->allocs;
2810                 size += cd->alloc_size;
2811                 if (!header_done++) {
2812                         fprintf (outfile, "\nAllocation summary\n");
2813                         fprintf (outfile, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
2814                 }
2815                 fprintf (outfile, "%10llu %10zd %8llu %s\n",
2816                         (unsigned long long) (cd->alloc_size),
2817                         cd->allocs,
2818                         (unsigned long long) (cd->alloc_size / cd->allocs),
2819                         cd->name);
2820                 dump_traces (&cd->traces, "bytes");
2821         }
2822         if (allocs)
2823                 fprintf (outfile, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size, allocs);
2824 }
2825
2826 enum {
2827         METHOD_SORT_TOTAL,
2828         METHOD_SORT_SELF,
2829         METHOD_SORT_CALLS
2830 };
2831
2832 static int method_sort_mode = METHOD_SORT_TOTAL;
2833
2834 static int
2835 compare_method (const void *a, const void *b)
2836 {
2837         MethodDesc *const*A = a;
2838         MethodDesc *const*B = b;
2839         uint64_t vala, valb;
2840         if (method_sort_mode == METHOD_SORT_SELF) {
2841                 vala = (*A)->self_time;
2842                 valb = (*B)->self_time;
2843         } else if (method_sort_mode == METHOD_SORT_CALLS) {
2844                 vala = (*A)->calls;
2845                 valb = (*B)->calls;
2846         } else {
2847                 vala = (*A)->total_time;
2848                 valb = (*B)->total_time;
2849         }
2850         if (vala == valb)
2851                 return 0;
2852         if (valb < vala)
2853                 return -1;
2854         return 1;
2855 }
2856
2857 static void
2858 dump_metadata (void)
2859 {
2860         fprintf (outfile, "\nMetadata summary\n");
2861         fprintf (outfile, "\tLoaded images: %d\n", num_images);
2862         if (verbose) {
2863                 ImageDesc *image;
2864                 int i;
2865                 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
2866                         image = image_hash [i];
2867                         while (image) {
2868                                 fprintf (outfile, "\t\t%s\n", image->filename);
2869                                 image = image->next;
2870                         }
2871                 }
2872         }
2873
2874 }
2875
2876 static void
2877 dump_methods (void)
2878 {
2879         int i, c;
2880         uint64_t calls = 0;
2881         int header_done = 0;
2882         MethodDesc **methods = malloc (num_methods * sizeof (void*));
2883         MethodDesc *cd;
2884         c = 0;
2885         for (i = 0; i < HASH_SIZE; ++i) {
2886                 cd = method_hash [i];
2887                 while (cd) {
2888                         cd->total_time = cd->self_time + cd->callee_time;
2889                         methods [c++] = cd;
2890                         cd = cd->next;
2891                 }
2892         }
2893         qsort (methods, num_methods, sizeof (void*), compare_method);
2894         for (i = 0; i < num_methods; ++i) {
2895                 uint64_t msecs;
2896                 uint64_t smsecs;
2897                 cd = methods [i];
2898                 if (!cd->calls)
2899                         continue;
2900                 calls += cd->calls;
2901                 msecs = cd->total_time / 1000000;
2902                 smsecs = (cd->total_time - cd->callee_time) / 1000000;
2903                 if (!msecs && !verbose)
2904                         continue;
2905                 if (!header_done++) {
2906                         fprintf (outfile, "\nMethod call summary\n");
2907                         fprintf (outfile, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
2908                 }
2909                 fprintf (outfile, "%8llu %8llu %10llu %s\n",
2910                         (unsigned long long) (msecs),
2911                         (unsigned long long) (smsecs),
2912                         (unsigned long long) (cd->calls),
2913                         cd->name);
2914                 dump_traces (&cd->traces, "calls");
2915         }
2916         if (calls)
2917                 fprintf (outfile, "Total calls: %llu\n", (unsigned long long) calls);
2918 }
2919
2920 static int
2921 compare_heap_class (const void *a, const void *b)
2922 {
2923         HeapClassDesc *const*A = a;
2924         HeapClassDesc *const*B = b;
2925         uint64_t vala, valb;
2926         if (alloc_sort_mode == ALLOC_SORT_BYTES) {
2927                 vala = (*A)->total_size;
2928                 valb = (*B)->total_size;
2929         } else {
2930                 vala = (*A)->count;
2931                 valb = (*B)->count;
2932         }
2933         if (valb == vala)
2934                 return 0;
2935         if (valb < vala)
2936                 return -1;
2937         return 1;
2938 }
2939
2940 static int
2941 compare_rev_class (const void *a, const void *b)
2942 {
2943         const HeapClassRevRef *A = a;
2944         const HeapClassRevRef *B = b;
2945         if (B->count == A->count)
2946                 return 0;
2947         if (B->count < A->count)
2948                 return -1;
2949         return 1;
2950 }
2951
2952 static void
2953 dump_rev_claases (HeapClassRevRef *revs, int count)
2954 {
2955         int j;
2956         if (!show_traces)
2957                 return;
2958         if (!count)
2959                 return;
2960         for (j = 0; j < count; ++j) {
2961                 HeapClassDesc *cd = revs [j].klass;
2962                 fprintf (outfile, "\t\t%llu references from: %s\n",
2963                         (unsigned long long) (revs [j].count),
2964                         cd->klass->name);
2965         }
2966 }
2967
2968 static void
2969 heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
2970 {
2971         uint64_t size = 0;
2972         uint64_t count = 0;
2973         int ccount = 0;
2974         int i;
2975         HeapClassDesc *cd;
2976         HeapClassDesc **sorted;
2977         sorted = malloc (sizeof (void*) * hs->class_count);
2978         for (i = 0; i < hs->hash_size; ++i) {
2979                 cd = hs->class_hash [i];
2980                 if (!cd)
2981                         continue;
2982                 count += cd->count;
2983                 size += cd->total_size;
2984                 sorted [ccount++] = cd;
2985         }
2986         hs->sorted = sorted;
2987         qsort (sorted, ccount, sizeof (void*), compare_heap_class);
2988         fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %zd\n",
2989                 hs_num,
2990                 (hs->timestamp - startup_time)/1000000000.0,
2991                 (unsigned long long) (size),
2992                 (unsigned long long) (count),
2993                 ccount, hs->num_roots);
2994         if (!verbose && ccount > 30)
2995                 ccount = 30;
2996         fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
2997         for (i = 0; i < ccount; ++i) {
2998                 HeapClassRevRef *rev_sorted;
2999                 int j, k;
3000                 HeapClassDesc *ocd = NULL;
3001                 cd = sorted [i];
3002                 if (last_hs)
3003                         ocd = heap_class_lookup (last_hs, cd->klass);
3004                 fprintf (outfile, "\t%10llu %10llu %8llu %s",
3005                         (unsigned long long) (cd->total_size),
3006                         (unsigned long long) (cd->count),
3007                         (unsigned long long) (cd->total_size / cd->count),
3008                         cd->klass->name);
3009                 if (ocd) {
3010                         int64_t bdiff = cd->total_size - ocd->total_size;
3011                         int64_t cdiff = cd->count - ocd->count;
3012                         fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", (long long) bdiff, (long long) cdiff);
3013                 } else {
3014                         fprintf (outfile, "\n");
3015                 }
3016                 if (!collect_traces)
3017                         continue;
3018                 rev_sorted = malloc (cd->rev_count * sizeof (HeapClassRevRef));
3019                 k = 0;
3020                 for (j = 0; j < cd->rev_hash_size; ++j) {
3021                         if (cd->rev_hash [j].klass)
3022                                 rev_sorted [k++] = cd->rev_hash [j];
3023                 }
3024                 assert (cd->rev_count == k);
3025                 qsort (rev_sorted, cd->rev_count, sizeof (HeapClassRevRef), compare_rev_class);
3026                 if (cd->root_references)
3027                         fprintf (outfile, "\t\t%zd root references (%zd pinning)\n", cd->root_references, cd->pinned_references);
3028                 dump_rev_claases (rev_sorted, cd->rev_count);
3029                 free (rev_sorted);
3030         }
3031         free (sorted);
3032 }
3033
3034 static int
3035 compare_heap_shots (const void *a, const void *b)
3036 {
3037         HeapShot *const*A = a;
3038         HeapShot *const*B = b;
3039         if ((*B)->timestamp == (*A)->timestamp)
3040                 return 0;
3041         if ((*B)->timestamp > (*A)->timestamp)
3042                 return -1;
3043         return 1;
3044 }
3045
3046 static void
3047 dump_heap_shots (void)
3048 {
3049         HeapShot **hs_sorted;
3050         HeapShot *hs;
3051         HeapShot *last_hs = NULL;
3052         int i;
3053         if (!heap_shots)
3054                 return;
3055         hs_sorted = malloc (num_heap_shots * sizeof (void*));
3056         fprintf (outfile, "\nHeap shot summary\n");
3057         i = 0;
3058         for (hs = heap_shots; hs; hs = hs->next)
3059                 hs_sorted [i++] = hs;
3060         qsort (hs_sorted, num_heap_shots, sizeof (void*), compare_heap_shots);
3061         for (i = 0; i < num_heap_shots; ++i) {
3062                 hs = hs_sorted [i];
3063                 heap_shot_summary (hs, i, last_hs);
3064                 last_hs = hs;
3065         }
3066 }
3067
3068 static void
3069 flush_context (ProfContext *ctx)
3070 {
3071         ThreadContext *thread;
3072         /* FIXME: sometimes there are leftovers: indagate */
3073         for (thread = ctx->threads; thread; thread = thread->next) {
3074                 while (thread->stack_id) {
3075                         if (debug)
3076                                 fprintf (outfile, "thread %p has %d items on stack\n", (void*)thread->thread_id, thread->stack_id);
3077                         pop_method (thread, thread->stack [thread->stack_id - 1], thread->last_time);
3078                 }
3079         }
3080 }
3081
3082 static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,heapshot,counters";
3083
3084 static const char*
3085 match_option (const char *p, const char *opt)
3086 {
3087         int len = strlen (opt);
3088         if (strncmp (p, opt, len) == 0) {
3089                 if (p [len] == ',')
3090                         len++;
3091                 return p + len;
3092         }
3093         return p;
3094 }
3095
3096 static int
3097 print_reports (ProfContext *ctx, const char *reps, int parse_only)
3098 {
3099         const char *opt;
3100         const char *p;
3101         for (p = reps; *p; p = opt) {
3102                 if ((opt = match_option (p, "header")) != p) {
3103                         if (!parse_only)
3104                                 dump_header (ctx);
3105                         continue;
3106                 }
3107                 if ((opt = match_option (p, "thread")) != p) {
3108                         if (!parse_only)
3109                                 dump_threads (ctx);
3110                         continue;
3111                 }
3112                 if ((opt = match_option (p, "gc")) != p) {
3113                         if (!parse_only)
3114                                 dump_gcs ();
3115                         continue;
3116                 }
3117                 if ((opt = match_option (p, "jit")) != p) {
3118                         if (!parse_only)
3119                                 dump_jit ();
3120                         continue;
3121                 }
3122                 if ((opt = match_option (p, "alloc")) != p) {
3123                         if (!parse_only)
3124                                 dump_allocations ();
3125                         continue;
3126                 }
3127                 if ((opt = match_option (p, "call")) != p) {
3128                         if (!parse_only)
3129                                 dump_methods ();
3130                         continue;
3131                 }
3132                 if ((opt = match_option (p, "metadata")) != p) {
3133                         if (!parse_only)
3134                                 dump_metadata ();
3135                         continue;
3136                 }
3137                 if ((opt = match_option (p, "exception")) != p) {
3138                         if (!parse_only)
3139                                 dump_exceptions ();
3140                         continue;
3141                 }
3142                 if ((opt = match_option (p, "monitor")) != p) {
3143                         if (!parse_only)
3144                                 dump_monitors ();
3145                         continue;
3146                 }
3147                 if ((opt = match_option (p, "heapshot")) != p) {
3148                         if (!parse_only)
3149                                 dump_heap_shots ();
3150                         continue;
3151                 }
3152                 if ((opt = match_option (p, "sample")) != p) {
3153                         if (!parse_only)
3154                                 dump_samples ();
3155                         continue;
3156                 }
3157                 if ((opt = match_option (p, "counters")) != p) {
3158                         if (!parse_only)
3159                                 dump_counters ();
3160                         continue;
3161                 }
3162                 return 0;
3163         }
3164         return 1;
3165 }
3166
3167 static int
3168 add_find_spec (const char *p)
3169 {
3170         if (p [0] == 'S' && p [1] == ':') {
3171                 char *vale;
3172                 find_size = strtoul (p + 2, &vale, 10);
3173                 return 1;
3174         } else if (p [0] == 'T' && p [1] == ':') {
3175                 find_name = p + 2;
3176                 return 1;
3177         }
3178         return 0;
3179 }
3180
3181 static void
3182 usage (void)
3183 {
3184         printf ("Mono log profiler report version %d.%d\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR);
3185         printf ("Usage: mprof-report [OPTIONS] FILENAME\n");
3186         printf ("FILENAME can be '-' to read from standard input.\n");
3187         printf ("Options:\n");
3188         printf ("\t--help               display this help\n");
3189         printf ("\t--out=FILE           write to FILE instead of stdout\n");
3190         printf ("\t--traces             collect and show backtraces\n"); 
3191         printf ("\t--maxframes=NUM      limit backtraces to NUM entries\n");
3192         printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
3193         printf ("\t                     %s\n", reports);
3194         printf ("\t--method-sort=MODE   sort methods according to MODE: total, self, calls\n");
3195         printf ("\t--alloc-sort=MODE    sort allocations according to MODE: bytes, count\n");
3196         printf ("\t--counters-sort=MODE sort counters according to MODE: time, category\n");
3197         printf ("\t                     only accessible in verbose mode\n");
3198         printf ("\t--track=OB1[,OB2...] track what happens to objects OBJ1, O2 etc.\n");
3199         printf ("\t--find=FINDSPEC      find and track objects matching FINFSPEC, where FINDSPEC is:\n");
3200         printf ("\t                     S:minimum_size or T:partial_name\n");
3201         printf ("\t--thread=THREADID    consider just the data for thread THREADID\n");
3202         printf ("\t--time=FROM-TO       consider data FROM seconds from startup up to TO seconds\n");
3203         printf ("\t--verbose            increase verbosity level\n");
3204         printf ("\t--debug              display decoding debug info for mprof-report devs\n");
3205 }
3206
3207 int
3208 main (int argc, char *argv[])
3209 {
3210         ProfContext *ctx;
3211         int i;
3212         outfile = stdout;
3213         for (i = 1; i < argc; ++i) {
3214                 if (strcmp ("--debug", argv [i]) == 0) {
3215                         debug++;
3216                 } else if (strcmp ("--help", argv [i]) == 0) {
3217                         usage ();
3218                         return 0;
3219                 } else if (strncmp ("--alloc-sort=", argv [i], 13) == 0) {
3220                         const char *val = argv [i] + 13;
3221                         if (strcmp (val, "bytes") == 0) {
3222                                 alloc_sort_mode = ALLOC_SORT_BYTES;
3223                         } else if (strcmp (val, "count") == 0) {
3224                                 alloc_sort_mode = ALLOC_SORT_COUNT;
3225                         } else {
3226                                 usage ();
3227                                 return 1;
3228                         }
3229                 } else if (strncmp ("--method-sort=", argv [i], 14) == 0) {
3230                         const char *val = argv [i] + 14;
3231                         if (strcmp (val, "total") == 0) {
3232                                 method_sort_mode = METHOD_SORT_TOTAL;
3233                         } else if (strcmp (val, "self") == 0) {
3234                                 method_sort_mode = METHOD_SORT_SELF;
3235                         } else if (strcmp (val, "calls") == 0) {
3236                                 method_sort_mode = METHOD_SORT_CALLS;
3237                         } else {
3238                                 usage ();
3239                                 return 1;
3240                         }
3241                 } else if (strncmp ("--counters-sort=", argv [i], 16) == 0) {
3242                         const char *val = argv [i] + 16;
3243                         if (strcmp (val, "time") == 0) {
3244                                 counters_sort_mode = COUNTERS_SORT_TIME;
3245                         } else if (strcmp (val, "category") == 0) {
3246                                 counters_sort_mode = COUNTERS_SORT_CATEGORY;
3247                         } else {
3248                                 usage ();
3249                                 return 1;
3250                         }
3251                 } else if (strncmp ("--reports=", argv [i], 10) == 0) {
3252                         const char *val = argv [i] + 10;
3253                         if (!print_reports (NULL, val, 1)) {
3254                                 usage ();
3255                                 return 1;
3256                         }
3257                         reports = val;
3258                 } else if (strncmp ("--out=", argv [i], 6) == 0) {
3259                         const char *val = argv [i] + 6;
3260                         outfile = fopen (val, "w");
3261                         if (!outfile) {
3262                                 printf ("Cannot open output file: %s\n", val);
3263                                 return 1;
3264                         }
3265                 } else if (strncmp ("--maxframes=", argv [i], 12) == 0) {
3266                         const char *val = argv [i] + 12;
3267                         char *vale;
3268                         trace_max = strtoul (val, &vale, 10);
3269                 } else if (strncmp ("--find=", argv [i], 7) == 0) {
3270                         const char *val = argv [i] + 7;
3271                         if (!add_find_spec (val)) {
3272                                 usage ();
3273                                 return 1;
3274                         }
3275                 } else if (strncmp ("--track=", argv [i], 8) == 0) {
3276                         const char *val = argv [i] + 8;
3277                         char *vale;
3278                         while (*val) {
3279                                 uintptr_t tracked_obj;
3280                                 if (*val == ',') {
3281                                         val++;
3282                                         continue;
3283                                 }
3284                                 tracked_obj = strtoul (val, &vale, 0);
3285                                 found_object (tracked_obj);
3286                                 val = vale;
3287                         }
3288                 } else if (strncmp ("--thread=", argv [i], 9) == 0) {
3289                         const char *val = argv [i] + 9;
3290                         char *vale;
3291                         thread_filter = strtoul (val, &vale, 0);
3292                 } else if (strncmp ("--time=", argv [i], 7) == 0) {
3293                         char *val = pstrdup (argv [i] + 7);
3294                         double from_secs, to_secs;
3295                         char *top = strchr (val, '-');
3296                         if (!top) {
3297                                 usage ();
3298                                 return 1;
3299                         }
3300                         *top++ = 0;
3301                         from_secs = atof (val);
3302                         to_secs = atof (top);
3303                         free (val);
3304                         if (from_secs > to_secs) {
3305                                 usage ();
3306                                 return 1;
3307                         }
3308                         time_from = from_secs * 1000000000;
3309                         time_to = to_secs * 1000000000;
3310                         use_time_filter = 1;
3311                 } else if (strcmp ("--verbose", argv [i]) == 0) {
3312                         verbose++;
3313                 } else if (strcmp ("--traces", argv [i]) == 0) {
3314                         show_traces = 1;
3315                         collect_traces = 1;
3316                 } else {
3317                         break;
3318                 }
3319         }
3320         if (i >= argc) {
3321                 usage ();
3322                 return 2;
3323         }
3324         ctx = load_file (argv [i]);
3325         if (!ctx) {
3326                 printf ("Not a log profiler data file (or unsupported version).\n");
3327                 return 1;
3328         }
3329         while (decode_buffer (ctx));
3330         flush_context (ctx);
3331         if (num_tracked_objects)
3332                 return 0;
3333         print_reports (ctx, reports, 0);
3334         return 0;
3335 }
3336