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