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