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