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