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