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