Merge pull request #3481 from alexrp/master
[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         uint64_t startup_time;
1521         ThreadContext *threads;
1522         ThreadContext *current_thread;
1523         DomainContext *domains;
1524         DomainContext *current_domain;
1525         RemCtxContext *remctxs;
1526         RemCtxContext *current_remctx;
1527 } ProfContext;
1528
1529 struct _ThreadContext {
1530         ThreadContext *next;
1531         intptr_t thread_id;
1532         char *name;
1533         /* emulated stack */
1534         MethodDesc **stack;
1535         uint64_t *time_stack;
1536         uint64_t *callee_time_stack;
1537         uint64_t last_time;
1538         uint64_t contention_start;
1539         MonitorDesc *monitor;
1540         int stack_size;
1541         int stack_id;
1542         HeapShot *current_heap_shot;
1543         uintptr_t num_roots;
1544         uintptr_t size_roots;
1545         uintptr_t *roots;
1546         uintptr_t *roots_extra;
1547         int *roots_types;
1548         uint64_t gc_start_times [3];
1549 };
1550
1551 struct _DomainContext {
1552         DomainContext *next;
1553         intptr_t domain_id;
1554         const char *friendly_name;
1555 };
1556
1557 struct _RemCtxContext {
1558         RemCtxContext *next;
1559         intptr_t remctx_id;
1560         intptr_t domain_id;
1561 };
1562
1563 static void
1564 ensure_buffer (ProfContext *ctx, int size)
1565 {
1566         if (ctx->size < size) {
1567                 ctx->buf = (unsigned char *)realloc (ctx->buf, size);
1568                 ctx->size = size;
1569         }
1570 }
1571
1572 static int
1573 load_data (ProfContext *ctx, int size)
1574 {
1575         ensure_buffer (ctx, size);
1576 #if defined (HAVE_SYS_ZLIB)
1577         if (ctx->gzfile) {
1578                 int r = gzread (ctx->gzfile, ctx->buf, size);
1579                 if (r == 0)
1580                         return size == 0? 1: 0;
1581                 return r == size;
1582         } else
1583 #endif
1584         {
1585                 int r = fread (ctx->buf, size, 1, ctx->file);
1586                 if (r == 0)
1587                         return size == 0? 1: 0;
1588                 return r;
1589         }
1590 }
1591
1592 static ThreadContext*
1593 get_thread (ProfContext *ctx, intptr_t thread_id)
1594 {
1595         ThreadContext *thread;
1596         if (ctx->current_thread && ctx->current_thread->thread_id == thread_id)
1597                 return ctx->current_thread;
1598         thread = ctx->threads;
1599         while (thread) {
1600                 if (thread->thread_id == thread_id) {
1601                         return thread;
1602                 }
1603                 thread = thread->next;
1604         }
1605         thread = (ThreadContext *)calloc (sizeof (ThreadContext), 1);
1606         thread->next = ctx->threads;
1607         ctx->threads = thread;
1608         thread->thread_id = thread_id;
1609         thread->last_time = 0;
1610         thread->stack_id = 0;
1611         thread->stack_size = 32;
1612         thread->stack = (MethodDesc **)malloc (thread->stack_size * sizeof (void*));
1613         thread->time_stack = (uint64_t *)malloc (thread->stack_size * sizeof (uint64_t));
1614         thread->callee_time_stack = (uint64_t *)malloc (thread->stack_size * sizeof (uint64_t));
1615         return thread;
1616 }
1617
1618 static DomainContext *
1619 get_domain (ProfContext *ctx, intptr_t domain_id)
1620 {
1621         if (ctx->current_domain && ctx->current_domain->domain_id == domain_id)
1622                 return ctx->current_domain;
1623
1624         DomainContext *domain = ctx->domains;
1625
1626         while (domain) {
1627                 if (domain->domain_id == domain_id)
1628                         return domain;
1629
1630                 domain = domain->next;
1631         }
1632
1633         domain = (DomainContext *)calloc (sizeof (DomainContext), 1);
1634         domain->next = ctx->domains;
1635         ctx->domains = domain;
1636         domain->domain_id = domain_id;
1637
1638         return domain;
1639 }
1640
1641 static RemCtxContext *
1642 get_remctx (ProfContext *ctx, intptr_t remctx_id)
1643 {
1644         if (ctx->current_remctx && ctx->current_remctx->remctx_id == remctx_id)
1645                 return ctx->current_remctx;
1646
1647         RemCtxContext *remctx = ctx->remctxs;
1648
1649         while (remctx) {
1650                 if (remctx->remctx_id == remctx_id)
1651                         return remctx;
1652
1653                 remctx = remctx->next;
1654         }
1655
1656         remctx = (RemCtxContext *)calloc (sizeof (RemCtxContext), 1);
1657         remctx->next = ctx->remctxs;
1658         ctx->remctxs = remctx;
1659         remctx->remctx_id = remctx_id;
1660
1661         return remctx;
1662 }
1663
1664 static ThreadContext*
1665 load_thread (ProfContext *ctx, intptr_t thread_id)
1666 {
1667         ThreadContext *thread = get_thread (ctx, thread_id);
1668         ctx->current_thread = thread;
1669         return thread;
1670 }
1671
1672 static void
1673 ensure_thread_stack (ThreadContext *thread)
1674 {
1675         if (thread->stack_id == thread->stack_size) {
1676                 thread->stack_size *= 2;
1677                 thread->stack = (MethodDesc **)realloc (thread->stack, thread->stack_size * sizeof (void*));
1678                 thread->time_stack = (uint64_t *)realloc (thread->time_stack, thread->stack_size * sizeof (uint64_t));
1679                 thread->callee_time_stack = (uint64_t *)realloc (thread->callee_time_stack, thread->stack_size * sizeof (uint64_t));
1680         }
1681 }
1682
1683 static int
1684 add_trace_hashed (CallContext *traces, int size, BackTrace *bt, uint64_t value)
1685 {
1686         int i;
1687         unsigned int start_pos;
1688         start_pos = bt->hash % size;
1689         i = start_pos;
1690         do {
1691                 if (traces [i].bt == bt) {
1692                         traces [i].count += value;
1693                         return 0;
1694                 } else if (!traces [i].bt) {
1695                         traces [i].bt = bt;
1696                         traces [i].count += value;
1697                         return 1;
1698                 }
1699                 /* wrap around */
1700                 if (++i == size)
1701                         i = 0;
1702         } while (i != start_pos);
1703         /* should not happen */
1704         printf ("failed trace store\n");
1705         return 0;
1706 }
1707
1708 static void
1709 add_trace_bt (BackTrace *bt, TraceDesc *trace, uint64_t value)
1710 {
1711         int i;
1712         if (!collect_traces)
1713                 return;
1714         if (trace->count * 2 >= trace->size) {
1715                 CallContext *n;
1716                 int old_size = trace->size;
1717                 trace->size *= 2;
1718                 if (trace->size == 0)
1719                         trace->size = 4;
1720                 n = (CallContext *)calloc (sizeof (CallContext) * trace->size, 1);
1721                 for (i = 0; i < old_size; ++i) {
1722                         if (trace->traces [i].bt)
1723                                 add_trace_hashed (n, trace->size, trace->traces [i].bt, trace->traces [i].count);
1724                 }
1725                 if (trace->traces)
1726                         free (trace->traces);
1727                 trace->traces = n;
1728         }
1729         trace->count += add_trace_hashed (trace->traces, trace->size, bt, value);
1730 }
1731
1732 static BackTrace*
1733 add_trace_thread (ThreadContext *thread, TraceDesc *trace, uint64_t value)
1734 {
1735         BackTrace *bt;
1736         int count = thread->stack_id;
1737         if (!collect_traces)
1738                 return NULL;
1739         if (count > trace_max)
1740                 count = trace_max;
1741         bt = add_backtrace (count, thread->stack + thread->stack_id - count);
1742         add_trace_bt (bt, trace, value);
1743         return bt;
1744 }
1745
1746 static BackTrace*
1747 add_trace_methods (MethodDesc **methods, int count, TraceDesc *trace, uint64_t value)
1748 {
1749         BackTrace *bt;
1750         if (!collect_traces)
1751                 return NULL;
1752         if (count > trace_max)
1753                 count = trace_max;
1754         bt = add_backtrace (count, methods);
1755         add_trace_bt (bt, trace, value);
1756         return bt;
1757 }
1758
1759 static void
1760 thread_add_root (ThreadContext *ctx, uintptr_t obj, int root_type, uintptr_t extra_info)
1761 {
1762         if (ctx->num_roots == ctx->size_roots) {
1763                 int new_size = ctx->size_roots * 2;
1764                 if (!new_size)
1765                         new_size = 4;
1766                 ctx->roots = (uintptr_t *)realloc (ctx->roots, new_size * sizeof (uintptr_t));
1767                 ctx->roots_extra = (uintptr_t *)realloc (ctx->roots_extra, new_size * sizeof (uintptr_t));
1768                 ctx->roots_types = (int *)realloc (ctx->roots_types, new_size * sizeof (int));
1769                 ctx->size_roots = new_size;
1770         }
1771         ctx->roots_types [ctx->num_roots] = root_type;
1772         ctx->roots_extra [ctx->num_roots] = extra_info;
1773         ctx->roots [ctx->num_roots++] = obj;
1774 }
1775
1776 static int
1777 compare_callc (const void *a, const void *b)
1778 {
1779         const CallContext *A = (const CallContext *)a;
1780         const CallContext *B = (const CallContext *)b;
1781         if (B->count == A->count)
1782                 return 0;
1783         if (B->count < A->count)
1784                 return -1;
1785         return 1;
1786 }
1787
1788 static void
1789 sort_context_array (TraceDesc* traces)
1790 {
1791         int i, j;
1792         for (i = 0, j = 0; i < traces->size; ++i) {
1793                 if (traces->traces [i].bt) {
1794                         traces->traces [j].bt = traces->traces [i].bt;
1795                         traces->traces [j].count = traces->traces [i].count;
1796                         j++;
1797                 }
1798         }
1799         qsort (traces->traces, traces->count, sizeof (CallContext), compare_callc);
1800 }
1801
1802 static void
1803 push_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
1804 {
1805         ensure_thread_stack (thread);
1806         thread->time_stack [thread->stack_id] = timestamp;
1807         thread->callee_time_stack [thread->stack_id] = 0;
1808         thread->stack [thread->stack_id++] = method;
1809         method->recurse_count++;
1810 }
1811
1812 static void
1813 pop_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
1814 {
1815         method->recurse_count--;
1816         if (thread->stack_id > 0 && thread->stack [thread->stack_id - 1] == method) {
1817                 uint64_t tdiff;
1818                 thread->stack_id--;
1819                 method->calls++;
1820                 if (timestamp < thread->time_stack [thread->stack_id])
1821                         fprintf (outfile, "time went backwards for %s\n", method->name);
1822                 tdiff = timestamp - thread->time_stack [thread->stack_id];
1823                 if (thread->callee_time_stack [thread->stack_id] > tdiff)
1824                         fprintf (outfile, "callee time bigger for %s\n", method->name);
1825                 method->self_time += tdiff - thread->callee_time_stack [thread->stack_id];
1826                 method->callee_time += thread->callee_time_stack [thread->stack_id];
1827                 if (thread->stack_id)
1828                         thread->callee_time_stack [thread->stack_id - 1] += tdiff;
1829                 //fprintf (outfile, "method %s took %d\n", method->name, (int)(tdiff/1000));
1830         } else {
1831                 fprintf (outfile, "unmatched leave at stack pos: %d for method %s\n", thread->stack_id, method->name);
1832         }
1833 }
1834
1835 typedef struct {
1836         uint64_t total_time;
1837         uint64_t max_time;
1838         int count;
1839 } GCDesc;
1840 static GCDesc gc_info [3];
1841 static uint64_t max_heap_size;
1842 static uint64_t gc_object_moves;
1843 static int gc_resizes;
1844 typedef struct {
1845         uint64_t created;
1846         uint64_t destroyed;
1847         uint64_t live;
1848         uint64_t max_live;
1849         TraceDesc traces;
1850         TraceDesc destroy_traces;
1851 } HandleInfo;
1852 static HandleInfo handle_info [4];
1853
1854 static const char*
1855 gc_event_name (int ev)
1856 {
1857         switch (ev) {
1858         case MONO_GC_EVENT_START: return "start";
1859         case MONO_GC_EVENT_MARK_START: return "mark start";
1860         case MONO_GC_EVENT_MARK_END: return "mark end";
1861         case MONO_GC_EVENT_RECLAIM_START: return "reclaim start";
1862         case MONO_GC_EVENT_RECLAIM_END: return "reclaim end";
1863         case MONO_GC_EVENT_END: return "end";
1864         case MONO_GC_EVENT_PRE_STOP_WORLD: return "pre stop";
1865         case MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED: return "pre stop lock";
1866         case MONO_GC_EVENT_POST_STOP_WORLD: return "post stop";
1867         case MONO_GC_EVENT_PRE_START_WORLD: return "pre start";
1868         case MONO_GC_EVENT_POST_START_WORLD: return "post start";
1869         case MONO_GC_EVENT_POST_START_WORLD_UNLOCKED: return "post start unlock";
1870         default:
1871                 return "unknown";
1872         }
1873 }
1874
1875 static const char*
1876 sync_point_name (int type)
1877 {
1878         switch (type) {
1879         case SYNC_POINT_PERIODIC: return "periodic";
1880         case SYNC_POINT_WORLD_STOP: return "world stop";
1881         case SYNC_POINT_WORLD_START: return "world start";
1882         default:
1883                 return "unknown";
1884         }
1885 }
1886
1887 static uint64_t clause_summary [MONO_EXCEPTION_CLAUSE_FAULT + 1];
1888 static uint64_t throw_count = 0;
1889 static TraceDesc exc_traces;
1890
1891 static const char*
1892 clause_name (int type)
1893 {
1894         switch (type) {
1895         case MONO_EXCEPTION_CLAUSE_NONE: return "catch";
1896         case MONO_EXCEPTION_CLAUSE_FILTER: return "filter";
1897         case MONO_EXCEPTION_CLAUSE_FINALLY: return "finally";
1898         case MONO_EXCEPTION_CLAUSE_FAULT: return "fault";
1899         default: return "invalid";
1900         }
1901 }
1902
1903 static uint64_t monitor_contention;
1904 static uint64_t monitor_failed;
1905 static uint64_t monitor_acquired;
1906
1907 struct _MonitorDesc {
1908         MonitorDesc *next;
1909         uintptr_t objid;
1910         uintptr_t contentions;
1911         uint64_t wait_time;
1912         uint64_t max_wait_time;
1913         TraceDesc traces;
1914 };
1915
1916 static MonitorDesc* monitor_hash [SMALL_HASH_SIZE] = {0};
1917 static int num_monitors = 0;
1918
1919 static MonitorDesc*
1920 lookup_monitor (uintptr_t objid)
1921 {
1922         int slot = ((objid >> 3) & 0xffff) % SMALL_HASH_SIZE;
1923         MonitorDesc *cd = monitor_hash [slot];
1924         while (cd && cd->objid != objid)
1925                 cd = cd->next;
1926         if (!cd) {
1927                 cd = (MonitorDesc *)calloc (sizeof (MonitorDesc), 1);
1928                 cd->objid = objid;
1929                 cd->next = monitor_hash [slot];
1930                 monitor_hash [slot] = cd;
1931                 num_monitors++;
1932         }
1933         return cd;
1934 }
1935
1936 static const char*
1937 monitor_ev_name (int ev)
1938 {
1939         switch (ev) {
1940         case MONO_PROFILER_MONITOR_CONTENTION: return "contended";
1941         case MONO_PROFILER_MONITOR_DONE: return "acquired";
1942         case MONO_PROFILER_MONITOR_FAIL: return "not taken";
1943         default: return "invalid";
1944         }
1945 }
1946
1947 static const char*
1948 get_handle_name (int htype)
1949 {
1950         switch (htype) {
1951         case 0: return "weak";
1952         case 1: return "weaktrack";
1953         case 2: return "normal";
1954         case 3: return "pinned";
1955         default: return "unknown";
1956         }
1957 }
1958
1959 static const char*
1960 get_root_name (int rtype)
1961 {
1962         switch (rtype & MONO_PROFILE_GC_ROOT_TYPEMASK) {
1963         case MONO_PROFILE_GC_ROOT_STACK: return "stack";
1964         case MONO_PROFILE_GC_ROOT_FINALIZER: return "finalizer";
1965         case MONO_PROFILE_GC_ROOT_HANDLE: return "handle";
1966         case MONO_PROFILE_GC_ROOT_OTHER: return "other";
1967         case MONO_PROFILE_GC_ROOT_MISC: return "misc";
1968         default: return "unknown";
1969         }
1970 }
1971
1972 static MethodDesc**
1973 decode_bt (ProfContext *ctx, MethodDesc** sframes, int *size, unsigned char *p, unsigned char **endp, intptr_t ptr_base, intptr_t *method_base)
1974 {
1975         MethodDesc **frames;
1976         int i;
1977         if (ctx->data_version < 13)
1978                 decode_uleb128 (p, &p); /* flags */
1979         int count = decode_uleb128 (p, &p);
1980         if (count > *size)
1981                 frames = (MethodDesc **)malloc (count * sizeof (void*));
1982         else
1983                 frames = sframes;
1984         for (i = 0; i < count; ++i) {
1985                 intptr_t ptrdiff = decode_sleb128 (p, &p);
1986                 if (ctx->data_version > 12) {
1987                         *method_base += ptrdiff;
1988                         frames [i] = lookup_method (*method_base);
1989                 } else {
1990                         frames [i] = lookup_method (ptr_base + ptrdiff);
1991                 }
1992         }
1993         *size = count;
1994         *endp = p;
1995         return frames;
1996 }
1997
1998 static void
1999 tracked_creation (uintptr_t obj, ClassDesc *cd, uint64_t size, BackTrace *bt, uint64_t timestamp)
2000 {
2001         int i;
2002         for (i = 0; i < num_tracked_objects; ++i) {
2003                 if (tracked_objects [i] != obj)
2004                         continue;
2005                 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);
2006                 if (bt && bt->count) {
2007                         int k;
2008                         for (k = 0; k < bt->count; ++k)
2009                                 fprintf (outfile, "\t%s\n", bt->methods [k]->name);
2010                 }
2011         }
2012 }
2013
2014 static void
2015 track_handle (uintptr_t obj, int htype, uint32_t handle, BackTrace *bt, uint64_t timestamp)
2016 {
2017         int i;
2018         for (i = 0; i < num_tracked_objects; ++i) {
2019                 if (tracked_objects [i] != obj)
2020                         continue;
2021                 fprintf (outfile, "Object %p referenced from handle %u at %.3f secs.\n", (void*)obj, handle, (timestamp - startup_time) / 1000000000.0);
2022                 if (bt && bt->count) {
2023                         int k;
2024                         for (k = 0; k < bt->count; ++k)
2025                                 fprintf (outfile, "\t%s\n", bt->methods [k]->name);
2026                 }
2027         }
2028 }
2029
2030 static void
2031 track_move (uintptr_t src, uintptr_t dst)
2032 {
2033         int i;
2034         for (i = 0; i < num_tracked_objects; ++i) {
2035                 if (tracked_objects [i] == src)
2036                         fprintf (outfile, "Object %p moved to %p\n", (void*)src, (void*)dst);
2037                 else if (tracked_objects [i] == dst)
2038                         fprintf (outfile, "Object %p moved from %p\n", (void*)dst, (void*)src);
2039         }
2040 }
2041
2042 static void
2043 track_obj_reference (uintptr_t obj, uintptr_t parent, ClassDesc *cd)
2044 {
2045         int i;
2046         for (i = 0; i < num_tracked_objects; ++i) {
2047                 if (tracked_objects [i] == obj)
2048                         fprintf (outfile, "Object %p referenced from %p (%s).\n", (void*)obj, (void*)parent, cd->name);
2049         }
2050 }
2051
2052 static void
2053 found_object (uintptr_t obj)
2054 {
2055         num_tracked_objects ++;
2056         tracked_objects = (uintptr_t *)realloc (tracked_objects, num_tracked_objects * sizeof (tracked_objects [0]));
2057         tracked_objects [num_tracked_objects - 1] = obj;
2058 }
2059
2060 static int num_jit_helpers = 0;
2061 static int jit_helpers_code_size = 0;
2062
2063 static const char*
2064 code_buffer_desc (int type)
2065 {
2066         switch (type) {
2067         case MONO_PROFILER_CODE_BUFFER_METHOD:
2068                 return "method";
2069         case MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE:
2070                 return "method trampoline";
2071         case MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE:
2072                 return "unbox trampoline";
2073         case MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE:
2074                 return "imt trampoline";
2075         case MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE:
2076                 return "generics trampoline";
2077         case MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE:
2078                 return "specific trampoline";
2079         case MONO_PROFILER_CODE_BUFFER_HELPER:
2080                 return "misc helper";
2081         case MONO_PROFILER_CODE_BUFFER_MONITOR:
2082                 return "monitor/lock";
2083         case MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE:
2084                 return "delegate invoke";
2085         case MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING:
2086                 return "exception handling";
2087         default:
2088                 return "unspecified";
2089         }
2090 }
2091
2092 typedef struct _CoverageAssembly CoverageAssembly;
2093 struct _CoverageAssembly {
2094         char *name;
2095         char *guid;
2096         char *filename;
2097         int number_of_methods;
2098         int fully_covered;
2099         int partially_covered;
2100 };
2101
2102 typedef struct _CoverageClass CoverageClass;
2103 struct _CoverageClass {
2104         char *assembly_name;
2105         char *class_name;
2106         int number_of_methods;
2107         int fully_covered;
2108         int partially_covered;
2109 };
2110
2111 typedef struct _CoverageCoverage CoverageCoverage;
2112 struct _CoverageCoverage {
2113         int method_id;
2114         int offset;
2115         int count;
2116         int line;
2117         int column;
2118 };
2119
2120 typedef struct _CoverageMethod CoverageMethod;
2121 struct _CoverageMethod {
2122         char *assembly_name;
2123         char *class_name;
2124         char *method_name;
2125         char *method_signature;
2126         char *filename;
2127         int token;
2128         int n_statements;
2129         int method_id;
2130         GPtrArray *coverage;
2131 };
2132 static GPtrArray *coverage_assemblies = NULL;
2133 static GPtrArray *coverage_methods = NULL;
2134 static GPtrArray *coverage_statements = NULL;
2135 static GHashTable *coverage_methods_hash = NULL;
2136 static GPtrArray *coverage_classes = NULL;
2137 static GHashTable *coverage_assembly_classes = NULL;
2138
2139 static void
2140 gather_coverage_statements (void)
2141 {
2142         for (guint i = 0; i < coverage_statements->len; i++) {
2143                 CoverageCoverage *coverage = (CoverageCoverage *)coverage_statements->pdata[i];
2144                 CoverageMethod *method = (CoverageMethod *)g_hash_table_lookup (coverage_methods_hash, GINT_TO_POINTER (coverage->method_id));
2145                 if (method == NULL) {
2146                         fprintf (outfile, "Cannot find method with ID: %d\n", coverage->method_id);
2147                         continue;
2148                 }
2149
2150                 g_ptr_array_add (method->coverage, coverage);
2151         }
2152 }
2153
2154 static void
2155 coverage_add_assembly (CoverageAssembly *assembly)
2156 {
2157         if (coverage_assemblies == NULL)
2158                 coverage_assemblies = g_ptr_array_new ();
2159
2160         g_ptr_array_add (coverage_assemblies, assembly);
2161 }
2162
2163 static void
2164 coverage_add_method (CoverageMethod *method)
2165 {
2166         if (coverage_methods == NULL) {
2167                 coverage_methods = g_ptr_array_new ();
2168                 coverage_methods_hash = g_hash_table_new (NULL, NULL);
2169         }
2170
2171         g_ptr_array_add (coverage_methods, method);
2172         g_hash_table_insert (coverage_methods_hash, GINT_TO_POINTER (method->method_id), method);
2173 }
2174
2175 static void
2176 coverage_add_class (CoverageClass *klass)
2177 {
2178         GPtrArray *classes = NULL;
2179
2180         if (coverage_classes == NULL) {
2181                 coverage_classes = g_ptr_array_new ();
2182                 coverage_assembly_classes = g_hash_table_new (g_str_hash, g_str_equal);
2183         }
2184
2185         g_ptr_array_add (coverage_classes, klass);
2186         classes = (GPtrArray *)g_hash_table_lookup (coverage_assembly_classes, klass->assembly_name);
2187         if (classes == NULL) {
2188                 classes = g_ptr_array_new ();
2189                 g_hash_table_insert (coverage_assembly_classes, klass->assembly_name, classes);
2190         }
2191         g_ptr_array_add (classes, klass);
2192 }
2193
2194 static void
2195 coverage_add_coverage (CoverageCoverage *coverage)
2196 {
2197         if (coverage_statements == NULL)
2198                 coverage_statements = g_ptr_array_new ();
2199
2200         g_ptr_array_add (coverage_statements, coverage);
2201 }
2202
2203 #define OBJ_ADDR(diff) ((obj_base + diff) << 3)
2204 #define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
2205
2206
2207 /* Stats */
2208 #define BUFFER_HEADER_SIZE 48
2209
2210 typedef struct {
2211         int count, min_size, max_size, bytes;
2212 } EventStat;
2213
2214 static int buffer_count;
2215 static EventStat stats [256];
2216
2217 static void
2218 record_event_stats (int type, int size)
2219 {
2220         ++stats [type].count;
2221         if (!stats [type].min_size)
2222                 stats [type].min_size = size;
2223         stats [type].min_size = MIN (stats [type].min_size, size);
2224         stats [type].max_size = MAX (stats [type].max_size, size);
2225         stats [type].bytes += size;
2226 }
2227
2228 static int
2229 decode_buffer (ProfContext *ctx)
2230 {
2231         unsigned char *p;
2232         unsigned char *end;
2233         intptr_t thread_id;
2234         intptr_t ptr_base;
2235         intptr_t obj_base;
2236         intptr_t method_base;
2237         uint64_t time_base;
2238         uint64_t file_offset;
2239         int len, i;
2240         ThreadContext *thread;
2241
2242 #ifdef HAVE_SYS_ZLIB
2243         if (ctx->gzfile)
2244                 file_offset = gztell (ctx->gzfile);
2245         else
2246 #endif
2247                 file_offset = ftell (ctx->file);
2248         if (!load_data (ctx, 48))
2249                 return 0;
2250         p = ctx->buf;
2251         if (read_int32 (p) != BUF_ID) {
2252                 fprintf (outfile, "Incorrect buffer id: 0x%x\n", read_int32 (p));
2253                 for (i = 0; i < 48; ++i) {
2254                         fprintf (outfile, "0x%x%s", p [i], i % 8?" ":"\n");
2255                 }
2256                 return 0;
2257         }
2258         len = read_int32 (p + 4);
2259         time_base = read_int64 (p + 8);
2260         ptr_base = read_int64 (p + 16);
2261         obj_base = read_int64 (p + 24);
2262         thread_id = read_int64 (p + 32);
2263         method_base = read_int64 (p + 40);
2264         if (debug)
2265                 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);
2266         thread = load_thread (ctx, thread_id);
2267         if (!load_data (ctx, len))
2268                 return 0;
2269
2270         ++buffer_count;
2271
2272         if (!startup_time) {
2273                 startup_time = time_base;
2274                 if (use_time_filter) {
2275                         time_from += startup_time;
2276                         time_to += startup_time;
2277                 }
2278         }
2279         for (i = 0; i < thread->stack_id; ++i)
2280                 thread->stack [i]->recurse_count++;
2281         p = ctx->buf;
2282         end = p + len;
2283         while (p < end) {
2284                 unsigned char *start = p;
2285                 unsigned char event = *p;
2286                 switch (*p & 0xf) {
2287                 case TYPE_GC: {
2288                         int subtype = *p & 0xf0;
2289                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2290                         LOG_TIME (time_base, tdiff);
2291                         time_base += tdiff;
2292                         if (subtype == TYPE_GC_RESIZE) {
2293                                 uint64_t new_size = decode_uleb128 (p, &p);
2294                                 if (debug)
2295                                         fprintf (outfile, "gc heap resized to %llu\n", (unsigned long long) new_size);
2296                                 gc_resizes++;
2297                                 if (new_size > max_heap_size)
2298                                         max_heap_size = new_size;
2299                         } else if (subtype == TYPE_GC_EVENT) {
2300                                 uint64_t ev;
2301                                 if (ctx->data_version > 12)
2302                                         ev = *p++;
2303                                 else
2304                                         ev = decode_uleb128 (p, &p);
2305                                 int gen;
2306                                 if (ctx->data_version > 12)
2307                                         gen = *p++;
2308                                 else
2309                                         gen = decode_uleb128 (p, &p);
2310                                 if (debug)
2311                                         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);
2312                                 if (gen > 2) {
2313                                         fprintf (outfile, "incorrect gc gen: %d\n", gen);
2314                                         break;
2315                                 }
2316                                 if (ev == MONO_GC_EVENT_START) {
2317                                         thread->gc_start_times [gen] = time_base;
2318                                         gc_info [gen].count++;
2319                                 } else if (ev == MONO_GC_EVENT_END) {
2320                                         tdiff = time_base - thread->gc_start_times [gen];
2321                                         gc_info [gen].total_time += tdiff;
2322                                         if (tdiff > gc_info [gen].max_time)
2323                                                 gc_info [gen].max_time = tdiff;
2324                                 }
2325                         } else if (subtype == TYPE_GC_MOVE) {
2326                                 int j, num = decode_uleb128 (p, &p);
2327                                 gc_object_moves += num / 2;
2328                                 for (j = 0; j < num; j += 2) {
2329                                         intptr_t obj1diff = decode_sleb128 (p, &p);
2330                                         intptr_t obj2diff = decode_sleb128 (p, &p);
2331                                         if (num_tracked_objects)
2332                                                 track_move (OBJ_ADDR (obj1diff), OBJ_ADDR (obj2diff));
2333                                         if (debug) {
2334                                                 fprintf (outfile, "moved obj %p to %p\n", (void*)OBJ_ADDR (obj1diff), (void*)OBJ_ADDR (obj2diff));
2335                                         }
2336                                 }
2337                         } else if (subtype == TYPE_GC_HANDLE_CREATED || subtype == TYPE_GC_HANDLE_CREATED_BT) {
2338                                 int has_bt = subtype == TYPE_GC_HANDLE_CREATED_BT;
2339                                 int num_bt = 0;
2340                                 MethodDesc *sframes [8];
2341                                 MethodDesc **frames = sframes;
2342                                 int htype = decode_uleb128 (p, &p);
2343                                 uint32_t handle = decode_uleb128 (p, &p);
2344                                 intptr_t objdiff = decode_sleb128 (p, &p);
2345                                 if (has_bt) {
2346                                         num_bt = 8;
2347                                         frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2348                                         if (!frames) {
2349                                                 fprintf (outfile, "Cannot load backtrace\n");
2350                                                 return 0;
2351                                         }
2352                                 }
2353                                 if (htype > 3)
2354                                         return 0;
2355                                 if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2356                                         handle_info [htype].created++;
2357                                         handle_info [htype].live++;
2358                                         if (handle_info [htype].live > handle_info [htype].max_live)
2359                                                 handle_info [htype].max_live = handle_info [htype].live;
2360                                         BackTrace *bt;
2361                                         if (has_bt)
2362                                                 bt = add_trace_methods (frames, num_bt, &handle_info [htype].traces, 1);
2363                                         else
2364                                                 bt = add_trace_thread (thread, &handle_info [htype].traces, 1);
2365                                         if (num_tracked_objects)
2366                                                 track_handle (OBJ_ADDR (objdiff), htype, handle, bt, time_base);
2367                                 }
2368                                 if (debug)
2369                                         fprintf (outfile, "handle (%s) %u created for object %p\n", get_handle_name (htype), handle, (void*)OBJ_ADDR (objdiff));
2370                                 if (frames != sframes)
2371                                         free (frames);
2372                         } else if (subtype == TYPE_GC_HANDLE_DESTROYED || subtype == TYPE_GC_HANDLE_DESTROYED_BT) {
2373                                 int has_bt = subtype == TYPE_GC_HANDLE_DESTROYED_BT;
2374                                 int num_bt = 0;
2375                                 MethodDesc *sframes [8];
2376                                 MethodDesc **frames = sframes;
2377                                 int htype = decode_uleb128 (p, &p);
2378                                 uint32_t handle = decode_uleb128 (p, &p);
2379                                 if (has_bt) {
2380                                         num_bt = 8;
2381                                         frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2382                                         if (!frames) {
2383                                                 fprintf (outfile, "Cannot load backtrace\n");
2384                                                 return 0;
2385                                         }
2386                                 }
2387                                 if (htype > 3)
2388                                         return 0;
2389                                 if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2390                                         handle_info [htype].destroyed ++;
2391                                         handle_info [htype].live--;
2392                                         BackTrace *bt;
2393                                         if (has_bt)
2394                                                 bt = add_trace_methods (frames, num_bt, &handle_info [htype].destroy_traces, 1);
2395                                         else
2396                                                 bt = add_trace_thread (thread, &handle_info [htype].destroy_traces, 1);
2397                                         /* TODO: track_handle_free () - would need to record and keep track of the associated object address... */
2398                                 }
2399                                 if (debug)
2400                                         fprintf (outfile, "handle (%s) %u destroyed\n", get_handle_name (htype), handle);
2401                                 if (frames != sframes)
2402                                         free (frames);
2403                         } else if (subtype == TYPE_GC_FINALIZE_START) {
2404                                 // TODO: Generate a finalizer report based on these events.
2405                                 if (debug)
2406                                         fprintf (outfile, "gc finalizer queue being processed at %llu\n", (unsigned long long) time_base);
2407                         } else if (subtype == TYPE_GC_FINALIZE_END) {
2408                                 if (debug)
2409                                         fprintf (outfile, "gc finalizer queue finished processing at %llu\n", (unsigned long long) time_base);
2410                         } else if (subtype == TYPE_GC_FINALIZE_OBJECT_START) {
2411                                 intptr_t objdiff = decode_sleb128 (p, &p);
2412                                 if (debug)
2413                                         fprintf (outfile, "gc finalizing object %p at %llu\n", (void *) OBJ_ADDR (objdiff), (unsigned long long) time_base);
2414                         } else if (subtype == TYPE_GC_FINALIZE_OBJECT_END) {
2415                                 intptr_t objdiff = decode_sleb128 (p, &p);
2416                                 if (debug)
2417                                         fprintf (outfile, "gc finalized object %p at %llu\n", (void *) OBJ_ADDR (objdiff), (unsigned long long) time_base);
2418                         }
2419                         break;
2420                 }
2421                 case TYPE_METADATA: {
2422                         int subtype = *p & 0xf0;
2423                         const char *load_str = subtype == TYPE_END_LOAD ? "loaded" : "unloaded";
2424                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2425                         int mtype = *p++;
2426                         intptr_t ptrdiff = decode_sleb128 (p, &p);
2427                         LOG_TIME (time_base, tdiff);
2428                         time_base += tdiff;
2429                         if (mtype == TYPE_CLASS) {
2430                                 intptr_t imptrdiff = decode_sleb128 (p, &p);
2431                                 if (ctx->data_version < 13)
2432                                         decode_uleb128 (p, &p); /* flags */
2433                                 if (debug)
2434                                         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);
2435                                 if (subtype == TYPE_END_LOAD)
2436                                         add_class (ptr_base + ptrdiff, (char*)p);
2437                                 while (*p) p++;
2438                                 p++;
2439                         } else if (mtype == TYPE_IMAGE) {
2440                                 if (ctx->data_version < 13)
2441                                         decode_uleb128 (p, &p); /* flags */
2442                                 if (debug)
2443                                         fprintf (outfile, "%s image %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
2444                                 if (subtype == TYPE_END_LOAD)
2445                                         add_image (ptr_base + ptrdiff, (char*)p);
2446                                 while (*p) p++;
2447                                 p++;
2448                         } else if (mtype == TYPE_ASSEMBLY) {
2449                                 if (ctx->data_version < 13)
2450                                         decode_uleb128 (p, &p); /* flags */
2451                                 if (debug)
2452                                         fprintf (outfile, "%s assembly %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
2453                                 if (subtype == TYPE_END_LOAD)
2454                                         add_assembly (ptr_base + ptrdiff, (char*)p);
2455                                 while (*p) p++;
2456                                 p++;
2457                         } else if (mtype == TYPE_DOMAIN) {
2458                                 if (ctx->data_version < 13)
2459                                         decode_uleb128 (p, &p); /* flags */
2460                                 DomainContext *nd = get_domain (ctx, ptr_base + ptrdiff);
2461                                 /* no subtype means it's a name event, rather than start/stop */
2462                                 if (subtype == 0)
2463                                         nd->friendly_name = pstrdup ((char *) p);
2464                                 if (debug) {
2465                                         if (subtype == 0)
2466                                                 fprintf (outfile, "domain %p named at %llu: %s\n", (void *) (ptr_base + ptrdiff), (unsigned long long) time_base, p);
2467                                         else
2468                                                 fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
2469                                 }
2470                                 if (subtype == 0) {
2471                                         while (*p) p++;
2472                                         p++;
2473                                 }
2474                         } else if (mtype == TYPE_CONTEXT) {
2475                                 if (ctx->data_version < 13)
2476                                         decode_uleb128 (p, &p); /* flags */
2477                                 intptr_t domaindiff = decode_sleb128 (p, &p);
2478                                 if (debug)
2479                                         fprintf (outfile, "%s context %p (%p) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), (void *) (ptr_base + domaindiff), (unsigned long long) time_base);
2480                                 if (subtype == TYPE_END_LOAD)
2481                                         get_remctx (ctx, ptr_base + ptrdiff)->domain_id = ptr_base + domaindiff;
2482                         } else if (mtype == TYPE_THREAD) {
2483                                 if (ctx->data_version < 13)
2484                                         decode_uleb128 (p, &p); /* flags */
2485                                 ThreadContext *nt = get_thread (ctx, ptr_base + ptrdiff);
2486                                 /* no subtype means it's a name event, rather than start/stop */
2487                                 if (subtype == 0)
2488                                         nt->name = pstrdup ((char*)p);
2489                                 if (debug) {
2490                                         if (subtype == 0)
2491                                                 fprintf (outfile, "thread %p named at %llu: %s\n", (void*)(ptr_base + ptrdiff), (unsigned long long) time_base, p);
2492                                         else
2493                                                 fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
2494                                 }
2495                                 if (subtype == 0) {
2496                                         while (*p) p++;
2497                                         p++;
2498                                 }
2499                         }
2500                         break;
2501                 }
2502                 case TYPE_ALLOC: {
2503                         int has_bt = *p & TYPE_ALLOC_BT;
2504                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2505                         intptr_t ptrdiff = decode_sleb128 (p, &p);
2506                         intptr_t objdiff = decode_sleb128 (p, &p);
2507                         uint64_t len;
2508                         int num_bt = 0;
2509                         MethodDesc* sframes [8];
2510                         MethodDesc** frames = sframes;
2511                         ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
2512                         len = decode_uleb128 (p, &p);
2513                         LOG_TIME (time_base, tdiff);
2514                         time_base += tdiff;
2515                         if (debug)
2516                                 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);
2517                         if (has_bt) {
2518                                 num_bt = 8;
2519                                 frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2520                                 if (!frames) {
2521                                         fprintf (outfile, "Cannot load backtrace\n");
2522                                         return 0;
2523                                 }
2524                         }
2525                         if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2526                                 BackTrace *bt;
2527                                 cd->allocs++;
2528                                 cd->alloc_size += len;
2529                                 if (has_bt)
2530                                         bt = add_trace_methods (frames, num_bt, &cd->traces, len);
2531                                 else
2532                                         bt = add_trace_thread (thread, &cd->traces, len);
2533                                 if (find_size && len >= find_size) {
2534                                         if (!find_name || strstr (cd->name, find_name))
2535                                                 found_object (OBJ_ADDR (objdiff));
2536                                 } else if (!find_size && find_name && strstr (cd->name, find_name)) {
2537                                         found_object (OBJ_ADDR (objdiff));
2538                                 }
2539                                 if (num_tracked_objects)
2540                                         tracked_creation (OBJ_ADDR (objdiff), cd, len, bt, time_base);
2541                         }
2542                         if (frames != sframes)
2543                                 free (frames);
2544                         break;
2545                 }
2546                 case TYPE_METHOD: {
2547                         int subtype = *p & 0xf0;
2548                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2549                         int64_t ptrdiff = decode_sleb128 (p, &p);
2550                         LOG_TIME (time_base, tdiff);
2551                         time_base += tdiff;
2552                         method_base += ptrdiff;
2553                         if (subtype == TYPE_JIT) {
2554                                 intptr_t codediff = decode_sleb128 (p, &p);
2555                                 int codelen = decode_uleb128 (p, &p);
2556                                 MethodDesc *jitted_method;
2557                                 if (debug)
2558                                         fprintf (outfile, "jitted method %p (%s), size: %d, code: %p\n", (void*)(method_base), p, codelen, (void*)(ptr_base + codediff));
2559                                 jitted_method = add_method (method_base, (char*)p, ptr_base + codediff, codelen);
2560                                 if (!(time_base >= time_from && time_base < time_to))
2561                                         jitted_method->ignore_jit = 1;
2562                                 while (*p) p++;
2563                                 p++;
2564                         } else {
2565                                 MethodDesc *method;
2566                                 if ((thread_filter && thread_filter != thread->thread_id))
2567                                         break;
2568                                 if (!(time_base >= time_from && time_base < time_to))
2569                                         break;
2570                                 method = lookup_method (method_base);
2571                                 if (subtype == TYPE_ENTER) {
2572                                         add_trace_thread (thread, &method->traces, 1);
2573                                         push_method (thread, method, time_base);
2574                                 } else {
2575                                         pop_method (thread, method, time_base);
2576                                 }
2577                                 if (debug)
2578                                         fprintf (outfile, "%s method %s\n", subtype == TYPE_ENTER? "enter": subtype == TYPE_EXC_LEAVE? "exleave": "leave", method->name);
2579                         }
2580                         break;
2581                 }
2582                 case TYPE_HEAP: {
2583                         int subtype = *p & 0xf0;
2584                         if (subtype == TYPE_HEAP_OBJECT) {
2585                                 HeapObjectDesc *ho = NULL;
2586                                 int i;
2587                                 intptr_t objdiff;
2588                                 if (ctx->data_version > 12) {
2589                                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2590                                         LOG_TIME (time_base, tdiff);
2591                                         time_base += tdiff;
2592                                         objdiff = decode_sleb128 (p, &p);
2593                                 } else
2594                                         objdiff = decode_sleb128 (p + 1, &p);
2595                                 intptr_t ptrdiff = decode_sleb128 (p, &p);
2596                                 uint64_t size = decode_uleb128 (p, &p);
2597                                 uintptr_t num = decode_uleb128 (p, &p);
2598                                 uintptr_t ref_offset = 0;
2599                                 uintptr_t last_obj_offset = 0;
2600                                 ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
2601                                 if (size) {
2602                                         HeapClassDesc *hcd = add_heap_shot_class (thread->current_heap_shot, cd, size);
2603                                         if (collect_traces) {
2604                                                 ho = alloc_heap_obj (OBJ_ADDR (objdiff), hcd, num);
2605                                                 add_heap_shot_obj (thread->current_heap_shot, ho);
2606                                                 ref_offset = 0;
2607                                         }
2608                                 } else {
2609                                         if (collect_traces)
2610                                                 ho = heap_shot_obj_add_refs (thread->current_heap_shot, OBJ_ADDR (objdiff), num, &ref_offset);
2611                                 }
2612                                 for (i = 0; i < num; ++i) {
2613                                         /* FIXME: use object distance to measure how good
2614                                          * the GC is at keeping related objects close
2615                                          */
2616                                         uintptr_t offset = ctx->data_version > 1? last_obj_offset + decode_uleb128 (p, &p): -1;
2617                                         intptr_t obj1diff = decode_sleb128 (p, &p);
2618                                         last_obj_offset = offset;
2619                                         if (collect_traces)
2620                                                 ho->refs [ref_offset + i] = OBJ_ADDR (obj1diff);
2621                                         if (num_tracked_objects)
2622                                                 track_obj_reference (OBJ_ADDR (obj1diff), OBJ_ADDR (objdiff), cd);
2623                                 }
2624                                 if (debug && size)
2625                                         fprintf (outfile, "traced object %p, size %llu (%s), refs: %zd\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) size, cd->name, num);
2626                         } else if (subtype == TYPE_HEAP_ROOT) {
2627                                 uintptr_t num;
2628                                 if (ctx->data_version > 12) {
2629                                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2630                                         LOG_TIME (time_base, tdiff);
2631                                         time_base += tdiff;
2632                                         num = decode_uleb128 (p, &p);
2633                                 } else
2634                                         num = decode_uleb128 (p + 1, &p);
2635                                 uintptr_t gc_num G_GNUC_UNUSED = decode_uleb128 (p, &p);
2636                                 int i;
2637                                 for (i = 0; i < num; ++i) {
2638                                         intptr_t objdiff = decode_sleb128 (p, &p);
2639                                         int root_type;
2640                                         if (ctx->data_version > 12)
2641                                                 root_type = *p++;
2642                                         else
2643                                                 root_type = decode_uleb128 (p, &p);
2644                                         /* we just discard the extra info for now */
2645                                         uintptr_t extra_info = decode_uleb128 (p, &p);
2646                                         if (debug)
2647                                                 fprintf (outfile, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff), get_root_name (root_type));
2648                                         if (collect_traces)
2649                                                 thread_add_root (thread, OBJ_ADDR (objdiff), root_type, extra_info);
2650                                 }
2651                         } else if (subtype == TYPE_HEAP_END) {
2652                                 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2653                                 LOG_TIME (time_base, tdiff);
2654                                 time_base += tdiff;
2655                                 if (debug)
2656                                         fprintf (outfile, "heap shot end\n");
2657                                 if (collect_traces) {
2658                                         HeapShot *hs = thread->current_heap_shot;
2659                                         if (hs && thread->num_roots) {
2660                                                 /* transfer the root ownershipt to the heapshot */
2661                                                 hs->num_roots = thread->num_roots;
2662                                                 hs->roots = thread->roots;
2663                                                 hs->roots_extra = thread->roots_extra;
2664                                                 hs->roots_types = thread->roots_types;
2665                                         } else {
2666                                                 free (thread->roots);
2667                                                 free (thread->roots_extra);
2668                                                 free (thread->roots_types);
2669                                         }
2670                                         thread->num_roots = 0;
2671                                         thread->size_roots = 0;
2672                                         thread->roots = NULL;
2673                                         thread->roots_extra = NULL;
2674                                         thread->roots_types = NULL;
2675                                         heap_shot_resolve_reverse_refs (hs);
2676                                         heap_shot_mark_objects (hs);
2677                                         heap_shot_free_objects (hs);
2678                                 }
2679                                 thread->current_heap_shot = NULL;
2680                         } else if (subtype == TYPE_HEAP_START) {
2681                                 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2682                                 LOG_TIME (time_base, tdiff);
2683                                 time_base += tdiff;
2684                                 if (debug)
2685                                         fprintf (outfile, "heap shot start\n");
2686                                 thread->current_heap_shot = new_heap_shot (time_base);
2687                         }
2688                         break;
2689                 }
2690                 case TYPE_MONITOR: {
2691                         int event = (*p >> 4) & 0x3;
2692                         int has_bt = *p & TYPE_MONITOR_BT;
2693                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2694                         intptr_t objdiff = decode_sleb128 (p, &p);
2695                         MethodDesc* sframes [8];
2696                         MethodDesc** frames = sframes;
2697                         int record;
2698                         int num_bt = 0;
2699                         LOG_TIME (time_base, tdiff);
2700                         time_base += tdiff;
2701                         record = (!thread_filter || thread_filter == thread->thread_id);
2702                         if (!(time_base >= time_from && time_base < time_to))
2703                                 record = 0;
2704                         if (event == MONO_PROFILER_MONITOR_CONTENTION) {
2705                                 MonitorDesc *mdesc = lookup_monitor (OBJ_ADDR (objdiff));
2706                                 if (record) {
2707                                         monitor_contention++;
2708                                         mdesc->contentions++;
2709                                         thread->monitor = mdesc;
2710                                         thread->contention_start = time_base;
2711                                 }
2712                                 if (has_bt) {
2713                                         num_bt = 8;
2714                                         frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2715                                         if (!frames) {
2716                                                 fprintf (outfile, "Cannot load backtrace\n");
2717                                                 return 0;
2718                                         }
2719                                         if (record)
2720                                                 add_trace_methods (frames, num_bt, &mdesc->traces, 1);
2721                                 } else {
2722                                         if (record)
2723                                                 add_trace_thread (thread, &mdesc->traces, 1);
2724                                 }
2725                         } else if (event == MONO_PROFILER_MONITOR_FAIL) {
2726                                 if (record) {
2727                                         monitor_failed++;
2728                                         if (thread->monitor && thread->contention_start) {
2729                                                 uint64_t wait_time = time_base - thread->contention_start;
2730                                                 if (wait_time > thread->monitor->max_wait_time)
2731                                                         thread->monitor->max_wait_time = wait_time;
2732                                                 thread->monitor->wait_time += wait_time;
2733                                                 thread->monitor = NULL;
2734                                                 thread->contention_start = 0;
2735                                         }
2736                                 }
2737                         } else if (event == MONO_PROFILER_MONITOR_DONE) {
2738                                 if (record) {
2739                                         monitor_acquired++;
2740                                         if (thread->monitor && thread->contention_start) {
2741                                                 uint64_t wait_time = time_base - thread->contention_start;
2742                                                 if (wait_time > thread->monitor->max_wait_time)
2743                                                         thread->monitor->max_wait_time = wait_time;
2744                                                 thread->monitor->wait_time += wait_time;
2745                                                 thread->monitor = NULL;
2746                                                 thread->contention_start = 0;
2747                                         }
2748                                 }
2749                         }
2750                         if (debug)
2751                                 fprintf (outfile, "monitor %s for object %p\n", monitor_ev_name (event), (void*)OBJ_ADDR (objdiff));
2752                         if (frames != sframes)
2753                                 free (frames);
2754                         break;
2755                 }
2756                 case TYPE_EXCEPTION: {
2757                         int subtype = *p & 0x70;
2758                         int has_bt = *p & TYPE_EXCEPTION_BT;
2759                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2760                         MethodDesc* sframes [8];
2761                         MethodDesc** frames = sframes;
2762                         int record;
2763                         LOG_TIME (time_base, tdiff);
2764                         time_base += tdiff;
2765                         record = (!thread_filter || thread_filter == thread->thread_id);
2766                         if (!(time_base >= time_from && time_base < time_to))
2767                                 record = 0;
2768                         if (subtype == TYPE_CLAUSE) {
2769                                 int clause_type;
2770                                 if (ctx->data_version > 12)
2771                                         clause_type = *p++;
2772                                 else
2773                                         clause_type = decode_uleb128 (p, &p);
2774                                 int clause_num = decode_uleb128 (p, &p);
2775                                 int64_t ptrdiff = decode_sleb128 (p, &p);
2776                                 method_base += ptrdiff;
2777                                 if (record)
2778                                         clause_summary [clause_type]++;
2779                                 if (debug)
2780                                         fprintf (outfile, "clause %s (%d) in method %s\n", clause_name (clause_type), clause_num, lookup_method (method_base)->name);
2781                         } else {
2782                                 intptr_t objdiff = decode_sleb128 (p, &p);
2783                                 if (record)
2784                                         throw_count++;
2785                                 if (has_bt) {
2786                                         has_bt = 8;
2787                                         frames = decode_bt (ctx, sframes, &has_bt, p, &p, ptr_base, &method_base);
2788                                         if (!frames) {
2789                                                 fprintf (outfile, "Cannot load backtrace\n");
2790                                                 return 0;
2791                                         }
2792                                         if (record)
2793                                                 add_trace_methods (frames, has_bt, &exc_traces, 1);
2794                                 } else {
2795                                         if (record)
2796                                                 add_trace_thread (thread, &exc_traces, 1);
2797                                 }
2798                                 if (frames != sframes)
2799                                         free (frames);
2800                                 if (debug)
2801                                         fprintf (outfile, "throw %p\n", (void*)OBJ_ADDR (objdiff));
2802                         }
2803                         break;
2804                 }
2805                 case TYPE_RUNTIME: {
2806                         int subtype = *p & 0xf0;
2807                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2808                         LOG_TIME (time_base, tdiff);
2809                         time_base += tdiff;
2810                         if (subtype == TYPE_JITHELPER) {
2811                                 int type;
2812                                 if (ctx->data_version > 12)
2813                                         type = *p++;
2814                                 else
2815                                         type = decode_uleb128 (p, &p);
2816                                 intptr_t codediff = decode_sleb128 (p, &p);
2817                                 int codelen = decode_uleb128 (p, &p);
2818                                 const char *name;
2819                                 if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
2820                                         name = (const char *)p;
2821                                         while (*p) p++;
2822                                                 p++;
2823                                 } else {
2824                                         name = code_buffer_desc (type);
2825                                 }
2826                                 num_jit_helpers++;
2827                                 jit_helpers_code_size += codelen;
2828                                 if (debug)
2829                                         fprintf (outfile, "jit helper %s, size: %d, code: %p\n", name, codelen, (void*)(ptr_base + codediff));
2830                         }
2831                         break;
2832                 }
2833                 case TYPE_SAMPLE: {
2834                         int subtype = *p & 0xf0;
2835                         if (subtype == TYPE_SAMPLE_HIT) {
2836                                 int i;
2837                                 int sample_type;
2838                                 uint64_t tstamp;
2839                                 if (ctx->data_version > 12) {
2840                                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2841                                         LOG_TIME (time_base, tdiff);
2842                                         time_base += tdiff;
2843                                         sample_type = *p++;
2844                                         tstamp = time_base;
2845                                 } else {
2846                                         sample_type = decode_uleb128 (p + 1, &p);
2847                                         tstamp = decode_uleb128 (p, &p);
2848                                 }
2849                                 void *tid = (void *) thread_id;
2850                                 if (ctx->data_version > 10)
2851                                         tid = (void *) (ptr_base + decode_sleb128 (p, &p));
2852                                 int count = decode_uleb128 (p, &p);
2853                                 for (i = 0; i < count; ++i) {
2854                                         uintptr_t ip = ptr_base + decode_sleb128 (p, &p);
2855                                         if ((tstamp >= time_from && tstamp < time_to))
2856                                                 add_stat_sample (sample_type, ip);
2857                                         if (debug)
2858                                                 fprintf (outfile, "sample hit, type: %d at %p for thread %p\n", sample_type, (void*)ip, tid);
2859                                 }
2860                                 if (ctx->data_version > 5) {
2861                                         count = decode_uleb128 (p, &p);
2862                                         for (i = 0; i < count; ++i) {
2863                                                 MethodDesc *method;
2864                                                 int64_t ptrdiff = decode_sleb128 (p, &p);
2865                                                 method_base += ptrdiff;
2866                                                 method = lookup_method (method_base);
2867                                                 if (debug)
2868                                                         fprintf (outfile, "sample hit bt %d: %s\n", i, method->name);
2869                                                 if (ctx->data_version < 13) {
2870                                                         decode_sleb128 (p, &p); /* il offset */
2871                                                         decode_sleb128 (p, &p); /* native offset */
2872                                                 }
2873                                         }
2874                                 }
2875                         } else if (subtype == TYPE_SAMPLE_USYM) {
2876                                 /* un unmanaged symbol description */
2877                                 uintptr_t addr;
2878                                 if (ctx->data_version > 12) {
2879                                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2880                                         LOG_TIME (time_base, tdiff);
2881                                         time_base += tdiff;
2882                                         addr = ptr_base + decode_sleb128 (p, &p);
2883                                 } else
2884                                         addr = ptr_base + decode_sleb128 (p + 1, &p);
2885                                 uintptr_t size = decode_uleb128 (p, &p);
2886                                 char *name;
2887                                 name = pstrdup ((char*)p);
2888                                 add_unmanaged_symbol (addr, name, size);
2889                                 if (debug)
2890                                         fprintf (outfile, "unmanaged symbol %s at %p\n", name, (void*)addr);
2891                                 while (*p) p++;
2892                                 p++;
2893                         } else if (subtype == TYPE_SAMPLE_UBIN) {
2894                                 /* un unmanaged binary loaded in memory */
2895                                 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2896                                 uintptr_t addr = decode_sleb128 (p, &p);
2897                                 uint64_t offset G_GNUC_UNUSED = decode_uleb128 (p, &p);
2898                                 uintptr_t size = decode_uleb128 (p, &p);
2899                                 char *name;
2900                                 LOG_TIME (time_base, tdiff);
2901                                 time_base += tdiff;
2902                                 name = pstrdup ((char*)p);
2903                                 add_unmanaged_binary (addr, name, size);
2904                                 if (debug)
2905                                         fprintf (outfile, "unmanaged binary %s at %p\n", name, (void*)addr);
2906                                 while (*p) p++;
2907                                 p++;
2908                         } else if (subtype == TYPE_SAMPLE_COUNTERS_DESC) {
2909                                 uint64_t i, len;
2910                                 if (ctx->data_version > 12) {
2911                                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2912                                         LOG_TIME (time_base, tdiff);
2913                                         time_base += tdiff;
2914                                         len = decode_uleb128 (p, &p);
2915                                 } else
2916                                         len = decode_uleb128 (p + 1, &p);
2917                                 for (i = 0; i < len; i++) {
2918                                         uint64_t type, unit, variance, index;
2919                                         uint64_t section = decode_uleb128 (p, &p);
2920                                         char *section_str, *name;
2921                                         if (section != MONO_COUNTER_PERFCOUNTERS) {
2922                                                 section_str = (char*) section_name (section);
2923                                         } else {
2924                                                 section_str = pstrdup ((char*)p);
2925                                                 while (*p++);
2926                                         }
2927                                         name = pstrdup ((char*)p);
2928                                         while (*p++);
2929                                         if (ctx->data_version > 12) {
2930                                                 type = *p++;
2931                                                 unit = *p++;
2932                                                 variance = *p++;
2933                                         } else {
2934                                                 type = decode_uleb128 (p, &p);
2935                                                 unit = decode_uleb128 (p, &p);
2936                                                 variance = decode_uleb128 (p, &p);
2937                                         }
2938                                         index = decode_uleb128 (p, &p);
2939                                         add_counter (section_str, name, (int)type, (int)unit, (int)variance, (int)index);
2940                                 }
2941                         } else if (subtype == TYPE_SAMPLE_COUNTERS) {
2942                                 int i;
2943                                 CounterValue *value, *previous = NULL;
2944                                 CounterList *list;
2945                                 uint64_t timestamp; // milliseconds since startup
2946                                 if (ctx->data_version > 12) {
2947                                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2948                                         LOG_TIME (time_base, tdiff);
2949                                         time_base += tdiff;
2950                                         timestamp = (time_base - startup_time) / 1000 / 1000;
2951                                 } else
2952                                         timestamp = decode_uleb128 (p + 1, &p);
2953                                 uint64_t time_between = timestamp / 1000 * 1000 * 1000 * 1000 + startup_time;
2954                                 while (1) {
2955                                         uint64_t type, index = decode_uleb128 (p, &p);
2956                                         if (index == 0)
2957                                                 break;
2958
2959                                         for (list = counters; list; list = list->next) {
2960                                                 if (list->counter->index == (int)index) {
2961                                                         previous = list->counter->values_last;
2962                                                         break;
2963                                                 }
2964                                         }
2965
2966                                         if (ctx->data_version > 12)
2967                                                 type = *p++;
2968                                         else
2969                                                 type = decode_uleb128 (p, &p);
2970
2971                                         value = (CounterValue *)calloc (1, sizeof (CounterValue));
2972                                         value->timestamp = timestamp;
2973
2974                                         switch (type) {
2975                                         case MONO_COUNTER_INT:
2976 #if SIZEOF_VOID_P == 4
2977                                         case MONO_COUNTER_WORD:
2978 #endif
2979                                                 value->buffer = (unsigned char *)malloc (sizeof (int32_t));
2980                                                 *(int32_t*)value->buffer = (int32_t)decode_sleb128 (p, &p) + (previous ? (*(int32_t*)previous->buffer) : 0);
2981                                                 break;
2982                                         case MONO_COUNTER_UINT:
2983                                                 value->buffer = (unsigned char *)malloc (sizeof (uint32_t));
2984                                                 *(uint32_t*)value->buffer = (uint32_t)decode_uleb128 (p, &p) + (previous ? (*(uint32_t*)previous->buffer) : 0);
2985                                                 break;
2986                                         case MONO_COUNTER_LONG:
2987 #if SIZEOF_VOID_P == 8
2988                                         case MONO_COUNTER_WORD:
2989 #endif
2990                                         case MONO_COUNTER_TIME_INTERVAL:
2991                                                 value->buffer = (unsigned char *)malloc (sizeof (int64_t));
2992                                                 *(int64_t*)value->buffer = (int64_t)decode_sleb128 (p, &p) + (previous ? (*(int64_t*)previous->buffer) : 0);
2993                                                 break;
2994                                         case MONO_COUNTER_ULONG:
2995                                                 value->buffer = (unsigned char *)malloc (sizeof (uint64_t));
2996                                                 *(uint64_t*)value->buffer = (uint64_t)decode_uleb128 (p, &p) + (previous ? (*(uint64_t*)previous->buffer) : 0);
2997                                                 break;
2998                                         case MONO_COUNTER_DOUBLE:
2999                                                 value->buffer = (unsigned char *)malloc (sizeof (double));
3000 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
3001                                                 for (i = 0; i < sizeof (double); i++)
3002 #else
3003                                                 for (i = sizeof (double) - 1; i >= 0; i--)
3004 #endif
3005                                                         value->buffer[i] = *p++;
3006                                                 break;
3007                                         case MONO_COUNTER_STRING:
3008                                                 if (*p++ == 0) {
3009                                                         value->buffer = NULL;
3010                                                 } else {
3011                                                         value->buffer = (unsigned char*) pstrdup ((char*)p);
3012                                                         while (*p++);
3013                                                 }
3014                                                 break;
3015                                         }
3016                                         if (time_between >= time_from && time_between <= time_to)
3017                                                 add_counter_value (index, value);
3018                                 }
3019                         } else {
3020                                 return 0;
3021                         }
3022                         break;
3023                 }
3024                 case TYPE_COVERAGE:{
3025                         int subtype = *p & 0xf0;
3026                         switch (subtype) {
3027                         case TYPE_COVERAGE_METHOD: {
3028                                 CoverageMethod *method = g_new0 (CoverageMethod, 1);
3029                                 const char *assembly, *klass, *name, *sig, *filename;
3030                                 int token, n_offsets, method_id;
3031
3032                                 p++;
3033
3034                                 if (ctx->data_version > 12) {
3035                                         uint64_t tdiff = decode_uleb128 (p, &p);
3036                                         LOG_TIME (time_base, tdiff);
3037                                         time_base += tdiff;
3038                                 }
3039
3040                                 assembly = (const char *)p; while (*p) p++; p++;
3041                                 klass = (const char *)p; while (*p) p++; p++;
3042                                 name = (const char *)p; while (*p) p++; p++;
3043                                 sig = (const char *)p; while (*p) p++; p++;
3044                                 filename = (const char *)p; while (*p) p++; p++;
3045
3046                                 token = decode_uleb128 (p, &p);
3047                                 method_id = decode_uleb128 (p, &p);
3048                                 n_offsets = decode_uleb128 (p, &p);
3049
3050                                 method->assembly_name = g_strdup (assembly);
3051                                 method->class_name = g_strdup (klass);
3052                                 method->method_name = g_strdup (name);
3053                                 method->method_signature = g_strdup (sig);
3054                                 method->filename = g_strdup (filename);
3055                                 method->token = token;
3056                                 method->n_statements = n_offsets;
3057                                 method->coverage = g_ptr_array_new ();
3058                                 method->method_id = method_id;
3059
3060                                 coverage_add_method (method);
3061
3062                                 break;
3063                         }
3064                         case TYPE_COVERAGE_STATEMENT: {
3065                                 CoverageCoverage *coverage = g_new0 (CoverageCoverage, 1);
3066                                 int offset, count, line, column, method_id;
3067
3068                                 p++;
3069
3070                                 if (ctx->data_version > 12) {
3071                                         uint64_t tdiff = decode_uleb128 (p, &p);
3072                                         LOG_TIME (time_base, tdiff);
3073                                         time_base += tdiff;
3074                                 }
3075
3076                                 method_id = decode_uleb128 (p, &p);
3077                                 offset = decode_uleb128 (p, &p);
3078                                 count = decode_uleb128 (p, &p);
3079                                 line = decode_uleb128 (p, &p);
3080                                 column = decode_uleb128 (p, &p);
3081
3082                                 coverage->method_id = method_id;
3083                                 coverage->offset = offset;
3084                                 coverage->count = count;
3085                                 coverage->line = line;
3086                                 coverage->column = column;
3087
3088                                 coverage_add_coverage (coverage);
3089                                 break;
3090                         }
3091                         case TYPE_COVERAGE_ASSEMBLY: {
3092                                 CoverageAssembly *assembly = g_new0 (CoverageAssembly, 1);
3093                                 char *name, *guid, *filename;
3094                                 int number_of_methods, fully_covered, partially_covered;
3095                                 p++;
3096
3097                                 if (ctx->data_version > 12) {
3098                                         uint64_t tdiff = decode_uleb128 (p, &p);
3099                                         LOG_TIME (time_base, tdiff);
3100                                         time_base += tdiff;
3101                                 }
3102
3103                                 name = (char *)p; while (*p) p++; p++;
3104                                 guid = (char *)p; while (*p) p++; p++;
3105                                 filename = (char *)p; while (*p) p++; p++;
3106                                 number_of_methods = decode_uleb128 (p, &p);
3107                                 fully_covered = decode_uleb128 (p, &p);
3108                                 partially_covered = decode_uleb128 (p, &p);
3109
3110                                 assembly->name = g_strdup (name);
3111                                 assembly->guid = g_strdup (guid);
3112                                 assembly->filename = g_strdup (filename);
3113                                 assembly->number_of_methods = number_of_methods;
3114                                 assembly->fully_covered = fully_covered;
3115                                 assembly->partially_covered = partially_covered;
3116
3117                                 coverage_add_assembly (assembly);
3118                                 break;
3119                         }
3120                         case TYPE_COVERAGE_CLASS: {
3121                                 CoverageClass *klass = g_new0 (CoverageClass, 1);
3122                                 char *assembly_name, *class_name;
3123                                 int number_of_methods, fully_covered, partially_covered;
3124                                 p++;
3125
3126                                 if (ctx->data_version > 12) {
3127                                         uint64_t tdiff = decode_uleb128 (p, &p);
3128                                         LOG_TIME (time_base, tdiff);
3129                                         time_base += tdiff;
3130                                 }
3131
3132                                 assembly_name = (char *)p; while (*p) p++; p++;
3133                                 class_name = (char *)p; while (*p) p++; p++;
3134                                 number_of_methods = decode_uleb128 (p, &p);
3135                                 fully_covered = decode_uleb128 (p, &p);
3136                                 partially_covered = decode_uleb128 (p, &p);
3137
3138                                 klass->assembly_name = g_strdup (assembly_name);
3139                                 klass->class_name = g_strdup (class_name);
3140                                 klass->number_of_methods = number_of_methods;
3141                                 klass->fully_covered = fully_covered;
3142                                 klass->partially_covered = partially_covered;
3143
3144                                 coverage_add_class (klass);
3145                                 break;
3146                         }
3147
3148                         default:
3149                                 break;
3150                         }
3151                         break;
3152                 }
3153                 case TYPE_META: {
3154                         int subtype = *p & 0xf0;
3155                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
3156                         LOG_TIME (time_base, tdiff);
3157                         time_base += tdiff;
3158                         if (subtype == TYPE_SYNC_POINT) {
3159                                 int type = *p++;
3160                                 if (debug)
3161                                         fprintf (outfile, "sync point %i (%s)\n", type, sync_point_name (type));
3162                         }
3163                         break;
3164                 }
3165                 default:
3166                         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);
3167                         exit (1);
3168                 }
3169                 record_event_stats (event, p - start);
3170         }
3171         thread->last_time = time_base;
3172         for (i = 0; i < thread->stack_id; ++i)
3173                 thread->stack [i]->recurse_count = 0;
3174         return 1;
3175 }
3176
3177 static ProfContext*
3178 load_file (char *name)
3179 {
3180         unsigned char *p;
3181         ProfContext *ctx = (ProfContext *)calloc (sizeof (ProfContext), 1);
3182         if (strcmp (name, "-") == 0)
3183                 ctx->file = stdin;
3184         else
3185                 ctx->file = fopen (name, "rb");
3186         if (!ctx->file) {
3187                 printf ("Cannot open file: %s\n", name);
3188                 exit (1);
3189         }
3190 #if defined (HAVE_SYS_ZLIB)
3191         if (ctx->file != stdin)
3192                 ctx->gzfile = gzdopen (fileno (ctx->file), "rb");
3193 #endif
3194         if (!load_data (ctx, 32))
3195                 return NULL;
3196         p = ctx->buf;
3197         if (read_int32 (p) != LOG_HEADER_ID || p [6] > LOG_DATA_VERSION)
3198                 return NULL;
3199         ctx->version_major = p [4];
3200         ctx->version_minor = p [5];
3201         ctx->data_version = p [6];
3202         /* reading 64 bit files on 32 bit systems not supported yet */
3203         if (p [7] > sizeof (void*))
3204                 return NULL;
3205         if (read_int32 (p + 20)) /* flags must be 0 */
3206                 return NULL;
3207         ctx->startup_time = read_int64 (p + 8);
3208         ctx->timer_overhead = read_int32 (p + 16);
3209         ctx->pid = read_int32 (p + 24);
3210         ctx->port = read_int16 (p + 28);
3211         return ctx;
3212 }
3213
3214 enum {
3215         ALLOC_SORT_BYTES,
3216         ALLOC_SORT_COUNT
3217 };
3218 static int alloc_sort_mode = ALLOC_SORT_BYTES;
3219
3220 static int
3221 compare_class (const void *a, const void *b)
3222 {
3223         ClassDesc *const *A = (ClassDesc *const *)a;
3224         ClassDesc *const *B = (ClassDesc *const *)b;
3225         uint64_t vala, valb;
3226         if (alloc_sort_mode == ALLOC_SORT_BYTES) {
3227                 vala = (*A)->alloc_size;
3228                 valb = (*B)->alloc_size;
3229         } else {
3230                 vala = (*A)->allocs;
3231                 valb = (*B)->allocs;
3232         }
3233         if (valb == vala)
3234                 return 0;
3235         if (valb < vala)
3236                 return -1;
3237         return 1;
3238 }
3239
3240 static void
3241 dump_header (ProfContext *ctx)
3242 {
3243         time_t st = ctx->startup_time / 1000;
3244         char *t = ctime (&st);
3245         fprintf (outfile, "\nMono log profiler data\n");
3246         fprintf (outfile, "\tProfiler version: %d.%d\n", ctx->version_major, ctx->version_minor);
3247         fprintf (outfile, "\tData version: %d\n", ctx->data_version);
3248         fprintf (outfile, "\tMean timer overhead: %d nanoseconds\n", ctx->timer_overhead);
3249         fprintf (outfile, "\tProgram startup: %s", t);
3250         if (ctx->pid)
3251                 fprintf (outfile, "\tProgram ID: %d\n", ctx->pid);
3252         if (ctx->port)
3253                 fprintf (outfile, "\tServer listening on: %d\n", ctx->port);
3254 }
3255
3256 static void
3257 dump_traces (TraceDesc *traces, const char *desc)
3258 {
3259         int j;
3260         if (!show_traces)
3261                 return;
3262         if (!traces->count)
3263                 return;
3264         sort_context_array (traces);
3265         for (j = 0; j < traces->count; ++j) {
3266                 int k;
3267                 BackTrace *bt;
3268                 bt = traces->traces [j].bt;
3269                 if (!bt->count)
3270                         continue;
3271                 fprintf (outfile, "\t%llu %s from:\n", (unsigned long long) traces->traces [j].count, desc);
3272                 for (k = 0; k < bt->count; ++k)
3273                         fprintf (outfile, "\t\t%s\n", bt->methods [k]->name);
3274         }
3275 }
3276
3277 static void
3278 dump_threads (ProfContext *ctx)
3279 {
3280         ThreadContext *thread;
3281         fprintf (outfile, "\nThread summary\n");
3282         for (thread = ctx->threads; thread; thread = thread->next) {
3283                 if (thread->thread_id) {
3284                         fprintf (outfile, "\tThread: %p, name: \"%s\"\n", (void*)thread->thread_id, thread->name? thread->name: "");
3285                 }
3286         }
3287 }
3288
3289 static void
3290 dump_domains (ProfContext *ctx)
3291 {
3292         fprintf (outfile, "\nDomain summary\n");
3293
3294         for (DomainContext *domain = ctx->domains; domain; domain = domain->next)
3295                 fprintf (outfile, "\tDomain: %p, friendly name: \"%s\"\n", (void *) domain->domain_id, domain->friendly_name);
3296 }
3297
3298 static void
3299 dump_remctxs (ProfContext *ctx)
3300 {
3301         fprintf (outfile, "\nContext summary\n");
3302
3303         for (RemCtxContext *remctx = ctx->remctxs; remctx; remctx = remctx->next)
3304                 fprintf (outfile, "\tContext: %p, domain: %p\n", (void *) remctx->remctx_id, (void *) remctx->domain_id);
3305 }
3306
3307 static void
3308 dump_exceptions (void)
3309 {
3310         int i;
3311         fprintf (outfile, "\nException summary\n");
3312         fprintf (outfile, "\tThrows: %llu\n", (unsigned long long) throw_count);
3313         dump_traces (&exc_traces, "throws");
3314         for (i = 0; i <= MONO_EXCEPTION_CLAUSE_FAULT; ++i) {
3315                 if (!clause_summary [i])
3316                         continue;
3317                 fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), (unsigned long long) clause_summary [i]);
3318         }
3319 }
3320
3321 static int
3322 compare_monitor (const void *a, const void *b)
3323 {
3324         MonitorDesc *const *A = (MonitorDesc *const *)a;
3325         MonitorDesc *const *B = (MonitorDesc *const *)b;
3326         if ((*B)->wait_time == (*A)->wait_time)
3327                 return 0;
3328         if ((*B)->wait_time < (*A)->wait_time)
3329                 return -1;
3330         return 1;
3331 }
3332
3333 static void
3334 dump_monitors (void)
3335 {
3336         MonitorDesc **monitors;
3337         int i, j;
3338         if (!num_monitors)
3339                 return;
3340         monitors = (MonitorDesc **)malloc (sizeof (void*) * num_monitors);
3341         for (i = 0, j = 0; i < SMALL_HASH_SIZE; ++i) {
3342                 MonitorDesc *mdesc = monitor_hash [i];
3343                 while (mdesc) {
3344                         monitors [j++] = mdesc;
3345                         mdesc = mdesc->next;
3346                 }
3347         }
3348         qsort (monitors, num_monitors, sizeof (void*), compare_monitor);
3349         fprintf (outfile, "\nMonitor lock summary\n");
3350         for (i = 0; i < num_monitors; ++i) {
3351                 MonitorDesc *mdesc = monitors [i];
3352                 fprintf (outfile, "\tLock object %p: %d contentions\n", (void*)mdesc->objid, (int)mdesc->contentions);
3353                 fprintf (outfile, "\t\t%.6f secs total wait time, %.6f max, %.6f average\n",
3354                         mdesc->wait_time/1000000000.0, mdesc->max_wait_time/1000000000.0, mdesc->wait_time/1000000000.0/mdesc->contentions);
3355                 dump_traces (&mdesc->traces, "contentions");
3356         }
3357         fprintf (outfile, "\tLock contentions: %llu\n", (unsigned long long) monitor_contention);
3358         fprintf (outfile, "\tLock acquired: %llu\n", (unsigned long long) monitor_acquired);
3359         fprintf (outfile, "\tLock failures: %llu\n", (unsigned long long) monitor_failed);
3360 }
3361
3362 static void
3363 dump_gcs (void)
3364 {
3365         int i;
3366         fprintf (outfile, "\nGC summary\n");
3367         fprintf (outfile, "\tGC resizes: %d\n", gc_resizes);
3368         fprintf (outfile, "\tMax heap size: %llu\n", (unsigned long long) max_heap_size);
3369         fprintf (outfile, "\tObject moves: %llu\n", (unsigned long long) gc_object_moves);
3370         for (i = 0; i < 3; ++i) {
3371                 if (!gc_info [i].count)
3372                         continue;
3373                 fprintf (outfile, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
3374                         i, gc_info [i].count,
3375                         (unsigned long long) (gc_info [i].max_time / 1000),
3376                         (unsigned long long) (gc_info [i].total_time / 1000),
3377                         (unsigned long long) (gc_info [i].total_time / gc_info [i].count / 1000));
3378         }
3379         for (i = 0; i < 3; ++i) {
3380                 if (!handle_info [i].max_live)
3381                         continue;
3382                 fprintf (outfile, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
3383                         get_handle_name (i),
3384                         (unsigned long long) (handle_info [i].created),
3385                         (unsigned long long) (handle_info [i].destroyed),
3386                         (unsigned long long) (handle_info [i].max_live));
3387                 dump_traces (&handle_info [i].traces, "created");
3388                 dump_traces (&handle_info [i].destroy_traces, "destroyed");
3389         }
3390 }
3391
3392 static void
3393 dump_jit (void)
3394 {
3395         int i;
3396         int code_size = 0;
3397         int compiled_methods = 0;
3398         MethodDesc* m;
3399         fprintf (outfile, "\nJIT summary\n");
3400         for (i = 0; i < HASH_SIZE; ++i) {
3401                 m = method_hash [i];
3402                 for (m = method_hash [i]; m; m = m->next) {
3403                         if (!m->code || m->ignore_jit)
3404                                 continue;
3405                         compiled_methods++;
3406                         code_size += m->len;
3407                 }
3408         }
3409         fprintf (outfile, "\tCompiled methods: %d\n", compiled_methods);
3410         fprintf (outfile, "\tGenerated code size: %d\n", code_size);
3411         fprintf (outfile, "\tJIT helpers: %d\n", num_jit_helpers);
3412         fprintf (outfile, "\tJIT helpers code size: %d\n", jit_helpers_code_size);
3413 }
3414
3415 static void
3416 dump_allocations (void)
3417 {
3418         int i, c;
3419         intptr_t allocs = 0;
3420         uint64_t size = 0;
3421         int header_done = 0;
3422         ClassDesc **classes = (ClassDesc **)malloc (num_classes * sizeof (void*));
3423         ClassDesc *cd;
3424         c = 0;
3425         for (i = 0; i < HASH_SIZE; ++i) {
3426                 cd = class_hash [i];
3427                 while (cd) {
3428                         classes [c++] = cd;
3429                         cd = cd->next;
3430                 }
3431         }
3432         qsort (classes, num_classes, sizeof (void*), compare_class);
3433         for (i = 0; i < num_classes; ++i) {
3434                 cd = classes [i];
3435                 if (!cd->allocs)
3436                         continue;
3437                 allocs += cd->allocs;
3438                 size += cd->alloc_size;
3439                 if (!header_done++) {
3440                         fprintf (outfile, "\nAllocation summary\n");
3441                         fprintf (outfile, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
3442                 }
3443                 fprintf (outfile, "%10llu %10zd %8llu %s\n",
3444                         (unsigned long long) (cd->alloc_size),
3445                         cd->allocs,
3446                         (unsigned long long) (cd->alloc_size / cd->allocs),
3447                         cd->name);
3448                 dump_traces (&cd->traces, "bytes");
3449         }
3450         if (allocs)
3451                 fprintf (outfile, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size, allocs);
3452 }
3453
3454 enum {
3455         METHOD_SORT_TOTAL,
3456         METHOD_SORT_SELF,
3457         METHOD_SORT_CALLS
3458 };
3459
3460 static int method_sort_mode = METHOD_SORT_TOTAL;
3461
3462 static int
3463 compare_method (const void *a, const void *b)
3464 {
3465         MethodDesc *const *A = (MethodDesc *const *)a;
3466         MethodDesc *const *B = (MethodDesc *const *)b;
3467         uint64_t vala, valb;
3468         if (method_sort_mode == METHOD_SORT_SELF) {
3469                 vala = (*A)->self_time;
3470                 valb = (*B)->self_time;
3471         } else if (method_sort_mode == METHOD_SORT_CALLS) {
3472                 vala = (*A)->calls;
3473                 valb = (*B)->calls;
3474         } else {
3475                 vala = (*A)->total_time;
3476                 valb = (*B)->total_time;
3477         }
3478         if (vala == valb)
3479                 return 0;
3480         if (valb < vala)
3481                 return -1;
3482         return 1;
3483 }
3484
3485 static void
3486 dump_metadata (void)
3487 {
3488         fprintf (outfile, "\nMetadata summary\n");
3489         fprintf (outfile, "\tLoaded images: %d\n", num_images);
3490         if (verbose) {
3491                 ImageDesc *image;
3492                 int i;
3493                 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
3494                         image = image_hash [i];
3495                         while (image) {
3496                                 fprintf (outfile, "\t\t%s\n", image->filename);
3497                                 image = image->next;
3498                         }
3499                 }
3500         }
3501         fprintf (outfile, "\tLoaded assemblies: %d\n", num_assemblies);
3502         if (verbose) {
3503                 AssemblyDesc *assembly;
3504                 int i;
3505                 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
3506                         assembly = assembly_hash [i];
3507                         while (assembly) {
3508                                 fprintf (outfile, "\t\t%s\n", assembly->asmname);
3509                                 assembly = assembly->next;
3510                         }
3511                 }
3512         }
3513 }
3514
3515 static void
3516 dump_methods (void)
3517 {
3518         int i, c;
3519         uint64_t calls = 0;
3520         int header_done = 0;
3521         MethodDesc **methods = (MethodDesc **)malloc (num_methods * sizeof (void*));
3522         MethodDesc *cd;
3523         c = 0;
3524         for (i = 0; i < HASH_SIZE; ++i) {
3525                 cd = method_hash [i];
3526                 while (cd) {
3527                         cd->total_time = cd->self_time + cd->callee_time;
3528                         methods [c++] = cd;
3529                         cd = cd->next;
3530                 }
3531         }
3532         qsort (methods, num_methods, sizeof (void*), compare_method);
3533         for (i = 0; i < num_methods; ++i) {
3534                 uint64_t msecs;
3535                 uint64_t smsecs;
3536                 cd = methods [i];
3537                 if (!cd->calls)
3538                         continue;
3539                 calls += cd->calls;
3540                 msecs = cd->total_time / 1000000;
3541                 smsecs = (cd->total_time - cd->callee_time) / 1000000;
3542                 if (!msecs && !verbose)
3543                         continue;
3544                 if (!header_done++) {
3545                         fprintf (outfile, "\nMethod call summary\n");
3546                         fprintf (outfile, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
3547                 }
3548                 fprintf (outfile, "%8llu %8llu %10llu %s\n",
3549                         (unsigned long long) (msecs),
3550                         (unsigned long long) (smsecs),
3551                         (unsigned long long) (cd->calls),
3552                         cd->name);
3553                 dump_traces (&cd->traces, "calls");
3554         }
3555         if (calls)
3556                 fprintf (outfile, "Total calls: %llu\n", (unsigned long long) calls);
3557 }
3558
3559 static int
3560 compare_heap_class (const void *a, const void *b)
3561 {
3562         HeapClassDesc *const *A = (HeapClassDesc *const *)a;
3563         HeapClassDesc *const *B = (HeapClassDesc *const *)b;
3564         uint64_t vala, valb;
3565         if (alloc_sort_mode == ALLOC_SORT_BYTES) {
3566                 vala = (*A)->total_size;
3567                 valb = (*B)->total_size;
3568         } else {
3569                 vala = (*A)->count;
3570                 valb = (*B)->count;
3571         }
3572         if (valb == vala)
3573                 return 0;
3574         if (valb < vala)
3575                 return -1;
3576         return 1;
3577 }
3578
3579 static int
3580 compare_rev_class (const void *a, const void *b)
3581 {
3582         const HeapClassRevRef *A = (const HeapClassRevRef *)a;
3583         const HeapClassRevRef *B = (const HeapClassRevRef *)b;
3584         if (B->count == A->count)
3585                 return 0;
3586         if (B->count < A->count)
3587                 return -1;
3588         return 1;
3589 }
3590
3591 static void
3592 dump_rev_claases (HeapClassRevRef *revs, int count)
3593 {
3594         int j;
3595         if (!show_traces)
3596                 return;
3597         if (!count)
3598                 return;
3599         for (j = 0; j < count; ++j) {
3600                 HeapClassDesc *cd = revs [j].klass;
3601                 fprintf (outfile, "\t\t%llu references from: %s\n",
3602                         (unsigned long long) (revs [j].count),
3603                         cd->klass->name);
3604         }
3605 }
3606
3607 static void
3608 heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
3609 {
3610         uint64_t size = 0;
3611         uint64_t count = 0;
3612         int ccount = 0;
3613         int i;
3614         HeapClassDesc *cd;
3615         HeapClassDesc **sorted;
3616         sorted = (HeapClassDesc **)malloc (sizeof (void*) * hs->class_count);
3617         for (i = 0; i < hs->hash_size; ++i) {
3618                 cd = hs->class_hash [i];
3619                 if (!cd)
3620                         continue;
3621                 count += cd->count;
3622                 size += cd->total_size;
3623                 sorted [ccount++] = cd;
3624         }
3625         hs->sorted = sorted;
3626         qsort (sorted, ccount, sizeof (void*), compare_heap_class);
3627         fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %zd\n",
3628                 hs_num,
3629                 (hs->timestamp - startup_time)/1000000000.0,
3630                 (unsigned long long) (size),
3631                 (unsigned long long) (count),
3632                 ccount, hs->num_roots);
3633         if (!verbose && ccount > 30)
3634                 ccount = 30;
3635         fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
3636         for (i = 0; i < ccount; ++i) {
3637                 HeapClassRevRef *rev_sorted;
3638                 int j, k;
3639                 HeapClassDesc *ocd = NULL;
3640                 cd = sorted [i];
3641                 if (last_hs)
3642                         ocd = heap_class_lookup (last_hs, cd->klass);
3643                 fprintf (outfile, "\t%10llu %10llu %8llu %s",
3644                         (unsigned long long) (cd->total_size),
3645                         (unsigned long long) (cd->count),
3646                         (unsigned long long) (cd->total_size / cd->count),
3647                         cd->klass->name);
3648                 if (ocd) {
3649                         int64_t bdiff = cd->total_size - ocd->total_size;
3650                         int64_t cdiff = cd->count - ocd->count;
3651                         fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", (long long) bdiff, (long long) cdiff);
3652                 } else {
3653                         fprintf (outfile, "\n");
3654                 }
3655                 if (!collect_traces)
3656                         continue;
3657                 rev_sorted = (HeapClassRevRef *)malloc (cd->rev_count * sizeof (HeapClassRevRef));
3658                 k = 0;
3659                 for (j = 0; j < cd->rev_hash_size; ++j) {
3660                         if (cd->rev_hash [j].klass)
3661                                 rev_sorted [k++] = cd->rev_hash [j];
3662                 }
3663                 assert (cd->rev_count == k);
3664                 qsort (rev_sorted, cd->rev_count, sizeof (HeapClassRevRef), compare_rev_class);
3665                 if (cd->root_references)
3666                         fprintf (outfile, "\t\t%zd root references (%zd pinning)\n", cd->root_references, cd->pinned_references);
3667                 dump_rev_claases (rev_sorted, cd->rev_count);
3668                 free (rev_sorted);
3669         }
3670         free (sorted);
3671 }
3672
3673 static int
3674 compare_heap_shots (const void *a, const void *b)
3675 {
3676         HeapShot *const *A = (HeapShot *const *)a;
3677         HeapShot *const *B = (HeapShot *const *)b;
3678         if ((*B)->timestamp == (*A)->timestamp)
3679                 return 0;
3680         if ((*B)->timestamp > (*A)->timestamp)
3681                 return -1;
3682         return 1;
3683 }
3684
3685 static void
3686 dump_heap_shots (void)
3687 {
3688         HeapShot **hs_sorted;
3689         HeapShot *hs;
3690         HeapShot *last_hs = NULL;
3691         int i;
3692         if (!heap_shots)
3693                 return;
3694         hs_sorted = (HeapShot **)malloc (num_heap_shots * sizeof (void*));
3695         fprintf (outfile, "\nHeap shot summary\n");
3696         i = 0;
3697         for (hs = heap_shots; hs; hs = hs->next)
3698                 hs_sorted [i++] = hs;
3699         qsort (hs_sorted, num_heap_shots, sizeof (void*), compare_heap_shots);
3700         for (i = 0; i < num_heap_shots; ++i) {
3701                 hs = hs_sorted [i];
3702                 heap_shot_summary (hs, i, last_hs);
3703                 last_hs = hs;
3704         }
3705 }
3706
3707 /* This is a very basic escape function that escapes < > and &
3708    Ideally we'd use g_markup_escape_string but that function isn't
3709          available in Mono's eglib. This was written without looking at the
3710          source of that function in glib. */
3711 static char *
3712 escape_string_for_xml (const char *string)
3713 {
3714         GString *string_builder = g_string_new (NULL);
3715         const char *start, *p;
3716
3717         start = p = string;
3718         while (*p) {
3719                 while (*p && *p != '&' && *p != '<' && *p != '>')
3720                         p++;
3721
3722                 g_string_append_len (string_builder, start, p - start);
3723
3724                 if (*p == '\0')
3725                         break;
3726
3727                 switch (*p) {
3728                 case '<':
3729                         g_string_append (string_builder, "&lt;");
3730                         break;
3731
3732                 case '>':
3733                         g_string_append (string_builder, "&gt;");
3734                         break;
3735
3736                 case '&':
3737                         g_string_append (string_builder, "&amp;");
3738                         break;
3739
3740                 default:
3741                         break;
3742                 }
3743
3744                 p++;
3745                 start = p;
3746         }
3747
3748         return g_string_free (string_builder, FALSE);
3749 }
3750
3751 static int
3752 sort_assemblies (gconstpointer a, gconstpointer b)
3753 {
3754         CoverageAssembly *assembly_a = *(CoverageAssembly **)a;
3755         CoverageAssembly *assembly_b = *(CoverageAssembly **)b;
3756
3757         if (assembly_a->name == NULL && assembly_b->name == NULL)
3758                 return 0;
3759         else if (assembly_a->name == NULL)
3760                 return -1;
3761         else if (assembly_b->name == NULL)
3762                 return 1;
3763
3764         return strcmp (assembly_a->name, assembly_b->name);
3765 }
3766
3767 static void
3768 dump_coverage (void)
3769 {
3770         if (!coverage_methods && !coverage_assemblies)
3771                 return;
3772
3773         gather_coverage_statements ();
3774         fprintf (outfile, "\nCoverage Summary:\n");
3775
3776         if (coverage_outfile) {
3777                 fprintf (coverage_outfile, "<?xml version=\"1.0\"?>\n");
3778                 fprintf (coverage_outfile, "<coverage version=\"0.3\">\n");
3779         }
3780
3781         g_ptr_array_sort (coverage_assemblies, sort_assemblies);
3782
3783         for (guint i = 0; i < coverage_assemblies->len; i++) {
3784                 CoverageAssembly *assembly = (CoverageAssembly *)coverage_assemblies->pdata[i];
3785                 GPtrArray *classes;
3786
3787                 if (assembly->number_of_methods != 0) {
3788                         int percentage = ((assembly->fully_covered + assembly->partially_covered) * 100) / assembly->number_of_methods;
3789                         fprintf (outfile, "\t%s (%s) %d%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, percentage, assembly->number_of_methods, assembly->fully_covered);
3790                 } else
3791                         fprintf (outfile, "\t%s (%s) ?%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, assembly->number_of_methods, assembly->fully_covered);
3792
3793                 if (coverage_outfile) {
3794                         char *escaped_name, *escaped_filename;
3795                         escaped_name = escape_string_for_xml (assembly->name);
3796                         escaped_filename = escape_string_for_xml (assembly->filename);
3797
3798                         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);
3799
3800                         g_free (escaped_name);
3801                         g_free (escaped_filename);
3802                 }
3803
3804                 classes = (GPtrArray *)g_hash_table_lookup (coverage_assembly_classes, assembly->name);
3805                 if (classes) {
3806                         for (guint j = 0; j < classes->len; j++) {
3807                                 CoverageClass *klass = (CoverageClass *)classes->pdata [j];
3808
3809                                 if (klass->number_of_methods > 0) {
3810                                         int percentage = ((klass->fully_covered + klass->partially_covered) * 100) / klass->number_of_methods;
3811                                         fprintf (outfile, "\t\t%s %d%% covered (%d methods - %d covered)\n", klass->class_name, percentage, klass->number_of_methods, klass->fully_covered);
3812                                 } else
3813                                         fprintf (outfile, "\t\t%s ?%% covered (%d methods - %d covered)\n", klass->class_name, klass->number_of_methods, klass->fully_covered);
3814
3815                                 if (coverage_outfile) {
3816                                         char *escaped_name;
3817                                         escaped_name = escape_string_for_xml (klass->class_name);
3818
3819                                         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);
3820                                         g_free (escaped_name);
3821                                 }
3822                         }
3823                 }
3824         }
3825
3826         for (guint i = 0; i < coverage_methods->len; i++) {
3827                 CoverageMethod *method = (CoverageMethod *)coverage_methods->pdata [i];
3828
3829                 if (coverage_outfile) {
3830                         char *escaped_assembly, *escaped_class, *escaped_method, *escaped_sig, *escaped_filename;
3831
3832                         escaped_assembly = escape_string_for_xml (method->assembly_name);
3833                         escaped_class = escape_string_for_xml (method->class_name);
3834                         escaped_method = escape_string_for_xml (method->method_name);
3835                         escaped_sig = escape_string_for_xml (method->method_signature);
3836                         escaped_filename = escape_string_for_xml (method->filename);
3837
3838                         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);
3839
3840                         g_free (escaped_assembly);
3841                         g_free (escaped_class);
3842                         g_free (escaped_method);
3843                         g_free (escaped_sig);
3844                         g_free (escaped_filename);
3845
3846                         for (guint j = 0; j < method->coverage->len; j++) {
3847                                 CoverageCoverage *coverage = (CoverageCoverage *)method->coverage->pdata [j];
3848                                 fprintf (coverage_outfile, "\t\t<statement offset=\"%d\" counter=\"%d\" line=\"%d\" column=\"%d\"/>\n", coverage->offset, coverage->count, coverage->line, coverage->column);
3849                         }
3850                         fprintf (coverage_outfile, "\t</method>\n");
3851                 }
3852         }
3853
3854         if (coverage_outfile) {
3855                 fprintf (coverage_outfile, "</coverage>\n");
3856                 fclose (coverage_outfile);
3857                 coverage_outfile = NULL;
3858         }
3859 }
3860
3861 #define DUMP_EVENT_STAT(EVENT,SUBTYPE) dump_event (#EVENT, #SUBTYPE, EVENT, SUBTYPE);
3862
3863 static void
3864 dump_event (const char *event_name, const char *subtype_name, int event, int subtype)
3865 {
3866         int idx = event | subtype;
3867         EventStat evt = stats [idx];
3868         if (!evt.count)
3869                 return;
3870
3871         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);
3872 }
3873
3874 static void
3875 dump_stats (void)
3876 {
3877         fprintf (outfile, "\nMlpd statistics\n");
3878         fprintf (outfile, "\tBuffer count %d\toverhead %d (%d bytes per header)\n", buffer_count, buffer_count * BUFFER_HEADER_SIZE, BUFFER_HEADER_SIZE);
3879         fprintf (outfile, "\nEvent details:\n");
3880
3881         DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_NO_BT);
3882         DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_BT);
3883
3884         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_EVENT);
3885         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_RESIZE);
3886         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_MOVE);
3887         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED);
3888         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED);
3889         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED_BT);
3890         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED_BT);
3891
3892         DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_LOAD);
3893         DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_UNLOAD);
3894
3895         DUMP_EVENT_STAT (TYPE_METHOD, TYPE_LEAVE);
3896         DUMP_EVENT_STAT (TYPE_METHOD, TYPE_ENTER);
3897         DUMP_EVENT_STAT (TYPE_METHOD, TYPE_EXC_LEAVE);
3898         DUMP_EVENT_STAT (TYPE_METHOD, TYPE_JIT);
3899
3900         DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_THROW);
3901         DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_CLAUSE);
3902         DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_EXCEPTION_BT);
3903
3904         DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_NO_BT);
3905         DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_BT);
3906
3907         DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_START);
3908         DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_END);
3909         DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_OBJECT);
3910         DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_ROOT);
3911
3912         DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_HIT);
3913         DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_USYM);
3914         DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_UBIN);
3915         DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS_DESC);
3916         DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS);
3917
3918         DUMP_EVENT_STAT (TYPE_RUNTIME, TYPE_JITHELPER);
3919
3920         DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_ASSEMBLY);
3921         DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_METHOD);
3922         DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_STATEMENT);
3923         DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_CLASS);
3924
3925         DUMP_EVENT_STAT (TYPE_META, TYPE_SYNC_POINT);
3926 }
3927
3928
3929
3930 static void
3931 flush_context (ProfContext *ctx)
3932 {
3933         ThreadContext *thread;
3934         /* FIXME: sometimes there are leftovers: indagate */
3935         for (thread = ctx->threads; thread; thread = thread->next) {
3936                 while (thread->stack_id) {
3937                         if (debug)
3938                                 fprintf (outfile, "thread %p has %d items on stack\n", (void*)thread->thread_id, thread->stack_id);
3939                         pop_method (thread, thread->stack [thread->stack_id - 1], thread->last_time);
3940                 }
3941         }
3942 }
3943
3944 static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,heapshot,counters,coverage";
3945
3946 static const char*
3947 match_option (const char *p, const char *opt)
3948 {
3949         int len = strlen (opt);
3950         if (strncmp (p, opt, len) == 0) {
3951                 if (p [len] == ',')
3952                         len++;
3953                 return p + len;
3954         }
3955         return p;
3956 }
3957
3958 static int
3959 print_reports (ProfContext *ctx, const char *reps, int parse_only)
3960 {
3961         const char *opt;
3962         const char *p;
3963         for (p = reps; *p; p = opt) {
3964                 if ((opt = match_option (p, "header")) != p) {
3965                         if (!parse_only)
3966                                 dump_header (ctx);
3967                         continue;
3968                 }
3969                 if ((opt = match_option (p, "thread")) != p) {
3970                         if (!parse_only)
3971                                 dump_threads (ctx);
3972                         continue;
3973                 }
3974                 if ((opt = match_option (p, "domain")) != p) {
3975                         if (!parse_only)
3976                                 dump_domains (ctx);
3977                         continue;
3978                 }
3979                 if ((opt = match_option (p, "context")) != p) {
3980                         if (!parse_only)
3981                                 dump_remctxs (ctx);
3982                         continue;
3983                 }
3984                 if ((opt = match_option (p, "gc")) != p) {
3985                         if (!parse_only)
3986                                 dump_gcs ();
3987                         continue;
3988                 }
3989                 if ((opt = match_option (p, "jit")) != p) {
3990                         if (!parse_only)
3991                                 dump_jit ();
3992                         continue;
3993                 }
3994                 if ((opt = match_option (p, "alloc")) != p) {
3995                         if (!parse_only)
3996                                 dump_allocations ();
3997                         continue;
3998                 }
3999                 if ((opt = match_option (p, "call")) != p) {
4000                         if (!parse_only)
4001                                 dump_methods ();
4002                         continue;
4003                 }
4004                 if ((opt = match_option (p, "metadata")) != p) {
4005                         if (!parse_only)
4006                                 dump_metadata ();
4007                         continue;
4008                 }
4009                 if ((opt = match_option (p, "exception")) != p) {
4010                         if (!parse_only)
4011                                 dump_exceptions ();
4012                         continue;
4013                 }
4014                 if ((opt = match_option (p, "monitor")) != p) {
4015                         if (!parse_only)
4016                                 dump_monitors ();
4017                         continue;
4018                 }
4019                 if ((opt = match_option (p, "heapshot")) != p) {
4020                         if (!parse_only)
4021                                 dump_heap_shots ();
4022                         continue;
4023                 }
4024                 if ((opt = match_option (p, "sample")) != p) {
4025                         if (!parse_only)
4026                                 dump_samples ();
4027                         continue;
4028                 }
4029                 if ((opt = match_option (p, "counters")) != p) {
4030                         if (!parse_only)
4031                                 dump_counters ();
4032                         continue;
4033                 }
4034                 if ((opt = match_option (p, "coverage")) != p) {
4035                         if (!parse_only)
4036                                 dump_coverage ();
4037                         continue;
4038                 }
4039                 if ((opt = match_option (p, "stats")) != p) {
4040                         if (!parse_only)
4041                                 dump_stats ();
4042                         continue;
4043                 }
4044                 return 0;
4045         }
4046         return 1;
4047 }
4048
4049 static int
4050 add_find_spec (const char *p)
4051 {
4052         if (p [0] == 'S' && p [1] == ':') {
4053                 char *vale;
4054                 find_size = strtoul (p + 2, &vale, 10);
4055                 return 1;
4056         } else if (p [0] == 'T' && p [1] == ':') {
4057                 find_name = p + 2;
4058                 return 1;
4059         }
4060         return 0;
4061 }
4062
4063 static void
4064 usage (void)
4065 {
4066         printf ("Mono log profiler report version %d.%d\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR);
4067         printf ("Usage: mprof-report [OPTIONS] FILENAME\n");
4068         printf ("FILENAME can be '-' to read from standard input.\n");
4069         printf ("Options:\n");
4070         printf ("\t--help               display this help\n");
4071         printf ("\t--out=FILE           write to FILE instead of stdout\n");
4072         printf ("\t--traces             collect and show backtraces\n");
4073         printf ("\t--maxframes=NUM      limit backtraces to NUM entries\n");
4074         printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
4075         printf ("\t                     %s\n", reports);
4076         printf ("\t--method-sort=MODE   sort methods according to MODE: total, self, calls\n");
4077         printf ("\t--alloc-sort=MODE    sort allocations according to MODE: bytes, count\n");
4078         printf ("\t--counters-sort=MODE sort counters according to MODE: time, category\n");
4079         printf ("\t                     only accessible in verbose mode\n");
4080         printf ("\t--track=OB1[,OB2...] track what happens to objects OBJ1, O2 etc.\n");
4081         printf ("\t--find=FINDSPEC      find and track objects matching FINFSPEC, where FINDSPEC is:\n");
4082         printf ("\t                     S:minimum_size or T:partial_name\n");
4083         printf ("\t--thread=THREADID    consider just the data for thread THREADID\n");
4084         printf ("\t--time=FROM-TO       consider data FROM seconds from startup up to TO seconds\n");
4085         printf ("\t--verbose            increase verbosity level\n");
4086         printf ("\t--debug              display decoding debug info for mprof-report devs\n");
4087         printf ("\t--coverage-out=FILE  write the coverage info to FILE as XML\n");
4088 }
4089
4090 int
4091 main (int argc, char *argv[])
4092 {
4093         ProfContext *ctx;
4094         int i;
4095         outfile = stdout;
4096         for (i = 1; i < argc; ++i) {
4097                 if (strcmp ("--debug", argv [i]) == 0) {
4098                         debug++;
4099                 } else if (strcmp ("--help", argv [i]) == 0) {
4100                         usage ();
4101                         return 0;
4102                 } else if (strncmp ("--alloc-sort=", argv [i], 13) == 0) {
4103                         const char *val = argv [i] + 13;
4104                         if (strcmp (val, "bytes") == 0) {
4105                                 alloc_sort_mode = ALLOC_SORT_BYTES;
4106                         } else if (strcmp (val, "count") == 0) {
4107                                 alloc_sort_mode = ALLOC_SORT_COUNT;
4108                         } else {
4109                                 usage ();
4110                                 return 1;
4111                         }
4112                 } else if (strncmp ("--method-sort=", argv [i], 14) == 0) {
4113                         const char *val = argv [i] + 14;
4114                         if (strcmp (val, "total") == 0) {
4115                                 method_sort_mode = METHOD_SORT_TOTAL;
4116                         } else if (strcmp (val, "self") == 0) {
4117                                 method_sort_mode = METHOD_SORT_SELF;
4118                         } else if (strcmp (val, "calls") == 0) {
4119                                 method_sort_mode = METHOD_SORT_CALLS;
4120                         } else {
4121                                 usage ();
4122                                 return 1;
4123                         }
4124                 } else if (strncmp ("--counters-sort=", argv [i], 16) == 0) {
4125                         const char *val = argv [i] + 16;
4126                         if (strcmp (val, "time") == 0) {
4127                                 counters_sort_mode = COUNTERS_SORT_TIME;
4128                         } else if (strcmp (val, "category") == 0) {
4129                                 counters_sort_mode = COUNTERS_SORT_CATEGORY;
4130                         } else {
4131                                 usage ();
4132                                 return 1;
4133                         }
4134                 } else if (strncmp ("--reports=", argv [i], 10) == 0) {
4135                         const char *val = argv [i] + 10;
4136                         if (!print_reports (NULL, val, 1)) {
4137                                 usage ();
4138                                 return 1;
4139                         }
4140                         reports = val;
4141                 } else if (strncmp ("--out=", argv [i], 6) == 0) {
4142                         const char *val = argv [i] + 6;
4143                         outfile = fopen (val, "w");
4144                         if (!outfile) {
4145                                 printf ("Cannot open output file: %s\n", val);
4146                                 return 1;
4147                         }
4148                 } else if (strncmp ("--maxframes=", argv [i], 12) == 0) {
4149                         const char *val = argv [i] + 12;
4150                         char *vale;
4151                         trace_max = strtoul (val, &vale, 10);
4152                 } else if (strncmp ("--find=", argv [i], 7) == 0) {
4153                         const char *val = argv [i] + 7;
4154                         if (!add_find_spec (val)) {
4155                                 usage ();
4156                                 return 1;
4157                         }
4158                 } else if (strncmp ("--track=", argv [i], 8) == 0) {
4159                         const char *val = argv [i] + 8;
4160                         char *vale;
4161                         while (*val) {
4162                                 uintptr_t tracked_obj;
4163                                 if (*val == ',') {
4164                                         val++;
4165                                         continue;
4166                                 }
4167                                 tracked_obj = strtoul (val, &vale, 0);
4168                                 found_object (tracked_obj);
4169                                 val = vale;
4170                         }
4171                 } else if (strncmp ("--thread=", argv [i], 9) == 0) {
4172                         const char *val = argv [i] + 9;
4173                         char *vale;
4174                         thread_filter = strtoul (val, &vale, 0);
4175                 } else if (strncmp ("--time=", argv [i], 7) == 0) {
4176                         char *val = pstrdup (argv [i] + 7);
4177                         double from_secs, to_secs;
4178                         char *top = strchr (val, '-');
4179                         if (!top) {
4180                                 usage ();
4181                                 return 1;
4182                         }
4183                         *top++ = 0;
4184                         from_secs = atof (val);
4185                         to_secs = atof (top);
4186                         free (val);
4187                         if (from_secs > to_secs) {
4188                                 usage ();
4189                                 return 1;
4190                         }
4191                         time_from = from_secs * 1000000000;
4192                         time_to = to_secs * 1000000000;
4193                         use_time_filter = 1;
4194                 } else if (strcmp ("--verbose", argv [i]) == 0) {
4195                         verbose++;
4196                 } else if (strcmp ("--traces", argv [i]) == 0) {
4197                         show_traces = 1;
4198                         collect_traces = 1;
4199                 } else if (strncmp ("--coverage-out=", argv [i], 15) == 0) {
4200                         const char *val = argv [i] + 15;
4201                         coverage_outfile = fopen (val, "w");
4202                         if (!coverage_outfile) {
4203                                 printf ("Cannot open output file: %s\n", val);
4204                                 return 1;
4205                         }
4206                 } else {
4207                         break;
4208                 }
4209         }
4210         if (i >= argc) {
4211                 usage ();
4212                 return 2;
4213         }
4214         ctx = load_file (argv [i]);
4215         if (!ctx) {
4216                 printf ("Not a log profiler data file (or unsupported version).\n");
4217                 return 1;
4218         }
4219         while (decode_buffer (ctx));
4220         flush_context (ctx);
4221         if (num_tracked_objects)
4222                 return 0;
4223         print_reports (ctx, reports, 0);
4224         return 0;
4225 }