e57211635ee700b0ea1bea48015d8709333b89fd
[mono.git] / mono / profiler / decode.c
1 /*
2  * decode.c: mprof-report program source: decode and analyze the log profiler data
3  *
4  * Authors:
5  *   Paolo Molaro (lupus@ximian.com)
6  *   Alex Rønne Petersen (alexrp@xamarin.com)
7  *
8  * Copyright 2010 Novell, Inc (http://www.novell.com)
9  */
10
11 /*
12  * The Coverage XML output schema
13  * <coverage>
14  *   <assembly/>
15  *   <class/>
16  *   <method>
17  *     <statement/>
18  *   </method>
19  * </coverage>
20  *
21  * Elements:
22  *   <coverage> - The root element of the documentation. It can contain any number of
23  *                <assembly>, <class> or <method> elements.
24  *                Attributes:
25  *                   - version: The version number for the file format - (eg: "0.3")
26  *   <assembly> - Contains data about assemblies. Has no child elements
27  *                Attributes:
28  *                   - name: The name of the assembly - (eg: "System.Xml")
29  *                   - guid: The GUID of the assembly
30  *                   - filename: The filename of the assembly
31  *                   - method-count: The number of methods in the assembly
32  *                   - full: The number of fully covered methods
33  *                   - partial: The number of partially covered methods
34  *   <class> - Contains data about classes. Has no child elements
35  *             Attributes:
36  *                - name: The name of the class
37  *                - method-count: The number of methods in the class
38  *                - full: The number of fully covered methods
39  *                - partial: The number of partially covered methods
40  *   <method> - Contains data about methods. Can contain any number of <statement> elements
41  *              Attributes:
42  *                 - assembly: The name of the parent assembly
43  *                 - class: The name of the parent class
44  *                 - name: The name of the method, with all it's parameters
45  *                 - filename: The name of the source file containing this method
46  *                 - token
47  *   <statement> - Contains data about IL statements. Has no child elements
48  *                 Attributes:
49  *                    - offset: The offset of the statement in the IL code after the previous
50  *                              statement's offset
51  *                    - counter: 1 if the line was covered, 0 if it was not
52  *                    - line: The line number in the parent method's file
53  *                    - column: The column on the line
54  */
55 #include <config.h>
56 #include "utils.c"
57 #include "proflog.h"
58 #include <string.h>
59 #include <assert.h>
60 #include <stdio.h>
61 #if !defined(__APPLE__) && !defined(__FreeBSD__)
62 #include <malloc.h>
63 #endif
64 #include <unistd.h>
65 #include <stdlib.h>
66 #if defined (HAVE_SYS_ZLIB)
67 #include <zlib.h>
68 #endif
69 #include <glib.h>
70 #include <mono/metadata/profiler.h>
71 #include <mono/metadata/object.h>
72 #include <mono/metadata/debug-helpers.h>
73 #include <mono/utils/mono-counters.h>
74
75 #define HASH_SIZE 9371
76 #define SMALL_HASH_SIZE 31
77
78 #if defined(__native_client__) || defined(__native_client_codegen__)
79 volatile int __nacl_thread_suspension_needed = 0;
80 void __nacl_suspend_thread_if_needed() {}
81 #endif
82
83 static int debug = 0;
84 static int collect_traces = 0;
85 static int show_traces = 0;
86 static int trace_max = 6;
87 static int verbose = 0;
88 static uintptr_t *tracked_objects = 0;
89 static int num_tracked_objects = 0;
90 static uintptr_t thread_filter = 0;
91 static uint64_t find_size = 0;
92 static const char* find_name = NULL;
93 static uint64_t time_from = 0;
94 static uint64_t time_to = 0xffffffffffffffffULL;
95 static int use_time_filter = 0;
96 static uint64_t startup_time = 0;
97 static FILE* outfile = NULL;
98 static FILE* coverage_outfile = NULL;
99
100 static int32_t
101 read_int16 (unsigned char *p)
102 {
103         int32_t value = *p++;
104         value |= (*p++) << 8;
105         return value;
106 }
107
108 static int32_t
109 read_int32 (unsigned char *p)
110 {
111         int32_t value = *p++;
112         value |= (*p++) << 8;
113         value |= (*p++) << 16;
114         value |= (uint32_t)(*p++) << 24;
115         return value;
116 }
117
118 static int64_t
119 read_int64 (unsigned char *p)
120 {
121         uint64_t value = *p++;
122         value |= (*p++) << 8;
123         value |= (*p++) << 16;
124         value |= (uint64_t)(*p++) << 24;
125         value |= (uint64_t)(*p++) << 32;
126         value |= (uint64_t)(*p++) << 40;
127         value |= (uint64_t)(*p++) << 48;
128         value |= (uint64_t)(*p++) << 54;
129         return value;
130 }
131
132 static char*
133 pstrdup (const char *s)
134 {
135         int len = strlen (s) + 1;
136         char *p = (char *)malloc (len);
137         memcpy (p, s, len);
138         return p;
139 }
140
141 typedef struct _CounterValue CounterValue;
142 struct _CounterValue {
143         uint64_t timestamp;
144         unsigned char *buffer;
145         CounterValue *next;
146 };
147
148 typedef struct _Counter Counter;
149 struct _Counter {
150         int index;
151         const char *section;
152         const char *name;
153         int type;
154         int unit;
155         int variance;
156         CounterValue *values;
157         CounterValue *values_last;
158 };
159
160 typedef struct _CounterList CounterList;
161 struct _CounterList {
162         Counter *counter;
163         CounterList *next;
164 };
165
166 typedef struct _CounterSection CounterSection;
167 struct _CounterSection {
168         const char *value;
169         CounterList *counters;
170         CounterList *counters_last;
171         CounterSection *next;
172 };
173
174 typedef struct _CounterTimestamp CounterTimestamp;
175 struct _CounterTimestamp {
176         uint64_t value;
177         CounterSection *sections;
178         CounterSection *sections_last;
179         CounterTimestamp *next;
180 };
181
182 static CounterList *counters = NULL;
183 static CounterSection *counters_sections = NULL;
184 static CounterTimestamp *counters_timestamps = NULL;
185
186 enum {
187         COUNTERS_SORT_TIME,
188         COUNTERS_SORT_CATEGORY
189 };
190
191 static int counters_sort_mode = COUNTERS_SORT_TIME;
192
193 static void
194 add_counter_to_section (Counter *counter)
195 {
196         CounterSection *csection, *s;
197         CounterList *clist;
198
199         clist = (CounterList *)calloc (1, sizeof (CounterList));
200         clist->counter = counter;
201
202         for (csection = counters_sections; csection; csection = csection->next) {
203                 if (strcmp (csection->value, counter->section) == 0) {
204                         /* If section exist */
205                         if (!csection->counters)
206                                 csection->counters = clist;
207                         else
208                                 csection->counters_last->next = clist;
209                         csection->counters_last = clist;
210                         return;
211                 }
212         }
213
214         /* If section does not exist */
215         csection = (CounterSection *)calloc (1, sizeof (CounterSection));
216         csection->value = counter->section;
217         csection->counters = clist;
218         csection->counters_last = clist;
219
220         if (!counters_sections) {
221                 counters_sections = csection;
222         } else {
223                 s = counters_sections;
224                 while (s->next)
225                         s = s->next;
226                 s->next = csection;
227         }
228 }
229
230 static void
231 add_counter (const char *section, const char *name, int type, int unit, int variance, int index)
232 {
233         CounterList *list, *l;
234         Counter *counter;
235
236         for (list = counters; list; list = list->next)
237                 if (list->counter->index == index)
238                         return;
239
240         counter = (Counter *)calloc (1, sizeof (Counter));
241         counter->section = section;
242         counter->name = name;
243         counter->type = type;
244         counter->unit = unit;
245         counter->variance = variance;
246         counter->index = index;
247
248         list = (CounterList *)calloc (1, sizeof (CounterList));
249         list->counter = counter;
250
251         if (!counters) {
252                 counters = list;
253         } else {
254                 l = counters;
255                 while (l->next)
256                         l = l->next;
257                 l->next = list;
258         }
259
260         if (counters_sort_mode == COUNTERS_SORT_CATEGORY || !verbose)
261                 add_counter_to_section (counter);
262 }
263
264 static void
265 add_counter_to_timestamp (uint64_t timestamp, Counter *counter)
266 {
267         CounterTimestamp *ctimestamp, *t;
268         CounterSection *csection;
269         CounterList *clist;
270
271         clist = (CounterList *)calloc (1, sizeof (CounterList));
272         clist->counter = counter;
273
274         for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
275                 if (ctimestamp->value == timestamp) {
276                         for (csection = ctimestamp->sections; csection; csection = csection->next) {
277                                 if (strcmp (csection->value, counter->section) == 0) {
278                                         /* if timestamp exist and section exist */
279                                         if (!csection->counters)
280                                                 csection->counters = clist;
281                                         else
282                                                 csection->counters_last->next = clist;
283                                         csection->counters_last = clist;
284                                         return;
285                                 }
286                         }
287
288                         /* if timestamp exist and section does not exist */
289                         csection = (CounterSection *)calloc (1, sizeof (CounterSection));
290                         csection->value = counter->section;
291                         csection->counters = clist;
292                         csection->counters_last = clist;
293
294                         if (!ctimestamp->sections)
295                                 ctimestamp->sections = csection;
296                         else
297                                 ctimestamp->sections_last->next = csection;
298                         ctimestamp->sections_last = csection;
299                         return;
300                 }
301         }
302
303         /* If timestamp do not exist and section does not exist */
304         csection = (CounterSection *)calloc (1, sizeof (CounterSection));
305         csection->value = counter->section;
306         csection->counters = clist;
307         csection->counters_last = clist;
308
309         ctimestamp = (CounterTimestamp *)calloc (1, sizeof (CounterTimestamp));
310         ctimestamp->value = timestamp;
311         ctimestamp->sections = csection;
312         ctimestamp->sections_last = csection;
313
314         if (!counters_timestamps) {
315                 counters_timestamps = ctimestamp;
316         } else {
317                 t = counters_timestamps;
318                 while (t->next)
319                         t = t->next;
320                 t->next = ctimestamp;
321         }
322 }
323
324 static void
325 add_counter_value (int index, CounterValue *value)
326 {
327         CounterList *list;
328
329         for (list = counters; list; list = list->next) {
330                 if (list->counter->index == index) {
331                         if (!list->counter->values)
332                                 list->counter->values = value;
333                         else
334                                 list->counter->values_last->next = value;
335                         list->counter->values_last = value;
336
337                         if (counters_sort_mode == COUNTERS_SORT_TIME)
338                                 add_counter_to_timestamp (value->timestamp, list->counter);
339
340                         return;
341                 }
342         }
343 }
344
345 static const char*
346 section_name (int section)
347 {
348         switch (section) {
349         case MONO_COUNTER_JIT: return "Mono JIT";
350         case MONO_COUNTER_GC: return "Mono GC";
351         case MONO_COUNTER_METADATA: return "Mono Metadata";
352         case MONO_COUNTER_GENERICS: return "Mono Generics";
353         case MONO_COUNTER_SECURITY: return "Mono Security";
354         case MONO_COUNTER_RUNTIME: return "Mono Runtime";
355         case MONO_COUNTER_SYSTEM: return "Mono System";
356         default: return "<unknown>";
357         }
358 }
359
360 static const char*
361 type_name (int type)
362 {
363         switch (type) {
364         case MONO_COUNTER_INT: return "Int";
365         case MONO_COUNTER_UINT: return "UInt";
366         case MONO_COUNTER_WORD: return "Word";
367         case MONO_COUNTER_LONG: return "Long";
368         case MONO_COUNTER_ULONG: return "ULong";
369         case MONO_COUNTER_DOUBLE: return "Double";
370         case MONO_COUNTER_STRING: return "String";
371         case MONO_COUNTER_TIME_INTERVAL: return "Time Interval";
372         default: return "<unknown>";
373         }
374 }
375
376 static const char*
377 unit_name (int unit)
378 {
379         switch (unit) {
380         case MONO_COUNTER_RAW: return "Raw";
381         case MONO_COUNTER_BYTES: return "Bytes";
382         case MONO_COUNTER_TIME: return "Time";
383         case MONO_COUNTER_COUNT: return "Count";
384         case MONO_COUNTER_PERCENTAGE: return "Percentage";
385         default: return "<unknown>";
386         }
387 }
388
389 static const char*
390 variance_name (int variance)
391 {
392         switch (variance) {
393         case MONO_COUNTER_MONOTONIC: return "Monotonic";
394         case MONO_COUNTER_CONSTANT: return "Constant";
395         case MONO_COUNTER_VARIABLE: return "Variable";
396         default: return "<unknown>";
397         }
398 }
399
400 static void
401 dump_counters_value (Counter *counter, const char *key_format, const char *key, void *value)
402 {
403         char format[32];
404
405         if (value == NULL) {
406                 snprintf (format, sizeof (format), "%s : %%s\n", key_format);
407                 fprintf (outfile, format, key, "<null>");
408         } else {
409                 switch (counter->type) {
410                 case MONO_COUNTER_INT:
411 #if SIZEOF_VOID_P == 4
412                 case MONO_COUNTER_WORD:
413 #endif
414                         snprintf (format, sizeof (format), "%s : %%d\n", key_format);
415                         fprintf (outfile, format, key, *(int32_t*)value);
416                         break;
417                 case MONO_COUNTER_UINT:
418                         snprintf (format, sizeof (format), "%s : %%u\n", key_format);
419                         fprintf (outfile, format, key, *(uint32_t*)value);
420                         break;
421                 case MONO_COUNTER_LONG:
422 #if SIZEOF_VOID_P == 8
423                 case MONO_COUNTER_WORD:
424 #endif
425                 case MONO_COUNTER_TIME_INTERVAL:
426                         if (counter->type == MONO_COUNTER_LONG && counter->unit == MONO_COUNTER_TIME) {
427                                 snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
428                                 fprintf (outfile, format, key, (double)*(int64_t*)value / 10000.0);
429                         } else if (counter->type == MONO_COUNTER_TIME_INTERVAL) {
430                                 snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
431                                 fprintf (outfile, format, key, (double)*(int64_t*)value / 1000.0);
432                         } else {
433                                 snprintf (format, sizeof (format), "%s : %%u\n", key_format);
434                                 fprintf (outfile, format, key, *(int64_t*)value);
435                         }
436                         break;
437                 case MONO_COUNTER_ULONG:
438                         snprintf (format, sizeof (format), "%s : %%llu\n", key_format);
439                         fprintf (outfile, format, key, *(uint64_t*)value);
440                         break;
441                 case MONO_COUNTER_DOUBLE:
442                         snprintf (format, sizeof (format), "%s : %%f\n", key_format);
443                         fprintf (outfile, format, key, *(double*)value);
444                         break;
445                 case MONO_COUNTER_STRING:
446                         snprintf (format, sizeof (format), "%s : %%s\n", key_format);
447                         fprintf (outfile, format, key, *(char*)value);
448                         break;
449                 }
450         }
451 }
452
453 static void
454 dump_counters (void)
455 {
456         Counter *counter;
457         CounterValue *cvalue;
458         CounterTimestamp *ctimestamp;
459         CounterSection *csection;
460         CounterList *clist;
461         char strtimestamp[17];
462         int i, section_printed;
463
464         fprintf (outfile, "\nCounters:\n");
465
466         if (!verbose) {
467                 char counters_to_print[][64] = {
468                         "Methods from AOT",
469                         "Methods JITted using mono JIT",
470                         "Methods JITted using LLVM",
471                         "Total time spent JITting (sec)",
472                         "User Time",
473                         "System Time",
474                         "Total Time",
475                         "Working Set",
476                         "Private Bytes",
477                         "Virtual Bytes",
478                         "Page Faults",
479                         "CPU Load Average - 1min",
480                         "CPU Load Average - 5min",
481                         "CPU Load Average - 15min",
482                         ""
483                 };
484
485                 for (csection = counters_sections; csection; csection = csection->next) {
486                         section_printed = 0;
487
488                         for (clist = csection->counters; clist; clist = clist->next) {
489                                 counter = clist->counter;
490                                 if (!counter->values_last)
491                                         continue;
492
493                                 for (i = 0; counters_to_print [i][0] != 0; i++) {
494                                         if (strcmp (counters_to_print [i], counter->name) == 0) {
495                                                 if (!section_printed) {
496                                                         fprintf (outfile, "\t%s:\n", csection->value);
497                                                         section_printed = 1;
498                                                 }
499
500                                                 dump_counters_value (counter, "\t\t%-30s", counter->name, counter->values_last->buffer);
501                                                 break;
502                                         }
503                                 }
504                         }
505                 }
506         } else if (counters_sort_mode == COUNTERS_SORT_TIME) {
507                 for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
508                         fprintf (outfile, "\t%llu:%02llu:%02llu:%02llu.%03llu:\n",
509                                 (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 / 24 % 1000),
510                                 (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 % 24),
511                                 (unsigned long long) (ctimestamp->value / 1000 / 60 % 60),
512                                 (unsigned long long) (ctimestamp->value / 1000 % 60),
513                                 (unsigned long long) (ctimestamp->value % 1000));
514
515                         for (csection = ctimestamp->sections; csection; csection = csection->next) {
516                                 fprintf (outfile, "\t\t%s:\n", csection->value);
517
518                                 for (clist = csection->counters; clist; clist = clist->next) {
519                                         counter = clist->counter;
520                                         for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
521                                                 if (cvalue->timestamp != ctimestamp->value)
522                                                         continue;
523
524                                                 dump_counters_value (counter, "\t\t\t%-30s", counter->name, cvalue->buffer);
525                                         }
526                                 }
527                         }
528                 }
529         } else if (counters_sort_mode == COUNTERS_SORT_CATEGORY) {
530                 for (csection = counters_sections; csection; csection = csection->next) {
531                         fprintf (outfile, "\t%s:\n", csection->value);
532
533                         for (clist = csection->counters; clist; clist = clist->next) {
534                                 counter = clist->counter;
535                                 fprintf (outfile, "\t\t%s: [type: %s, unit: %s, variance: %s]\n",
536                                         counter->name, type_name (counter->type), unit_name (counter->unit), variance_name (counter->variance));
537
538                                 for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
539                                         snprintf (strtimestamp, sizeof (strtimestamp), "%llu:%02llu:%02llu:%02llu.%03llu",
540                                                 (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 / 24 % 1000),
541                                                 (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 % 24),
542                                                 (unsigned long long) (cvalue->timestamp / 1000 / 60 % 60),
543                                                 (unsigned long long) (cvalue->timestamp / 1000 % 60),
544                                                 (unsigned long long) (cvalue->timestamp % 1000));
545
546                                         dump_counters_value (counter, "\t\t\t%s", strtimestamp, cvalue->buffer);
547                                 }
548                         }
549                 }
550         }
551 }
552
553 static int num_images;
554 typedef struct _ImageDesc ImageDesc;
555 struct _ImageDesc {
556         ImageDesc *next;
557         intptr_t image;
558         char *filename;
559 };
560
561 static ImageDesc* image_hash [SMALL_HASH_SIZE] = {0};
562
563 static void
564 add_image (intptr_t image, char *name)
565 {
566         int slot = ((image >> 2) & 0xffff) % SMALL_HASH_SIZE;
567         ImageDesc *cd = (ImageDesc *)malloc (sizeof (ImageDesc));
568         cd->image = image;
569         cd->filename = pstrdup (name);
570         cd->next = image_hash [slot];
571         image_hash [slot] = cd;
572         num_images++;
573 }
574
575 static int num_assemblies;
576
577 typedef struct _AssemblyDesc AssemblyDesc;
578 struct _AssemblyDesc {
579         AssemblyDesc *next;
580         intptr_t assembly;
581         char *asmname;
582 };
583
584 static AssemblyDesc* assembly_hash [SMALL_HASH_SIZE] = {0};
585
586 static void
587 add_assembly (intptr_t assembly, char *name)
588 {
589         int slot = ((assembly >> 2) & 0xffff) % SMALL_HASH_SIZE;
590         AssemblyDesc *cd = (AssemblyDesc *)malloc (sizeof (AssemblyDesc));
591         cd->assembly = assembly;
592         cd->asmname = pstrdup (name);
593         cd->next = assembly_hash [slot];
594         assembly_hash [slot] = cd;
595         num_assemblies++;
596 }
597
598 typedef struct _BackTrace BackTrace;
599 typedef struct {
600         uint64_t count;
601         BackTrace *bt;
602 } CallContext;
603
604 typedef struct {
605         int count;
606         int size;
607         CallContext *traces;
608 } TraceDesc;
609
610 typedef struct _ClassDesc ClassDesc;
611 struct _ClassDesc {
612         ClassDesc *next;
613         intptr_t klass;
614         char *name;
615         intptr_t allocs;
616         uint64_t alloc_size;
617         TraceDesc traces;
618 };
619
620 static ClassDesc* class_hash [HASH_SIZE] = {0};
621 static int num_classes = 0;
622
623 static ClassDesc*
624 add_class (intptr_t klass, const char *name)
625 {
626         int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
627         ClassDesc *cd;
628         cd = class_hash [slot];
629         while (cd && cd->klass != klass)
630                 cd = cd->next;
631         /* we resolved an unknown class (unless we had the code unloaded) */
632         if (cd) {
633                 /*printf ("resolved unknown: %s\n", name);*/
634                 free (cd->name);
635                 cd->name = pstrdup (name);
636                 return cd;
637         }
638         cd = (ClassDesc *)calloc (sizeof (ClassDesc), 1);
639         cd->klass = klass;
640         cd->name = pstrdup (name);
641         cd->next = class_hash [slot];
642         cd->allocs = 0;
643         cd->alloc_size = 0;
644         cd->traces.count = 0;
645         cd->traces.size = 0;
646         cd->traces.traces = NULL;
647         class_hash [slot] = cd;
648         num_classes++;
649         return cd;
650 }
651
652 static ClassDesc *
653 lookup_class (intptr_t klass)
654 {
655         int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
656         ClassDesc *cd = class_hash [slot];
657         while (cd && cd->klass != klass)
658                 cd = cd->next;
659         if (!cd) {
660                 char buf [128];
661                 snprintf (buf, sizeof (buf), "unresolved class %p", (void*)klass);
662                 return add_class (klass, buf);
663         }
664         return cd;
665 }
666
667 typedef struct _MethodDesc MethodDesc;
668 struct _MethodDesc {
669         MethodDesc *next;
670         intptr_t method;
671         char *name;
672         intptr_t code;
673         int len;
674         int recurse_count;
675         int sample_hits;
676         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 */
677         uint64_t calls;
678         uint64_t total_time;
679         uint64_t callee_time;
680         uint64_t self_time;
681         TraceDesc traces;
682 };
683
684 static MethodDesc* method_hash [HASH_SIZE] = {0};
685 static int num_methods = 0;
686
687 static MethodDesc*
688 add_method (intptr_t method, const char *name, intptr_t code, int len)
689 {
690         int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
691         MethodDesc *cd;
692         cd = method_hash [slot];
693         while (cd && cd->method != method)
694                 cd = cd->next;
695         /* we resolved an unknown method (unless we had the code unloaded) */
696         if (cd) {
697                 cd->code = code;
698                 cd->len = len;
699                 /*printf ("resolved unknown: %s\n", name);*/
700                 free (cd->name);
701                 cd->name = pstrdup (name);
702                 return cd;
703         }
704         cd = (MethodDesc *)calloc (sizeof (MethodDesc), 1);
705         cd->method = method;
706         cd->name = pstrdup (name);
707         cd->code = code;
708         cd->len = len;
709         cd->calls = 0;
710         cd->total_time = 0;
711         cd->traces.count = 0;
712         cd->traces.size = 0;
713         cd->traces.traces = NULL;
714         cd->next = method_hash [slot];
715         method_hash [slot] = cd;
716         num_methods++;
717         return cd;
718 }
719
720 static MethodDesc *
721 lookup_method (intptr_t method)
722 {
723         int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
724         MethodDesc *cd = method_hash [slot];
725         while (cd && cd->method != method)
726                 cd = cd->next;
727         if (!cd) {
728                 char buf [128];
729                 snprintf (buf, sizeof (buf), "unknown method %p", (void*)method);
730                 return add_method (method, buf, 0, 0);
731         }
732         return cd;
733 }
734
735 static int num_stat_samples = 0;
736 static int size_stat_samples = 0;
737 uintptr_t *stat_samples = NULL;
738 int *stat_sample_desc = NULL;
739
740 static void
741 add_stat_sample (int type, uintptr_t ip) {
742         if (num_stat_samples == size_stat_samples) {
743                 size_stat_samples *= 2;
744                 if (!size_stat_samples)
745                 size_stat_samples = 32;
746                 stat_samples = (uintptr_t *)realloc (stat_samples, size_stat_samples * sizeof (uintptr_t));
747                 stat_sample_desc = (int *)realloc (stat_sample_desc, size_stat_samples * sizeof (int));
748         }
749         stat_samples [num_stat_samples] = ip;
750         stat_sample_desc [num_stat_samples++] = type;
751 }
752
753 static MethodDesc*
754 lookup_method_by_ip (uintptr_t ip)
755 {
756         int i;
757         MethodDesc* m;
758         /* dumb */
759         for (i = 0; i < HASH_SIZE; ++i) {
760                 m = method_hash [i];
761                 while (m) {
762                         //printf ("checking %p against %p-%p\n", (void*)ip, (void*)(m->code), (void*)(m->code + m->len));
763                         if (ip >= (uintptr_t)m->code && ip < (uintptr_t)m->code + m->len) {
764                                 return m;
765                         }
766                         m = m->next;
767                 }
768         }
769         return NULL;
770 }
771
772 static int
773 compare_method_samples (const void *a, const void *b)
774 {
775         MethodDesc *const *A = (MethodDesc *const *)a;
776         MethodDesc *const *B = (MethodDesc *const *)b;
777         if ((*A)->sample_hits == (*B)->sample_hits)
778                 return 0;
779         if ((*B)->sample_hits < (*A)->sample_hits)
780                 return -1;
781         return 1;
782 }
783
784 typedef struct _UnmanagedSymbol UnmanagedSymbol;
785 struct _UnmanagedSymbol {
786         UnmanagedSymbol *parent;
787         char *name;
788         int is_binary;
789         uintptr_t addr;
790         uintptr_t size;
791         uintptr_t sample_hits;
792 };
793
794 static UnmanagedSymbol **usymbols = NULL;
795 static int usymbols_size = 0;
796 static int usymbols_num = 0;
797
798 static int
799 compare_usymbol_addr (const void *a, const void *b)
800 {
801         UnmanagedSymbol *const *A = (UnmanagedSymbol *const *)a;
802         UnmanagedSymbol *const *B = (UnmanagedSymbol *const *)b;
803         if ((*B)->addr == (*A)->addr)
804                 return 0;
805         if ((*B)->addr > (*A)->addr)
806                 return -1;
807         return 1;
808 }
809
810 static int
811 compare_usymbol_samples (const void *a, const void *b)
812 {
813         UnmanagedSymbol *const *A = (UnmanagedSymbol *const *)a;
814         UnmanagedSymbol *const *B = (UnmanagedSymbol *const *)b;
815         if ((*B)->sample_hits == (*A)->sample_hits)
816                 return 0;
817         if ((*B)->sample_hits < (*A)->sample_hits)
818                 return -1;
819         return 1;
820 }
821
822 static void
823 add_unmanaged_symbol (uintptr_t addr, char *name, uintptr_t size)
824 {
825         UnmanagedSymbol *sym;
826         if (usymbols_num == usymbols_size) {
827                 int new_size = usymbols_size * 2;
828                 if (!new_size)
829                         new_size = 16;
830                 usymbols = (UnmanagedSymbol **)realloc (usymbols, sizeof (void*) * new_size);
831                 usymbols_size = new_size;
832         }
833         sym = (UnmanagedSymbol *)calloc (sizeof (UnmanagedSymbol), 1);
834         sym->addr = addr;
835         sym->name = name;
836         sym->size = size;
837         usymbols [usymbols_num++] = sym;
838 }
839
840 /* only valid after the symbols are sorted */
841 static UnmanagedSymbol*
842 lookup_unmanaged_symbol (uintptr_t addr)
843 {
844         int r = usymbols_num - 1;
845         int l = 0;
846         UnmanagedSymbol *sym;
847         int last_best = -1;
848         while (r >= l) {
849                 int m = (l + r) / 2;
850                 sym = usymbols [m];
851                 if (addr == sym->addr)
852                         return sym;
853                 if (addr < sym->addr) {
854                         r = m - 1;
855                 } else if (addr > sym->addr) {
856                         l = m + 1;
857                         last_best = m;
858                 }
859         }
860         if (last_best >= 0 && (addr - usymbols [last_best]->addr) < 4096)
861                 return usymbols [last_best];
862         return NULL;
863 }
864
865 /* we use the same structure for binaries */
866 static UnmanagedSymbol **ubinaries = NULL;
867 static int ubinaries_size = 0;
868 static int ubinaries_num = 0;
869
870 static void
871 add_unmanaged_binary (uintptr_t addr, char *name, uintptr_t size)
872 {
873         UnmanagedSymbol *sym;
874         if (ubinaries_num == ubinaries_size) {
875                 int new_size = ubinaries_size * 2;
876                 if (!new_size)
877                         new_size = 16;
878                 ubinaries = (UnmanagedSymbol **)realloc (ubinaries, sizeof (void*) * new_size);
879                 ubinaries_size = new_size;
880         }
881         sym = (UnmanagedSymbol *)calloc (sizeof (UnmanagedSymbol), 1);
882         sym->addr = addr;
883         sym->name = name;
884         sym->size = size;
885         sym->is_binary = 1;
886         ubinaries [ubinaries_num++] = sym;
887 }
888
889 static UnmanagedSymbol*
890 lookup_unmanaged_binary (uintptr_t addr)
891 {
892         int i;
893         for (i = 0; i < ubinaries_num; ++i) {
894                 UnmanagedSymbol *ubin = ubinaries [i];
895                 if (addr >= ubin->addr && addr < ubin->addr + ubin->size) {
896                         return ubin;
897                 }
898         }
899         return NULL;
900 }
901
902 static const char*
903 sample_type_name (int type)
904 {
905         switch (type) {
906         case SAMPLE_CYCLES: return "cycles";
907         case SAMPLE_INSTRUCTIONS: return "instructions retired";
908         case SAMPLE_CACHE_MISSES: return "cache misses";
909         case SAMPLE_CACHE_REFS: return "cache references";
910         case SAMPLE_BRANCHES: return "executed branches";
911         case SAMPLE_BRANCH_MISSES: return "unpredicted branches";
912         }
913         return "unknown";
914 }
915
916 static void
917 set_usym_parent (UnmanagedSymbol** cachedus, int count)
918 {
919         int i;
920         for (i = 0; i < count; ++i) {
921                 UnmanagedSymbol *ubin = lookup_unmanaged_binary (cachedus [i]->addr);
922                 if (ubin == cachedus [i])
923                         continue;
924                 cachedus [i]->parent = ubin;
925         }
926 }
927
928 static void
929 print_usym (UnmanagedSymbol* um)
930 {
931         if (um->parent)
932                 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);
933         else
934                 fprintf (outfile, "\t%6zd %6.2f %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name);
935 }
936
937 static int
938 sym_percent (uintptr_t sample_hits)
939 {
940         double pc;
941         if (verbose)
942                 return 1;
943         pc = sample_hits*100.0/num_stat_samples;
944         return pc >= 0.1;
945 }
946
947 static void
948 dump_samples (void)
949 {
950         int i, u;
951         int count = 0, msize = 0;
952         int unmanaged_hits = 0;
953         int unresolved_hits = 0;
954         MethodDesc** cachedm = NULL;
955         int ucount = 0, usize = 0;
956         UnmanagedSymbol** cachedus = NULL;
957         if (!num_stat_samples)
958                 return;
959         qsort (usymbols, usymbols_num, sizeof (UnmanagedSymbol*), compare_usymbol_addr);
960         for (i = 0; i < num_stat_samples; ++i) {
961                 MethodDesc *m = lookup_method_by_ip (stat_samples [i]);
962                 if (m) {
963                         if (!m->sample_hits) {
964                                 if (count == msize) {
965                                         msize *= 2;
966                                         if (!msize)
967                                                 msize = 4;
968                                         cachedm = (MethodDesc **)realloc (cachedm, sizeof (void*) * msize);
969                                 }
970                                 cachedm [count++] = m;
971                         }
972                         m->sample_hits++;
973                 } else {
974                         UnmanagedSymbol *usym = lookup_unmanaged_symbol (stat_samples [i]);
975                         if (!usym) {
976                                 unresolved_hits++;
977                                 //printf ("unmanaged hit at %p\n", (void*)stat_samples [i]);
978                                 usym = lookup_unmanaged_binary (stat_samples [i]);
979                         }
980                         if (usym) {
981                                 if (!usym->sample_hits) {
982                                         if (ucount == usize) {
983                                                 usize *= 2;
984                                                 if (!usize)
985                                                         usize = 4;
986                                                 cachedus = (UnmanagedSymbol **)realloc (cachedus, sizeof (void*) * usize);
987                                         }
988                                         cachedus [ucount++] = usym;
989                                 }
990                                 usym->sample_hits++;
991                         }
992                         unmanaged_hits++;
993                 }
994         }
995         qsort (cachedm, count, sizeof (MethodDesc*), compare_method_samples);
996         qsort (cachedus, ucount, sizeof (UnmanagedSymbol*), compare_usymbol_samples);
997         set_usym_parent (cachedus, ucount);
998         fprintf (outfile, "\nStatistical samples summary\n");
999         fprintf (outfile, "\tSample type: %s\n", sample_type_name (stat_sample_desc [0]));
1000         fprintf (outfile, "\tUnmanaged hits:  %6d (%4.1f%%)\n", unmanaged_hits, (100.0*unmanaged_hits)/num_stat_samples);
1001         fprintf (outfile, "\tManaged hits:    %6d (%4.1f%%)\n", num_stat_samples - unmanaged_hits, (100.0*(num_stat_samples-unmanaged_hits))/num_stat_samples);
1002         fprintf (outfile, "\tUnresolved hits: %6d (%4.1f%%)\n", unresolved_hits, (100.0*unresolved_hits)/num_stat_samples);
1003         fprintf (outfile, "\t%6s %6s %s\n", "Hits", "%", "Method name");
1004         i = 0;
1005         u = 0;
1006         while (i < count || u < ucount) {
1007                 if (i < count) {
1008                         MethodDesc *m = cachedm [i];
1009                         if (u < ucount) {
1010                                 UnmanagedSymbol *um = cachedus [u];
1011                                 if (um->sample_hits > m->sample_hits) {
1012                                         if (!sym_percent (um->sample_hits))
1013                                                 break;
1014                                         print_usym (um);
1015                                         u++;
1016                                         continue;
1017                                 }
1018                         }
1019                         if (!sym_percent (m->sample_hits))
1020                                 break;
1021                         fprintf (outfile, "\t%6d %6.2f %s\n", m->sample_hits, m->sample_hits*100.0/num_stat_samples, m->name);
1022                         i++;
1023                         continue;
1024                 }
1025                 if (u < ucount) {
1026                         UnmanagedSymbol *um = cachedus [u];
1027                         if (!sym_percent (um->sample_hits))
1028                                 break;
1029                         print_usym (um);
1030                         u++;
1031                         continue;
1032                 }
1033         }
1034 }
1035
1036 typedef struct _HeapClassDesc HeapClassDesc;
1037 typedef struct {
1038         HeapClassDesc *klass;
1039         uint64_t count;
1040 } HeapClassRevRef;
1041
1042 struct _HeapClassDesc {
1043         ClassDesc *klass;
1044         int64_t count;
1045         int64_t total_size;
1046         HeapClassRevRef *rev_hash;
1047         int rev_hash_size;
1048         int rev_count;
1049         uintptr_t pinned_references;
1050         uintptr_t root_references;
1051 };
1052
1053 static int
1054 add_rev_class_hashed (HeapClassRevRef *rev_hash, uintptr_t size, HeapClassDesc *hklass, uint64_t value)
1055 {
1056         uintptr_t i;
1057         uintptr_t start_pos;
1058         start_pos = (hklass->klass->klass >> 2) % size;
1059         assert (start_pos < size);
1060         i = start_pos;
1061         do {
1062                 if (rev_hash [i].klass == hklass) {
1063                         rev_hash [i].count += value;
1064                         return 0;
1065                 } else if (!rev_hash [i].klass) {
1066                         rev_hash [i].klass = hklass;
1067                         rev_hash [i].count += value;
1068                         start_pos = 0;
1069                         for (i = 0; i < size; ++i)
1070                                 if (rev_hash [i].klass && rev_hash [i].klass->klass == hklass->klass)
1071                                         start_pos ++;
1072                         assert (start_pos == 1);
1073                         return 1;
1074                 }
1075                 /* wrap around */
1076                 if (++i == size)
1077                         i = 0;
1078         } while (i != start_pos);
1079         /* should not happen */
1080         printf ("failed revref store\n");
1081         return 0;
1082 }
1083
1084 static void
1085 add_heap_class_rev (HeapClassDesc *from, HeapClassDesc *to)
1086 {
1087         uintptr_t i;
1088         if (to->rev_count * 2 >= to->rev_hash_size) {
1089                 HeapClassRevRef *n;
1090                 uintptr_t old_size = to->rev_hash_size;
1091                 to->rev_hash_size *= 2;
1092                 if (to->rev_hash_size == 0)
1093                         to->rev_hash_size = 4;
1094                 n = (HeapClassRevRef *)calloc (sizeof (HeapClassRevRef) * to->rev_hash_size, 1);
1095                 for (i = 0; i < old_size; ++i) {
1096                         if (to->rev_hash [i].klass)
1097                                 add_rev_class_hashed (n, to->rev_hash_size, to->rev_hash [i].klass, to->rev_hash [i].count);
1098                 }
1099                 if (to->rev_hash)
1100                         free (to->rev_hash);
1101                 to->rev_hash = n;
1102         }
1103         to->rev_count += add_rev_class_hashed (to->rev_hash, to->rev_hash_size, from, 1);
1104 }
1105
1106 typedef struct {
1107         uintptr_t objaddr;
1108         HeapClassDesc *hklass;
1109         uintptr_t num_refs;
1110         uintptr_t refs [0];
1111 } HeapObjectDesc;
1112
1113 typedef struct _HeapShot HeapShot;
1114 struct _HeapShot {
1115         HeapShot *next;
1116         uint64_t timestamp;
1117         int class_count;
1118         int hash_size;
1119         HeapClassDesc **class_hash;
1120         HeapClassDesc **sorted;
1121         HeapObjectDesc **objects_hash;
1122         uintptr_t objects_count;
1123         uintptr_t objects_hash_size;
1124         uintptr_t num_roots;
1125         uintptr_t *roots;
1126         uintptr_t *roots_extra;
1127         int *roots_types;
1128 };
1129
1130 static HeapShot *heap_shots = NULL;
1131 static int num_heap_shots = 0;
1132
1133 static HeapShot*
1134 new_heap_shot (uint64_t timestamp)
1135 {
1136         HeapShot *hs = (HeapShot *)calloc (sizeof (HeapShot), 1);
1137         hs->hash_size = 4;
1138         hs->class_hash = (HeapClassDesc **)calloc (sizeof (void*), hs->hash_size);
1139         hs->timestamp = timestamp;
1140         num_heap_shots++;
1141         hs->next = heap_shots;
1142         heap_shots = hs;
1143         return hs;
1144 }
1145
1146 static HeapClassDesc*
1147 heap_class_lookup (HeapShot *hs, ClassDesc *klass)
1148 {
1149         int i;
1150         unsigned int start_pos;
1151         start_pos = ((uintptr_t)klass->klass >> 2) % hs->hash_size;
1152         i = start_pos;
1153         do {
1154                 HeapClassDesc* cd = hs->class_hash [i];
1155                 if (!cd)
1156                         return NULL;
1157                 if (cd->klass == klass)
1158                         return cd;
1159                 /* wrap around */
1160                 if (++i == hs->hash_size)
1161                         i = 0;
1162         } while (i != start_pos);
1163         return NULL;
1164 }
1165
1166 static int
1167 add_heap_hashed (HeapClassDesc **hash, HeapClassDesc **retv, uintptr_t hsize, ClassDesc *klass, uint64_t size, uint64_t count)
1168 {
1169         uintptr_t i;
1170         uintptr_t start_pos;
1171         start_pos = ((uintptr_t)klass->klass >> 2) % hsize;
1172         i = start_pos;
1173         do {
1174                 if (hash [i] && hash [i]->klass == klass) {
1175                         hash [i]->total_size += size;
1176                         hash [i]->count += count;
1177                         *retv = hash [i];
1178                         return 0;
1179                 } else if (!hash [i]) {
1180                         if (*retv) {
1181                                 hash [i] = *retv;
1182                                 return 1;
1183                         }
1184                         hash [i] = (HeapClassDesc *)calloc (sizeof (HeapClassDesc), 1);
1185                         hash [i]->klass = klass;
1186                         hash [i]->total_size += size;
1187                         hash [i]->count += count;
1188                         *retv = hash [i];
1189                         return 1;
1190                 }
1191                 /* wrap around */
1192                 if (++i == hsize)
1193                         i = 0;
1194         } while (i != start_pos);
1195         /* should not happen */
1196         printf ("failed heap class store\n");
1197         return 0;
1198 }
1199
1200 static HeapClassDesc*
1201 add_heap_shot_class (HeapShot *hs, ClassDesc *klass, uint64_t size)
1202 {
1203         HeapClassDesc *res;
1204         int i;
1205         if (hs->class_count * 2 >= hs->hash_size) {
1206                 HeapClassDesc **n;
1207                 int old_size = hs->hash_size;
1208                 hs->hash_size *= 2;
1209                 if (hs->hash_size == 0)
1210                         hs->hash_size = 4;
1211                 n = (HeapClassDesc **)calloc (sizeof (void*) * hs->hash_size, 1);
1212                 for (i = 0; i < old_size; ++i) {
1213                         res = hs->class_hash [i];
1214                         if (hs->class_hash [i])
1215                                 add_heap_hashed (n, &res, hs->hash_size, hs->class_hash [i]->klass, hs->class_hash [i]->total_size, hs->class_hash [i]->count);
1216                 }
1217                 if (hs->class_hash)
1218                         free (hs->class_hash);
1219                 hs->class_hash = n;
1220         }
1221         res = NULL;
1222         hs->class_count += add_heap_hashed (hs->class_hash, &res, hs->hash_size, klass, size, 1);
1223         //if (res->count == 1)
1224         //      printf ("added heap class: %s\n", res->klass->name);
1225         return res;
1226 }
1227
1228 static HeapObjectDesc*
1229 alloc_heap_obj (uintptr_t objaddr, HeapClassDesc *hklass, uintptr_t num_refs)
1230 {
1231         HeapObjectDesc* ho = (HeapObjectDesc *)calloc (sizeof (HeapObjectDesc) + num_refs * sizeof (uintptr_t), 1);
1232         ho->objaddr = objaddr;
1233         ho->hklass = hklass;
1234         ho->num_refs = num_refs;
1235         return ho;
1236 }
1237
1238 static uintptr_t
1239 heap_shot_find_obj_slot (HeapShot *hs, uintptr_t objaddr)
1240 {
1241         uintptr_t i;
1242         uintptr_t start_pos;
1243         HeapObjectDesc **hash = hs->objects_hash;
1244         start_pos = ((uintptr_t)objaddr >> 3) % hs->objects_hash_size;
1245         i = start_pos;
1246         do {
1247                 if (hash [i] && hash [i]->objaddr == objaddr) {
1248                         return i;
1249                 } else if (!hash [i]) {
1250                         break; /* fail */
1251                 }
1252                 /* wrap around */
1253                 if (++i == hs->objects_hash_size)
1254                         i = 0;
1255         } while (i != start_pos);
1256         /* should not happen */
1257         //printf ("failed heap obj slot\n");
1258         return -1;
1259 }
1260
1261 static HeapObjectDesc*
1262 heap_shot_obj_add_refs (HeapShot *hs, uintptr_t objaddr, uintptr_t num, uintptr_t *ref_offset)
1263 {
1264         HeapObjectDesc **hash = hs->objects_hash;
1265         uintptr_t i = heap_shot_find_obj_slot (hs, objaddr);
1266         if (i >= 0) {
1267                 HeapObjectDesc* ho = alloc_heap_obj (objaddr, hash [i]->hklass, hash [i]->num_refs + num);
1268                 *ref_offset = hash [i]->num_refs;
1269                 memcpy (ho->refs, hash [i]->refs, hash [i]->num_refs * sizeof (uintptr_t));
1270                 free (hash [i]);
1271                 hash [i] = ho;
1272                 return ho;
1273         }
1274         /* should not happen */
1275         printf ("failed heap obj update\n");
1276         return NULL;
1277
1278 }
1279
1280 static uintptr_t
1281 add_heap_hashed_obj (HeapObjectDesc **hash, uintptr_t hsize, HeapObjectDesc *obj)
1282 {
1283         uintptr_t i;
1284         uintptr_t start_pos;
1285         start_pos = ((uintptr_t)obj->objaddr >> 3) % hsize;
1286         i = start_pos;
1287         do {
1288                 if (hash [i] && hash [i]->objaddr == obj->objaddr) {
1289                         printf ("duplicate object!\n");
1290                         return 0;
1291                 } else if (!hash [i]) {
1292                         hash [i] = obj;
1293                         return 1;
1294                 }
1295                 /* wrap around */
1296                 if (++i == hsize)
1297                         i = 0;
1298         } while (i != start_pos);
1299         /* should not happen */
1300         printf ("failed heap obj store\n");
1301         return 0;
1302 }
1303
1304 static void
1305 add_heap_shot_obj (HeapShot *hs, HeapObjectDesc *obj)
1306 {
1307         uintptr_t i;
1308         if (hs->objects_count * 2 >= hs->objects_hash_size) {
1309                 HeapObjectDesc **n;
1310                 uintptr_t old_size = hs->objects_hash_size;
1311                 hs->objects_hash_size *= 2;
1312                 if (hs->objects_hash_size == 0)
1313                         hs->objects_hash_size = 4;
1314                 n = (HeapObjectDesc **)calloc (sizeof (void*) * hs->objects_hash_size, 1);
1315                 for (i = 0; i < old_size; ++i) {
1316                         if (hs->objects_hash [i])
1317                                 add_heap_hashed_obj (n, hs->objects_hash_size, hs->objects_hash [i]);
1318                 }
1319                 if (hs->objects_hash)
1320                         free (hs->objects_hash);
1321                 hs->objects_hash = n;
1322         }
1323         hs->objects_count += add_heap_hashed_obj (hs->objects_hash, hs->objects_hash_size, obj);
1324 }
1325
1326 static void
1327 heap_shot_resolve_reverse_refs (HeapShot *hs)
1328 {
1329         uintptr_t i;
1330         for (i = 0; i < hs->objects_hash_size; ++i) {
1331                 uintptr_t r;
1332                 HeapObjectDesc *ho = hs->objects_hash [i];
1333                 if (!ho)
1334                         continue;
1335                 for (r = 0; r < ho->num_refs; ++r) {
1336                         uintptr_t oi = heap_shot_find_obj_slot (hs, ho->refs [r]);
1337                         add_heap_class_rev (ho->hklass, hs->objects_hash [oi]->hklass);
1338                 }
1339         }
1340 }
1341
1342 #define MARK_GRAY 1
1343 #define MARK_BLACK 2
1344
1345 static void
1346 heap_shot_mark_objects (HeapShot *hs)
1347 {
1348         uintptr_t i, oi, r;
1349         unsigned char *marks;
1350         HeapObjectDesc *obj, *ref;
1351         int marked_some;
1352         uintptr_t num_marked = 0, num_unmarked;
1353         for (i = 0; i < hs->num_roots; ++i) {
1354                 HeapClassDesc *cd;
1355                 oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
1356                 if (oi == -1) {
1357                         continue;
1358                 }
1359                 obj = hs->objects_hash [oi];
1360                 cd = obj->hklass;
1361                 if (hs->roots_types [i] & MONO_PROFILE_GC_ROOT_PINNING)
1362                         cd->pinned_references++;
1363                 cd->root_references++;
1364         }
1365         if (!debug)
1366                 return;
1367         /* consistency checks: it seems not all the objects are walked in the heap in some cases */
1368         marks = (unsigned char *)calloc (hs->objects_hash_size, 1);
1369         if (!marks)
1370                 return;
1371         for (i = 0; i < hs->num_roots; ++i) {
1372                 oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
1373                 if (oi == -1) {
1374                         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);
1375                         continue;
1376                 }
1377                 obj = hs->objects_hash [oi];
1378                 if (!marks [oi]) {
1379                         marks [oi] = obj->num_refs? MARK_GRAY: MARK_BLACK;
1380                         num_marked++;
1381                 }
1382         }
1383         marked_some = 1;
1384         while (marked_some) {
1385                 marked_some = 0;
1386                 for (i = 0; i < hs->objects_hash_size; ++i) {
1387                         if (marks [i] != MARK_GRAY)
1388                                 continue;
1389                         marks [i] = MARK_BLACK;
1390                         obj = hs->objects_hash [i];
1391                         for (r = 0; r < obj->num_refs; ++r) {
1392                                 oi = heap_shot_find_obj_slot (hs, obj->refs [r]);
1393                                 if (oi == -1) {
1394                                         fprintf (outfile, "referenced obj %p not found in heap\n", (void*)obj->refs [r]);
1395                                         continue;
1396                                 }
1397                                 ref = hs->objects_hash [oi];
1398                                 if (!marks [oi]) {
1399                                         marks [oi] = ref->num_refs? MARK_GRAY: MARK_BLACK;
1400                                 }
1401                         }
1402                         marked_some++;
1403                 }
1404         }
1405
1406         num_unmarked = 0;
1407         for (i = 0; i < hs->objects_hash_size; ++i) {
1408                 if (hs->objects_hash [i] && !marks [i]) {
1409                         num_unmarked++;
1410                         fprintf (outfile, "object %p (%s) unmarked\n", (void*)hs->objects_hash [i], hs->objects_hash [i]->hklass->klass->name);
1411                 }
1412         }
1413         fprintf (outfile, "Total unmarked: %zd/%zd\n", num_unmarked, hs->objects_count);
1414         free (marks);
1415 }
1416
1417 static void
1418 heap_shot_free_objects (HeapShot *hs)
1419 {
1420         uintptr_t i;
1421         for (i = 0; i < hs->objects_hash_size; ++i) {
1422                 HeapObjectDesc *ho = hs->objects_hash [i];
1423                 if (ho)
1424                         free (ho);
1425         }
1426         if (hs->objects_hash)
1427                 free (hs->objects_hash);
1428         hs->objects_hash = NULL;
1429         hs->objects_hash_size = 0;
1430         hs->objects_count = 0;
1431 }
1432
1433
1434 struct _BackTrace {
1435         BackTrace *next;
1436         unsigned int hash;
1437         int count;
1438         int id;
1439         MethodDesc *methods [1];
1440 };
1441
1442 static BackTrace *backtrace_hash [HASH_SIZE];
1443 static BackTrace **backtraces = NULL;
1444 static int num_backtraces = 0;
1445 static int next_backtrace = 0;
1446
1447 static int
1448 hash_backtrace (int count, MethodDesc **methods)
1449 {
1450         int hash = count;
1451         int i;
1452         for (i = 0; i < count; ++i) {
1453                 hash = (hash << 5) - hash + methods [i]->method;
1454         }
1455         return hash;
1456 }
1457
1458 static int
1459 compare_backtrace (BackTrace *bt, int count, MethodDesc **methods)
1460 {
1461         int i;
1462         if (bt->count != count)
1463                 return 0;
1464         for (i = 0; i < count; ++i)
1465                 if (methods [i] != bt->methods [i])
1466                         return 0;
1467         return 1;
1468 }
1469
1470 static BackTrace*
1471 add_backtrace (int count, MethodDesc **methods)
1472 {
1473         int hash = hash_backtrace (count, methods);
1474         int slot = (hash & 0xffff) % HASH_SIZE;
1475         BackTrace *bt = backtrace_hash [slot];
1476         while (bt) {
1477                 if (bt->hash == hash && compare_backtrace (bt, count, methods))
1478                         return bt;
1479                 bt = bt->next;
1480         }
1481         bt = (BackTrace *)malloc (sizeof (BackTrace) + ((count - 1) * sizeof (void*)));
1482         bt->next = backtrace_hash [slot];
1483         backtrace_hash [slot] = bt;
1484         if (next_backtrace == num_backtraces) {
1485                 num_backtraces *= 2;
1486                 if (!num_backtraces)
1487                         num_backtraces = 16;
1488                 backtraces = (BackTrace **)realloc (backtraces, sizeof (void*) * num_backtraces);
1489         }
1490         bt->id = next_backtrace++;
1491         backtraces [bt->id] = bt;
1492         bt->count = count;
1493         bt->hash = hash;
1494         for (slot = 0; slot < count; ++slot)
1495                 bt->methods [slot] = methods [slot];
1496
1497         return bt;
1498 }
1499
1500 typedef struct _MonitorDesc MonitorDesc;
1501 typedef struct _ThreadContext ThreadContext;
1502 typedef struct _DomainContext DomainContext;
1503 typedef struct _RemCtxContext RemCtxContext;
1504
1505 typedef struct {
1506         FILE *file;
1507 #if defined (HAVE_SYS_ZLIB)
1508         gzFile gzfile;
1509 #endif
1510         unsigned char *buf;
1511         int size;
1512         int data_version;
1513         int version_major;
1514         int version_minor;
1515         int timer_overhead;
1516         int pid;
1517         int port;
1518         uint64_t startup_time;
1519         ThreadContext *threads;
1520         ThreadContext *current_thread;
1521         DomainContext *domains;
1522         DomainContext *current_domain;
1523         RemCtxContext *remctxs;
1524         RemCtxContext *current_remctx;
1525 } ProfContext;
1526
1527 struct _ThreadContext {
1528         ThreadContext *next;
1529         intptr_t thread_id;
1530         char *name;
1531         /* emulated stack */
1532         MethodDesc **stack;
1533         uint64_t *time_stack;
1534         uint64_t *callee_time_stack;
1535         uint64_t last_time;
1536         uint64_t contention_start;
1537         MonitorDesc *monitor;
1538         int stack_size;
1539         int stack_id;
1540         HeapShot *current_heap_shot;
1541         uintptr_t num_roots;
1542         uintptr_t size_roots;
1543         uintptr_t *roots;
1544         uintptr_t *roots_extra;
1545         int *roots_types;
1546         uint64_t gc_start_times [3];
1547 };
1548
1549 struct _DomainContext {
1550         DomainContext *next;
1551         intptr_t domain_id;
1552         const char *friendly_name;
1553 };
1554
1555 struct _RemCtxContext {
1556         RemCtxContext *next;
1557         intptr_t remctx_id;
1558         intptr_t domain_id;
1559 };
1560
1561 static void
1562 ensure_buffer (ProfContext *ctx, int size)
1563 {
1564         if (ctx->size < size) {
1565                 ctx->buf = (unsigned char *)realloc (ctx->buf, size);
1566                 ctx->size = size;
1567         }
1568 }
1569
1570 static int
1571 load_data (ProfContext *ctx, int size)
1572 {
1573         ensure_buffer (ctx, size);
1574 #if defined (HAVE_SYS_ZLIB)
1575         if (ctx->gzfile) {
1576                 int r = gzread (ctx->gzfile, ctx->buf, size);
1577                 if (r == 0)
1578                         return size == 0? 1: 0;
1579                 return r == size;
1580         } else
1581 #endif
1582         {
1583                 int r = fread (ctx->buf, size, 1, ctx->file);
1584                 if (r == 0)
1585                         return size == 0? 1: 0;
1586                 return r;
1587         }
1588 }
1589
1590 static ThreadContext*
1591 get_thread (ProfContext *ctx, intptr_t thread_id)
1592 {
1593         ThreadContext *thread;
1594         if (ctx->current_thread && ctx->current_thread->thread_id == thread_id)
1595                 return ctx->current_thread;
1596         thread = ctx->threads;
1597         while (thread) {
1598                 if (thread->thread_id == thread_id) {
1599                         return thread;
1600                 }
1601                 thread = thread->next;
1602         }
1603         thread = (ThreadContext *)calloc (sizeof (ThreadContext), 1);
1604         thread->next = ctx->threads;
1605         ctx->threads = thread;
1606         thread->thread_id = thread_id;
1607         thread->last_time = 0;
1608         thread->stack_id = 0;
1609         thread->stack_size = 32;
1610         thread->stack = (MethodDesc **)malloc (thread->stack_size * sizeof (void*));
1611         thread->time_stack = (uint64_t *)malloc (thread->stack_size * sizeof (uint64_t));
1612         thread->callee_time_stack = (uint64_t *)malloc (thread->stack_size * sizeof (uint64_t));
1613         return thread;
1614 }
1615
1616 static DomainContext *
1617 get_domain (ProfContext *ctx, intptr_t domain_id)
1618 {
1619         if (ctx->current_domain && ctx->current_domain->domain_id == domain_id)
1620                 return ctx->current_domain;
1621
1622         DomainContext *domain = ctx->domains;
1623
1624         while (domain) {
1625                 if (domain->domain_id == domain_id)
1626                         return domain;
1627
1628                 domain = domain->next;
1629         }
1630
1631         domain = (DomainContext *)calloc (sizeof (DomainContext), 1);
1632         domain->next = ctx->domains;
1633         ctx->domains = domain;
1634         domain->domain_id = domain_id;
1635
1636         return domain;
1637 }
1638
1639 static RemCtxContext *
1640 get_remctx (ProfContext *ctx, intptr_t remctx_id)
1641 {
1642         if (ctx->current_remctx && ctx->current_remctx->remctx_id == remctx_id)
1643                 return ctx->current_remctx;
1644
1645         RemCtxContext *remctx = ctx->remctxs;
1646
1647         while (remctx) {
1648                 if (remctx->remctx_id == remctx_id)
1649                         return remctx;
1650
1651                 remctx = remctx->next;
1652         }
1653
1654         remctx = (RemCtxContext *)calloc (sizeof (RemCtxContext), 1);
1655         remctx->next = ctx->remctxs;
1656         ctx->remctxs = remctx;
1657         remctx->remctx_id = remctx_id;
1658
1659         return remctx;
1660 }
1661
1662 static ThreadContext*
1663 load_thread (ProfContext *ctx, intptr_t thread_id)
1664 {
1665         ThreadContext *thread = get_thread (ctx, thread_id);
1666         ctx->current_thread = thread;
1667         return thread;
1668 }
1669
1670 static void
1671 ensure_thread_stack (ThreadContext *thread)
1672 {
1673         if (thread->stack_id == thread->stack_size) {
1674                 thread->stack_size *= 2;
1675                 thread->stack = (MethodDesc **)realloc (thread->stack, thread->stack_size * sizeof (void*));
1676                 thread->time_stack = (uint64_t *)realloc (thread->time_stack, thread->stack_size * sizeof (uint64_t));
1677                 thread->callee_time_stack = (uint64_t *)realloc (thread->callee_time_stack, thread->stack_size * sizeof (uint64_t));
1678         }
1679 }
1680
1681 static int
1682 add_trace_hashed (CallContext *traces, int size, BackTrace *bt, uint64_t value)
1683 {
1684         int i;
1685         unsigned int start_pos;
1686         start_pos = bt->hash % size;
1687         i = start_pos;
1688         do {
1689                 if (traces [i].bt == bt) {
1690                         traces [i].count += value;
1691                         return 0;
1692                 } else if (!traces [i].bt) {
1693                         traces [i].bt = bt;
1694                         traces [i].count += value;
1695                         return 1;
1696                 }
1697                 /* wrap around */
1698                 if (++i == size)
1699                         i = 0;
1700         } while (i != start_pos);
1701         /* should not happen */
1702         printf ("failed trace store\n");
1703         return 0;
1704 }
1705
1706 static void
1707 add_trace_bt (BackTrace *bt, TraceDesc *trace, uint64_t value)
1708 {
1709         int i;
1710         if (!collect_traces)
1711                 return;
1712         if (trace->count * 2 >= trace->size) {
1713                 CallContext *n;
1714                 int old_size = trace->size;
1715                 trace->size *= 2;
1716                 if (trace->size == 0)
1717                         trace->size = 4;
1718                 n = (CallContext *)calloc (sizeof (CallContext) * trace->size, 1);
1719                 for (i = 0; i < old_size; ++i) {
1720                         if (trace->traces [i].bt)
1721                                 add_trace_hashed (n, trace->size, trace->traces [i].bt, trace->traces [i].count);
1722                 }
1723                 if (trace->traces)
1724                         free (trace->traces);
1725                 trace->traces = n;
1726         }
1727         trace->count += add_trace_hashed (trace->traces, trace->size, bt, value);
1728 }
1729
1730 static BackTrace*
1731 add_trace_thread (ThreadContext *thread, TraceDesc *trace, uint64_t value)
1732 {
1733         BackTrace *bt;
1734         int count = thread->stack_id;
1735         if (!collect_traces)
1736                 return NULL;
1737         if (count > trace_max)
1738                 count = trace_max;
1739         bt = add_backtrace (count, thread->stack + thread->stack_id - count);
1740         add_trace_bt (bt, trace, value);
1741         return bt;
1742 }
1743
1744 static BackTrace*
1745 add_trace_methods (MethodDesc **methods, int count, TraceDesc *trace, uint64_t value)
1746 {
1747         BackTrace *bt;
1748         if (!collect_traces)
1749                 return NULL;
1750         if (count > trace_max)
1751                 count = trace_max;
1752         bt = add_backtrace (count, methods);
1753         add_trace_bt (bt, trace, value);
1754         return bt;
1755 }
1756
1757 static void
1758 thread_add_root (ThreadContext *ctx, uintptr_t obj, int root_type, uintptr_t extra_info)
1759 {
1760         if (ctx->num_roots == ctx->size_roots) {
1761                 int new_size = ctx->size_roots * 2;
1762                 if (!new_size)
1763                         new_size = 4;
1764                 ctx->roots = (uintptr_t *)realloc (ctx->roots, new_size * sizeof (uintptr_t));
1765                 ctx->roots_extra = (uintptr_t *)realloc (ctx->roots_extra, new_size * sizeof (uintptr_t));
1766                 ctx->roots_types = (int *)realloc (ctx->roots_types, new_size * sizeof (int));
1767                 ctx->size_roots = new_size;
1768         }
1769         ctx->roots_types [ctx->num_roots] = root_type;
1770         ctx->roots_extra [ctx->num_roots] = extra_info;
1771         ctx->roots [ctx->num_roots++] = obj;
1772 }
1773
1774 static int
1775 compare_callc (const void *a, const void *b)
1776 {
1777         const CallContext *A = (const CallContext *)a;
1778         const CallContext *B = (const CallContext *)b;
1779         if (B->count == A->count)
1780                 return 0;
1781         if (B->count < A->count)
1782                 return -1;
1783         return 1;
1784 }
1785
1786 static void
1787 sort_context_array (TraceDesc* traces)
1788 {
1789         int i, j;
1790         for (i = 0, j = 0; i < traces->size; ++i) {
1791                 if (traces->traces [i].bt) {
1792                         traces->traces [j].bt = traces->traces [i].bt;
1793                         traces->traces [j].count = traces->traces [i].count;
1794                         j++;
1795                 }
1796         }
1797         qsort (traces->traces, traces->count, sizeof (CallContext), compare_callc);
1798 }
1799
1800 static void
1801 push_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
1802 {
1803         ensure_thread_stack (thread);
1804         thread->time_stack [thread->stack_id] = timestamp;
1805         thread->callee_time_stack [thread->stack_id] = 0;
1806         thread->stack [thread->stack_id++] = method;
1807         method->recurse_count++;
1808 }
1809
1810 static void
1811 pop_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
1812 {
1813         method->recurse_count--;
1814         if (thread->stack_id > 0 && thread->stack [thread->stack_id - 1] == method) {
1815                 uint64_t tdiff;
1816                 thread->stack_id--;
1817                 method->calls++;
1818                 if (timestamp < thread->time_stack [thread->stack_id])
1819                         fprintf (outfile, "time went backwards for %s\n", method->name);
1820                 tdiff = timestamp - thread->time_stack [thread->stack_id];
1821                 if (thread->callee_time_stack [thread->stack_id] > tdiff)
1822                         fprintf (outfile, "callee time bigger for %s\n", method->name);
1823                 method->self_time += tdiff - thread->callee_time_stack [thread->stack_id];
1824                 method->callee_time += thread->callee_time_stack [thread->stack_id];
1825                 if (thread->stack_id)
1826                         thread->callee_time_stack [thread->stack_id - 1] += tdiff;
1827                 //fprintf (outfile, "method %s took %d\n", method->name, (int)(tdiff/1000));
1828         } else {
1829                 fprintf (outfile, "unmatched leave at stack pos: %d for method %s\n", thread->stack_id, method->name);
1830         }
1831 }
1832
1833 typedef struct {
1834         uint64_t total_time;
1835         uint64_t max_time;
1836         int count;
1837 } GCDesc;
1838 static GCDesc gc_info [3];
1839 static uint64_t max_heap_size;
1840 static uint64_t gc_object_moves;
1841 static int gc_resizes;
1842 typedef struct {
1843         uint64_t created;
1844         uint64_t destroyed;
1845         uint64_t live;
1846         uint64_t max_live;
1847         TraceDesc traces;
1848         TraceDesc destroy_traces;
1849 } HandleInfo;
1850 static HandleInfo handle_info [4];
1851
1852 static const char*
1853 gc_event_name (int ev)
1854 {
1855         switch (ev) {
1856         case MONO_GC_EVENT_START: return "start";
1857         case MONO_GC_EVENT_MARK_START: return "mark start";
1858         case MONO_GC_EVENT_MARK_END: return "mark end";
1859         case MONO_GC_EVENT_RECLAIM_START: return "reclaim start";
1860         case MONO_GC_EVENT_RECLAIM_END: return "reclaim end";
1861         case MONO_GC_EVENT_END: return "end";
1862         case MONO_GC_EVENT_PRE_STOP_WORLD: return "pre stop";
1863         case MONO_GC_EVENT_POST_STOP_WORLD: return "post stop";
1864         case MONO_GC_EVENT_PRE_START_WORLD: return "pre start";
1865         case MONO_GC_EVENT_POST_START_WORLD: return "post start";
1866         default:
1867                 return "unknown";
1868         }
1869 }
1870
1871 static uint64_t clause_summary [MONO_EXCEPTION_CLAUSE_FAULT + 1];
1872 static uint64_t throw_count = 0;
1873 static TraceDesc exc_traces;
1874
1875 static const char*
1876 clause_name (int type)
1877 {
1878         switch (type) {
1879         case MONO_EXCEPTION_CLAUSE_NONE: return "catch";
1880         case MONO_EXCEPTION_CLAUSE_FILTER: return "filter";
1881         case MONO_EXCEPTION_CLAUSE_FINALLY: return "finally";
1882         case MONO_EXCEPTION_CLAUSE_FAULT: return "fault";
1883         default: return "invalid";
1884         }
1885 }
1886
1887 static uint64_t monitor_contention;
1888 static uint64_t monitor_failed;
1889 static uint64_t monitor_acquired;
1890
1891 struct _MonitorDesc {
1892         MonitorDesc *next;
1893         uintptr_t objid;
1894         uintptr_t contentions;
1895         uint64_t wait_time;
1896         uint64_t max_wait_time;
1897         TraceDesc traces;
1898 };
1899
1900 static MonitorDesc* monitor_hash [SMALL_HASH_SIZE] = {0};
1901 static int num_monitors = 0;
1902
1903 static MonitorDesc*
1904 lookup_monitor (uintptr_t objid)
1905 {
1906         int slot = ((objid >> 3) & 0xffff) % SMALL_HASH_SIZE;
1907         MonitorDesc *cd = monitor_hash [slot];
1908         while (cd && cd->objid != objid)
1909                 cd = cd->next;
1910         if (!cd) {
1911                 cd = (MonitorDesc *)calloc (sizeof (MonitorDesc), 1);
1912                 cd->objid = objid;
1913                 cd->next = monitor_hash [slot];
1914                 monitor_hash [slot] = cd;
1915                 num_monitors++;
1916         }
1917         return cd;
1918 }
1919
1920 static const char*
1921 monitor_ev_name (int ev)
1922 {
1923         switch (ev) {
1924         case MONO_PROFILER_MONITOR_CONTENTION: return "contended";
1925         case MONO_PROFILER_MONITOR_DONE: return "acquired";
1926         case MONO_PROFILER_MONITOR_FAIL: return "not taken";
1927         default: return "invalid";
1928         }
1929 }
1930
1931 static const char*
1932 get_handle_name (int htype)
1933 {
1934         switch (htype) {
1935         case 0: return "weak";
1936         case 1: return "weaktrack";
1937         case 2: return "normal";
1938         case 3: return "pinned";
1939         default: return "unknown";
1940         }
1941 }
1942
1943 static const char*
1944 get_root_name (int rtype)
1945 {
1946         switch (rtype & MONO_PROFILE_GC_ROOT_TYPEMASK) {
1947         case MONO_PROFILE_GC_ROOT_STACK: return "stack";
1948         case MONO_PROFILE_GC_ROOT_FINALIZER: return "finalizer";
1949         case MONO_PROFILE_GC_ROOT_HANDLE: return "handle";
1950         case MONO_PROFILE_GC_ROOT_OTHER: return "other";
1951         case MONO_PROFILE_GC_ROOT_MISC: return "misc";
1952         default: return "unknown";
1953         }
1954 }
1955
1956 static MethodDesc**
1957 decode_bt (MethodDesc** sframes, int *size, unsigned char *p, unsigned char **endp, intptr_t ptr_base)
1958 {
1959         MethodDesc **frames;
1960         int i;
1961         int flags = decode_uleb128 (p, &p);
1962         int count = decode_uleb128 (p, &p);
1963         if (flags != 0)
1964                 return NULL;
1965         if (count > *size)
1966                 frames = (MethodDesc **)malloc (count * sizeof (void*));
1967         else
1968                 frames = sframes;
1969         for (i = 0; i < count; ++i) {
1970                 intptr_t ptrdiff = decode_sleb128 (p, &p);
1971                 frames [i] = lookup_method (ptr_base + ptrdiff);
1972         }
1973         *size = count;
1974         *endp = p;
1975         return frames;
1976 }
1977
1978 static void
1979 tracked_creation (uintptr_t obj, ClassDesc *cd, uint64_t size, BackTrace *bt, uint64_t timestamp)
1980 {
1981         int i;
1982         for (i = 0; i < num_tracked_objects; ++i) {
1983                 if (tracked_objects [i] != obj)
1984                         continue;
1985                 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);
1986                 if (bt && bt->count) {
1987                         int k;
1988                         for (k = 0; k < bt->count; ++k)
1989                                 fprintf (outfile, "\t%s\n", bt->methods [k]->name);
1990                 }
1991         }
1992 }
1993
1994 static void
1995 track_handle (uintptr_t obj, int htype, uint32_t handle, BackTrace *bt, uint64_t timestamp)
1996 {
1997         int i;
1998         for (i = 0; i < num_tracked_objects; ++i) {
1999                 if (tracked_objects [i] != obj)
2000                         continue;
2001                 fprintf (outfile, "Object %p referenced from handle %u at %.3f secs.\n", (void*)obj, handle, (timestamp - startup_time) / 1000000000.0);
2002                 if (bt && bt->count) {
2003                         int k;
2004                         for (k = 0; k < bt->count; ++k)
2005                                 fprintf (outfile, "\t%s\n", bt->methods [k]->name);
2006                 }
2007         }
2008 }
2009
2010 static void
2011 track_move (uintptr_t src, uintptr_t dst)
2012 {
2013         int i;
2014         for (i = 0; i < num_tracked_objects; ++i) {
2015                 if (tracked_objects [i] == src)
2016                         fprintf (outfile, "Object %p moved to %p\n", (void*)src, (void*)dst);
2017                 else if (tracked_objects [i] == dst)
2018                         fprintf (outfile, "Object %p moved from %p\n", (void*)dst, (void*)src);
2019         }
2020 }
2021
2022 static void
2023 track_obj_reference (uintptr_t obj, uintptr_t parent, ClassDesc *cd)
2024 {
2025         int i;
2026         for (i = 0; i < num_tracked_objects; ++i) {
2027                 if (tracked_objects [i] == obj)
2028                         fprintf (outfile, "Object %p referenced from %p (%s).\n", (void*)obj, (void*)parent, cd->name);
2029         }
2030 }
2031
2032 static void
2033 found_object (uintptr_t obj)
2034 {
2035         num_tracked_objects ++;
2036         tracked_objects = (uintptr_t *)realloc (tracked_objects, num_tracked_objects * sizeof (tracked_objects [0]));
2037         tracked_objects [num_tracked_objects - 1] = obj;
2038 }
2039
2040 static int num_jit_helpers = 0;
2041 static int jit_helpers_code_size = 0;
2042
2043 static const char*
2044 code_buffer_desc (int type)
2045 {
2046         switch (type) {
2047         case MONO_PROFILER_CODE_BUFFER_METHOD:
2048                 return "method";
2049         case MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE:
2050                 return "method trampoline";
2051         case MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE:
2052                 return "unbox trampoline";
2053         case MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE:
2054                 return "imt trampoline";
2055         case MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE:
2056                 return "generics trampoline";
2057         case MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE:
2058                 return "specific trampoline";
2059         case MONO_PROFILER_CODE_BUFFER_HELPER:
2060                 return "misc helper";
2061         case MONO_PROFILER_CODE_BUFFER_MONITOR:
2062                 return "monitor/lock";
2063         case MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE:
2064                 return "delegate invoke";
2065         case MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING:
2066                 return "exception handling";
2067         default:
2068                 return "unspecified";
2069         }
2070 }
2071
2072 typedef struct _CoverageAssembly CoverageAssembly;
2073 struct _CoverageAssembly {
2074         char *name;
2075         char *guid;
2076         char *filename;
2077         int number_of_methods;
2078         int fully_covered;
2079         int partially_covered;
2080 };
2081
2082 typedef struct _CoverageClass CoverageClass;
2083 struct _CoverageClass {
2084         char *assembly_name;
2085         char *class_name;
2086         int number_of_methods;
2087         int fully_covered;
2088         int partially_covered;
2089 };
2090
2091 typedef struct _CoverageCoverage CoverageCoverage;
2092 struct _CoverageCoverage {
2093         int method_id;
2094         int offset;
2095         int count;
2096         int line;
2097         int column;
2098 };
2099
2100 typedef struct _CoverageMethod CoverageMethod;
2101 struct _CoverageMethod {
2102         char *assembly_name;
2103         char *class_name;
2104         char *method_name;
2105         char *method_signature;
2106         char *filename;
2107         int token;
2108         int n_statements;
2109         int method_id;
2110         GPtrArray *coverage;
2111 };
2112 static GPtrArray *coverage_assemblies = NULL;
2113 static GPtrArray *coverage_methods = NULL;
2114 static GPtrArray *coverage_statements = NULL;
2115 static GHashTable *coverage_methods_hash = NULL;
2116 static GPtrArray *coverage_classes = NULL;
2117 static GHashTable *coverage_assembly_classes = NULL;
2118
2119 static void
2120 gather_coverage_statements (void)
2121 {
2122         for (guint i = 0; i < coverage_statements->len; i++) {
2123                 CoverageCoverage *coverage = (CoverageCoverage *)coverage_statements->pdata[i];
2124                 CoverageMethod *method = (CoverageMethod *)g_hash_table_lookup (coverage_methods_hash, GINT_TO_POINTER (coverage->method_id));
2125                 if (method == NULL) {
2126                         fprintf (outfile, "Cannot find method with ID: %d\n", coverage->method_id);
2127                         continue;
2128                 }
2129
2130                 g_ptr_array_add (method->coverage, coverage);
2131         }
2132 }
2133
2134 static void
2135 coverage_add_assembly (CoverageAssembly *assembly)
2136 {
2137         if (coverage_assemblies == NULL)
2138                 coverage_assemblies = g_ptr_array_new ();
2139
2140         g_ptr_array_add (coverage_assemblies, assembly);
2141 }
2142
2143 static void
2144 coverage_add_method (CoverageMethod *method)
2145 {
2146         if (coverage_methods == NULL) {
2147                 coverage_methods = g_ptr_array_new ();
2148                 coverage_methods_hash = g_hash_table_new (NULL, NULL);
2149         }
2150
2151         g_ptr_array_add (coverage_methods, method);
2152         g_hash_table_insert (coverage_methods_hash, GINT_TO_POINTER (method->method_id), method);
2153 }
2154
2155 static void
2156 coverage_add_class (CoverageClass *klass)
2157 {
2158         GPtrArray *classes = NULL;
2159
2160         if (coverage_classes == NULL) {
2161                 coverage_classes = g_ptr_array_new ();
2162                 coverage_assembly_classes = g_hash_table_new (g_str_hash, g_str_equal);
2163         }
2164
2165         g_ptr_array_add (coverage_classes, klass);
2166         classes = (GPtrArray *)g_hash_table_lookup (coverage_assembly_classes, klass->assembly_name);
2167         if (classes == NULL) {
2168                 classes = g_ptr_array_new ();
2169                 g_hash_table_insert (coverage_assembly_classes, klass->assembly_name, classes);
2170         }
2171         g_ptr_array_add (classes, klass);
2172 }
2173
2174 static void
2175 coverage_add_coverage (CoverageCoverage *coverage)
2176 {
2177         if (coverage_statements == NULL)
2178                 coverage_statements = g_ptr_array_new ();
2179
2180         g_ptr_array_add (coverage_statements, coverage);
2181 }
2182
2183 #define OBJ_ADDR(diff) ((obj_base + diff) << 3)
2184 #define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
2185
2186
2187 /* Stats */
2188 #define BUFFER_HEADER_SIZE 48
2189
2190 typedef struct {
2191         int count, min_size, max_size, bytes;
2192 } EventStat;
2193
2194 static int buffer_count;
2195 static EventStat stats [256];
2196
2197 static void
2198 record_event_stats (int type, int size)
2199 {
2200         ++stats [type].count;
2201         if (!stats [type].min_size)
2202                 stats [type].min_size = size;
2203         stats [type].min_size = MIN (stats [type].min_size, size);
2204         stats [type].max_size = MAX (stats [type].max_size, size);
2205         stats [type].bytes += size;
2206 }
2207
2208 static int
2209 decode_buffer (ProfContext *ctx)
2210 {
2211         unsigned char *p;
2212         unsigned char *end;
2213         intptr_t thread_id;
2214         intptr_t ptr_base;
2215         intptr_t obj_base;
2216         intptr_t method_base;
2217         uint64_t time_base;
2218         uint64_t file_offset;
2219         int len, i;
2220         ThreadContext *thread;
2221
2222 #ifdef HAVE_SYS_ZLIB
2223         if (ctx->gzfile)
2224                 file_offset = gztell (ctx->gzfile);
2225         else
2226 #endif
2227                 file_offset = ftell (ctx->file);
2228         if (!load_data (ctx, 48))
2229                 return 0;
2230         p = ctx->buf;
2231         if (read_int32 (p) != BUF_ID) {
2232                 fprintf (outfile, "Incorrect buffer id: 0x%x\n", read_int32 (p));
2233                 for (i = 0; i < 48; ++i) {
2234                         fprintf (outfile, "0x%x%s", p [i], i % 8?" ":"\n");
2235                 }
2236                 return 0;
2237         }
2238         len = read_int32 (p + 4);
2239         time_base = read_int64 (p + 8);
2240         ptr_base = read_int64 (p + 16);
2241         obj_base = read_int64 (p + 24);
2242         thread_id = read_int64 (p + 32);
2243         method_base = read_int64 (p + 40);
2244         if (debug)
2245                 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);
2246         thread = load_thread (ctx, thread_id);
2247         if (!load_data (ctx, len))
2248                 return 0;
2249
2250         ++buffer_count;
2251
2252         if (!startup_time) {
2253                 startup_time = time_base;
2254                 if (use_time_filter) {
2255                         time_from += startup_time;
2256                         time_to += startup_time;
2257                 }
2258                 if (!thread->name)
2259                         thread->name = pstrdup ("Main");
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                 fprintf (outfile, "\tThread: %p, name: \"%s\"\n", (void*)thread->thread_id, thread->name? thread->name: "");
3155         }
3156 }
3157
3158 static void
3159 dump_domains (ProfContext *ctx)
3160 {
3161         fprintf (outfile, "\nDomain summary\n");
3162
3163         for (DomainContext *domain = ctx->domains; domain; domain = domain->next)
3164                 fprintf (outfile, "\tDomain: %p, friendly name: \"%s\"\n", (void *) domain->domain_id, domain->friendly_name);
3165 }
3166
3167 static void
3168 dump_remctxs (ProfContext *ctx)
3169 {
3170         fprintf (outfile, "\nContext summary\n");
3171
3172         for (RemCtxContext *remctx = ctx->remctxs; remctx; remctx = remctx->next)
3173                 fprintf (outfile, "\tContext: %p, domain: %p\n", (void *) remctx->remctx_id, (void *) remctx->domain_id);
3174 }
3175
3176 static void
3177 dump_exceptions (void)
3178 {
3179         int i;
3180         fprintf (outfile, "\nException summary\n");
3181         fprintf (outfile, "\tThrows: %llu\n", (unsigned long long) throw_count);
3182         dump_traces (&exc_traces, "throws");
3183         for (i = 0; i <= MONO_EXCEPTION_CLAUSE_FAULT; ++i) {
3184                 if (!clause_summary [i])
3185                         continue;
3186                 fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), (unsigned long long) clause_summary [i]);
3187         }
3188 }
3189
3190 static int
3191 compare_monitor (const void *a, const void *b)
3192 {
3193         MonitorDesc *const *A = (MonitorDesc *const *)a;
3194         MonitorDesc *const *B = (MonitorDesc *const *)b;
3195         if ((*B)->wait_time == (*A)->wait_time)
3196                 return 0;
3197         if ((*B)->wait_time < (*A)->wait_time)
3198                 return -1;
3199         return 1;
3200 }
3201
3202 static void
3203 dump_monitors (void)
3204 {
3205         MonitorDesc **monitors;
3206         int i, j;
3207         if (!num_monitors)
3208                 return;
3209         monitors = (MonitorDesc **)malloc (sizeof (void*) * num_monitors);
3210         for (i = 0, j = 0; i < SMALL_HASH_SIZE; ++i) {
3211                 MonitorDesc *mdesc = monitor_hash [i];
3212                 while (mdesc) {
3213                         monitors [j++] = mdesc;
3214                         mdesc = mdesc->next;
3215                 }
3216         }
3217         qsort (monitors, num_monitors, sizeof (void*), compare_monitor);
3218         fprintf (outfile, "\nMonitor lock summary\n");
3219         for (i = 0; i < num_monitors; ++i) {
3220                 MonitorDesc *mdesc = monitors [i];
3221                 fprintf (outfile, "\tLock object %p: %d contentions\n", (void*)mdesc->objid, (int)mdesc->contentions);
3222                 fprintf (outfile, "\t\t%.6f secs total wait time, %.6f max, %.6f average\n",
3223                         mdesc->wait_time/1000000000.0, mdesc->max_wait_time/1000000000.0, mdesc->wait_time/1000000000.0/mdesc->contentions);
3224                 dump_traces (&mdesc->traces, "contentions");
3225         }
3226         fprintf (outfile, "\tLock contentions: %llu\n", (unsigned long long) monitor_contention);
3227         fprintf (outfile, "\tLock acquired: %llu\n", (unsigned long long) monitor_acquired);
3228         fprintf (outfile, "\tLock failures: %llu\n", (unsigned long long) monitor_failed);
3229 }
3230
3231 static void
3232 dump_gcs (void)
3233 {
3234         int i;
3235         fprintf (outfile, "\nGC summary\n");
3236         fprintf (outfile, "\tGC resizes: %d\n", gc_resizes);
3237         fprintf (outfile, "\tMax heap size: %llu\n", (unsigned long long) max_heap_size);
3238         fprintf (outfile, "\tObject moves: %llu\n", (unsigned long long) gc_object_moves);
3239         for (i = 0; i < 3; ++i) {
3240                 if (!gc_info [i].count)
3241                         continue;
3242                 fprintf (outfile, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
3243                         i, gc_info [i].count,
3244                         (unsigned long long) (gc_info [i].max_time / 1000),
3245                         (unsigned long long) (gc_info [i].total_time / 1000),
3246                         (unsigned long long) (gc_info [i].total_time / gc_info [i].count / 1000));
3247         }
3248         for (i = 0; i < 3; ++i) {
3249                 if (!handle_info [i].max_live)
3250                         continue;
3251                 fprintf (outfile, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
3252                         get_handle_name (i),
3253                         (unsigned long long) (handle_info [i].created),
3254                         (unsigned long long) (handle_info [i].destroyed),
3255                         (unsigned long long) (handle_info [i].max_live));
3256                 dump_traces (&handle_info [i].traces, "created");
3257                 dump_traces (&handle_info [i].destroy_traces, "destroyed");
3258         }
3259 }
3260
3261 static void
3262 dump_jit (void)
3263 {
3264         int i;
3265         int code_size = 0;
3266         int compiled_methods = 0;
3267         MethodDesc* m;
3268         fprintf (outfile, "\nJIT summary\n");
3269         for (i = 0; i < HASH_SIZE; ++i) {
3270                 m = method_hash [i];
3271                 for (m = method_hash [i]; m; m = m->next) {
3272                         if (!m->code || m->ignore_jit)
3273                                 continue;
3274                         compiled_methods++;
3275                         code_size += m->len;
3276                 }
3277         }
3278         fprintf (outfile, "\tCompiled methods: %d\n", compiled_methods);
3279         fprintf (outfile, "\tGenerated code size: %d\n", code_size);
3280         fprintf (outfile, "\tJIT helpers: %d\n", num_jit_helpers);
3281         fprintf (outfile, "\tJIT helpers code size: %d\n", jit_helpers_code_size);
3282 }
3283
3284 static void
3285 dump_allocations (void)
3286 {
3287         int i, c;
3288         intptr_t allocs = 0;
3289         uint64_t size = 0;
3290         int header_done = 0;
3291         ClassDesc **classes = (ClassDesc **)malloc (num_classes * sizeof (void*));
3292         ClassDesc *cd;
3293         c = 0;
3294         for (i = 0; i < HASH_SIZE; ++i) {
3295                 cd = class_hash [i];
3296                 while (cd) {
3297                         classes [c++] = cd;
3298                         cd = cd->next;
3299                 }
3300         }
3301         qsort (classes, num_classes, sizeof (void*), compare_class);
3302         for (i = 0; i < num_classes; ++i) {
3303                 cd = classes [i];
3304                 if (!cd->allocs)
3305                         continue;
3306                 allocs += cd->allocs;
3307                 size += cd->alloc_size;
3308                 if (!header_done++) {
3309                         fprintf (outfile, "\nAllocation summary\n");
3310                         fprintf (outfile, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
3311                 }
3312                 fprintf (outfile, "%10llu %10zd %8llu %s\n",
3313                         (unsigned long long) (cd->alloc_size),
3314                         cd->allocs,
3315                         (unsigned long long) (cd->alloc_size / cd->allocs),
3316                         cd->name);
3317                 dump_traces (&cd->traces, "bytes");
3318         }
3319         if (allocs)
3320                 fprintf (outfile, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size, allocs);
3321 }
3322
3323 enum {
3324         METHOD_SORT_TOTAL,
3325         METHOD_SORT_SELF,
3326         METHOD_SORT_CALLS
3327 };
3328
3329 static int method_sort_mode = METHOD_SORT_TOTAL;
3330
3331 static int
3332 compare_method (const void *a, const void *b)
3333 {
3334         MethodDesc *const *A = (MethodDesc *const *)a;
3335         MethodDesc *const *B = (MethodDesc *const *)b;
3336         uint64_t vala, valb;
3337         if (method_sort_mode == METHOD_SORT_SELF) {
3338                 vala = (*A)->self_time;
3339                 valb = (*B)->self_time;
3340         } else if (method_sort_mode == METHOD_SORT_CALLS) {
3341                 vala = (*A)->calls;
3342                 valb = (*B)->calls;
3343         } else {
3344                 vala = (*A)->total_time;
3345                 valb = (*B)->total_time;
3346         }
3347         if (vala == valb)
3348                 return 0;
3349         if (valb < vala)
3350                 return -1;
3351         return 1;
3352 }
3353
3354 static void
3355 dump_metadata (void)
3356 {
3357         fprintf (outfile, "\nMetadata summary\n");
3358         fprintf (outfile, "\tLoaded images: %d\n", num_images);
3359         if (verbose) {
3360                 ImageDesc *image;
3361                 int i;
3362                 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
3363                         image = image_hash [i];
3364                         while (image) {
3365                                 fprintf (outfile, "\t\t%s\n", image->filename);
3366                                 image = image->next;
3367                         }
3368                 }
3369         }
3370         fprintf (outfile, "\tLoaded assemblies: %d\n", num_assemblies);
3371         if (verbose) {
3372                 AssemblyDesc *assembly;
3373                 int i;
3374                 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
3375                         assembly = assembly_hash [i];
3376                         while (assembly) {
3377                                 fprintf (outfile, "\t\t%s\n", assembly->asmname);
3378                                 assembly = assembly->next;
3379                         }
3380                 }
3381         }
3382 }
3383
3384 static void
3385 dump_methods (void)
3386 {
3387         int i, c;
3388         uint64_t calls = 0;
3389         int header_done = 0;
3390         MethodDesc **methods = (MethodDesc **)malloc (num_methods * sizeof (void*));
3391         MethodDesc *cd;
3392         c = 0;
3393         for (i = 0; i < HASH_SIZE; ++i) {
3394                 cd = method_hash [i];
3395                 while (cd) {
3396                         cd->total_time = cd->self_time + cd->callee_time;
3397                         methods [c++] = cd;
3398                         cd = cd->next;
3399                 }
3400         }
3401         qsort (methods, num_methods, sizeof (void*), compare_method);
3402         for (i = 0; i < num_methods; ++i) {
3403                 uint64_t msecs;
3404                 uint64_t smsecs;
3405                 cd = methods [i];
3406                 if (!cd->calls)
3407                         continue;
3408                 calls += cd->calls;
3409                 msecs = cd->total_time / 1000000;
3410                 smsecs = (cd->total_time - cd->callee_time) / 1000000;
3411                 if (!msecs && !verbose)
3412                         continue;
3413                 if (!header_done++) {
3414                         fprintf (outfile, "\nMethod call summary\n");
3415                         fprintf (outfile, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
3416                 }
3417                 fprintf (outfile, "%8llu %8llu %10llu %s\n",
3418                         (unsigned long long) (msecs),
3419                         (unsigned long long) (smsecs),
3420                         (unsigned long long) (cd->calls),
3421                         cd->name);
3422                 dump_traces (&cd->traces, "calls");
3423         }
3424         if (calls)
3425                 fprintf (outfile, "Total calls: %llu\n", (unsigned long long) calls);
3426 }
3427
3428 static int
3429 compare_heap_class (const void *a, const void *b)
3430 {
3431         HeapClassDesc *const *A = (HeapClassDesc *const *)a;
3432         HeapClassDesc *const *B = (HeapClassDesc *const *)b;
3433         uint64_t vala, valb;
3434         if (alloc_sort_mode == ALLOC_SORT_BYTES) {
3435                 vala = (*A)->total_size;
3436                 valb = (*B)->total_size;
3437         } else {
3438                 vala = (*A)->count;
3439                 valb = (*B)->count;
3440         }
3441         if (valb == vala)
3442                 return 0;
3443         if (valb < vala)
3444                 return -1;
3445         return 1;
3446 }
3447
3448 static int
3449 compare_rev_class (const void *a, const void *b)
3450 {
3451         const HeapClassRevRef *A = (const HeapClassRevRef *)a;
3452         const HeapClassRevRef *B = (const HeapClassRevRef *)b;
3453         if (B->count == A->count)
3454                 return 0;
3455         if (B->count < A->count)
3456                 return -1;
3457         return 1;
3458 }
3459
3460 static void
3461 dump_rev_claases (HeapClassRevRef *revs, int count)
3462 {
3463         int j;
3464         if (!show_traces)
3465                 return;
3466         if (!count)
3467                 return;
3468         for (j = 0; j < count; ++j) {
3469                 HeapClassDesc *cd = revs [j].klass;
3470                 fprintf (outfile, "\t\t%llu references from: %s\n",
3471                         (unsigned long long) (revs [j].count),
3472                         cd->klass->name);
3473         }
3474 }
3475
3476 static void
3477 heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
3478 {
3479         uint64_t size = 0;
3480         uint64_t count = 0;
3481         int ccount = 0;
3482         int i;
3483         HeapClassDesc *cd;
3484         HeapClassDesc **sorted;
3485         sorted = (HeapClassDesc **)malloc (sizeof (void*) * hs->class_count);
3486         for (i = 0; i < hs->hash_size; ++i) {
3487                 cd = hs->class_hash [i];
3488                 if (!cd)
3489                         continue;
3490                 count += cd->count;
3491                 size += cd->total_size;
3492                 sorted [ccount++] = cd;
3493         }
3494         hs->sorted = sorted;
3495         qsort (sorted, ccount, sizeof (void*), compare_heap_class);
3496         fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %zd\n",
3497                 hs_num,
3498                 (hs->timestamp - startup_time)/1000000000.0,
3499                 (unsigned long long) (size),
3500                 (unsigned long long) (count),
3501                 ccount, hs->num_roots);
3502         if (!verbose && ccount > 30)
3503                 ccount = 30;
3504         fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
3505         for (i = 0; i < ccount; ++i) {
3506                 HeapClassRevRef *rev_sorted;
3507                 int j, k;
3508                 HeapClassDesc *ocd = NULL;
3509                 cd = sorted [i];
3510                 if (last_hs)
3511                         ocd = heap_class_lookup (last_hs, cd->klass);
3512                 fprintf (outfile, "\t%10llu %10llu %8llu %s",
3513                         (unsigned long long) (cd->total_size),
3514                         (unsigned long long) (cd->count),
3515                         (unsigned long long) (cd->total_size / cd->count),
3516                         cd->klass->name);
3517                 if (ocd) {
3518                         int64_t bdiff = cd->total_size - ocd->total_size;
3519                         int64_t cdiff = cd->count - ocd->count;
3520                         fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", (long long) bdiff, (long long) cdiff);
3521                 } else {
3522                         fprintf (outfile, "\n");
3523                 }
3524                 if (!collect_traces)
3525                         continue;
3526                 rev_sorted = (HeapClassRevRef *)malloc (cd->rev_count * sizeof (HeapClassRevRef));
3527                 k = 0;
3528                 for (j = 0; j < cd->rev_hash_size; ++j) {
3529                         if (cd->rev_hash [j].klass)
3530                                 rev_sorted [k++] = cd->rev_hash [j];
3531                 }
3532                 assert (cd->rev_count == k);
3533                 qsort (rev_sorted, cd->rev_count, sizeof (HeapClassRevRef), compare_rev_class);
3534                 if (cd->root_references)
3535                         fprintf (outfile, "\t\t%zd root references (%zd pinning)\n", cd->root_references, cd->pinned_references);
3536                 dump_rev_claases (rev_sorted, cd->rev_count);
3537                 free (rev_sorted);
3538         }
3539         free (sorted);
3540 }
3541
3542 static int
3543 compare_heap_shots (const void *a, const void *b)
3544 {
3545         HeapShot *const *A = (HeapShot *const *)a;
3546         HeapShot *const *B = (HeapShot *const *)b;
3547         if ((*B)->timestamp == (*A)->timestamp)
3548                 return 0;
3549         if ((*B)->timestamp > (*A)->timestamp)
3550                 return -1;
3551         return 1;
3552 }
3553
3554 static void
3555 dump_heap_shots (void)
3556 {
3557         HeapShot **hs_sorted;
3558         HeapShot *hs;
3559         HeapShot *last_hs = NULL;
3560         int i;
3561         if (!heap_shots)
3562                 return;
3563         hs_sorted = (HeapShot **)malloc (num_heap_shots * sizeof (void*));
3564         fprintf (outfile, "\nHeap shot summary\n");
3565         i = 0;
3566         for (hs = heap_shots; hs; hs = hs->next)
3567                 hs_sorted [i++] = hs;
3568         qsort (hs_sorted, num_heap_shots, sizeof (void*), compare_heap_shots);
3569         for (i = 0; i < num_heap_shots; ++i) {
3570                 hs = hs_sorted [i];
3571                 heap_shot_summary (hs, i, last_hs);
3572                 last_hs = hs;
3573         }
3574 }
3575
3576 /* This is a very basic escape function that escapes < > and &
3577    Ideally we'd use g_markup_escape_string but that function isn't
3578          available in Mono's eglib. This was written without looking at the
3579          source of that function in glib. */
3580 static char *
3581 escape_string_for_xml (const char *string)
3582 {
3583         GString *string_builder = g_string_new (NULL);
3584         const char *start, *p;
3585
3586         start = p = string;
3587         while (*p) {
3588                 while (*p && *p != '&' && *p != '<' && *p != '>')
3589                         p++;
3590
3591                 g_string_append_len (string_builder, start, p - start);
3592
3593                 if (*p == '\0')
3594                         break;
3595
3596                 switch (*p) {
3597                 case '<':
3598                         g_string_append (string_builder, "&lt;");
3599                         break;
3600
3601                 case '>':
3602                         g_string_append (string_builder, "&gt;");
3603                         break;
3604
3605                 case '&':
3606                         g_string_append (string_builder, "&amp;");
3607                         break;
3608
3609                 default:
3610                         break;
3611                 }
3612
3613                 p++;
3614                 start = p;
3615         }
3616
3617         return g_string_free (string_builder, FALSE);
3618 }
3619
3620 static int
3621 sort_assemblies (gconstpointer a, gconstpointer b)
3622 {
3623         CoverageAssembly *assembly_a = *(CoverageAssembly **)a;
3624         CoverageAssembly *assembly_b = *(CoverageAssembly **)b;
3625
3626         if (assembly_a->name == NULL && assembly_b->name == NULL)
3627                 return 0;
3628         else if (assembly_a->name == NULL)
3629                 return -1;
3630         else if (assembly_b->name == NULL)
3631                 return 1;
3632
3633         return strcmp (assembly_a->name, assembly_b->name);
3634 }
3635
3636 static void
3637 dump_coverage (void)
3638 {
3639         if (!coverage_methods && !coverage_assemblies)
3640                 return;
3641
3642         gather_coverage_statements ();
3643         fprintf (outfile, "\nCoverage Summary:\n");
3644
3645         if (coverage_outfile) {
3646                 fprintf (coverage_outfile, "<?xml version=\"1.0\"?>\n");
3647                 fprintf (coverage_outfile, "<coverage version=\"0.3\">\n");
3648         }
3649
3650         g_ptr_array_sort (coverage_assemblies, sort_assemblies);
3651
3652         for (guint i = 0; i < coverage_assemblies->len; i++) {
3653                 CoverageAssembly *assembly = (CoverageAssembly *)coverage_assemblies->pdata[i];
3654                 GPtrArray *classes;
3655
3656                 if (assembly->number_of_methods != 0) {
3657                         int percentage = ((assembly->fully_covered + assembly->partially_covered) * 100) / assembly->number_of_methods;
3658                         fprintf (outfile, "\t%s (%s) %d%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, percentage, assembly->number_of_methods, assembly->fully_covered);
3659                 } else
3660                         fprintf (outfile, "\t%s (%s) ?%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, assembly->number_of_methods, assembly->fully_covered);
3661
3662                 if (coverage_outfile) {
3663                         char *escaped_name, *escaped_filename;
3664                         escaped_name = escape_string_for_xml (assembly->name);
3665                         escaped_filename = escape_string_for_xml (assembly->filename);
3666
3667                         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);
3668
3669                         g_free (escaped_name);
3670                         g_free (escaped_filename);
3671                 }
3672
3673                 classes = (GPtrArray *)g_hash_table_lookup (coverage_assembly_classes, assembly->name);
3674                 if (classes) {
3675                         for (guint j = 0; j < classes->len; j++) {
3676                                 CoverageClass *klass = (CoverageClass *)classes->pdata [j];
3677
3678                                 if (klass->number_of_methods > 0) {
3679                                         int percentage = ((klass->fully_covered + klass->partially_covered) * 100) / klass->number_of_methods;
3680                                         fprintf (outfile, "\t\t%s %d%% covered (%d methods - %d covered)\n", klass->class_name, percentage, klass->number_of_methods, klass->fully_covered);
3681                                 } else
3682                                         fprintf (outfile, "\t\t%s ?%% covered (%d methods - %d covered)\n", klass->class_name, klass->number_of_methods, klass->fully_covered);
3683
3684                                 if (coverage_outfile) {
3685                                         char *escaped_name;
3686                                         escaped_name = escape_string_for_xml (klass->class_name);
3687
3688                                         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);
3689                                         g_free (escaped_name);
3690                                 }
3691                         }
3692                 }
3693         }
3694
3695         for (guint i = 0; i < coverage_methods->len; i++) {
3696                 CoverageMethod *method = (CoverageMethod *)coverage_methods->pdata [i];
3697
3698                 if (coverage_outfile) {
3699                         char *escaped_assembly, *escaped_class, *escaped_method, *escaped_sig, *escaped_filename;
3700
3701                         escaped_assembly = escape_string_for_xml (method->assembly_name);
3702                         escaped_class = escape_string_for_xml (method->class_name);
3703                         escaped_method = escape_string_for_xml (method->method_name);
3704                         escaped_sig = escape_string_for_xml (method->method_signature);
3705                         escaped_filename = escape_string_for_xml (method->filename);
3706
3707                         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);
3708
3709                         g_free (escaped_assembly);
3710                         g_free (escaped_class);
3711                         g_free (escaped_method);
3712                         g_free (escaped_sig);
3713                         g_free (escaped_filename);
3714
3715                         for (guint j = 0; j < method->coverage->len; j++) {
3716                                 CoverageCoverage *coverage = (CoverageCoverage *)method->coverage->pdata [j];
3717                                 fprintf (coverage_outfile, "\t\t<statement offset=\"%d\" counter=\"%d\" line=\"%d\" column=\"%d\"/>\n", coverage->offset, coverage->count, coverage->line, coverage->column);
3718                         }
3719                         fprintf (coverage_outfile, "\t</method>\n");
3720                 }
3721         }
3722
3723         if (coverage_outfile) {
3724                 fprintf (coverage_outfile, "</coverage>\n");
3725                 fclose (coverage_outfile);
3726                 coverage_outfile = NULL;
3727         }
3728 }
3729
3730 #define DUMP_EVENT_STAT(EVENT,SUBTYPE) dump_event (#EVENT, #SUBTYPE, EVENT, SUBTYPE);
3731
3732 static void
3733 dump_event (const char *event_name, const char *subtype_name, int event, int subtype)
3734 {
3735         int idx = event | subtype;
3736         EventStat evt = stats [idx];
3737         if (!evt.count)
3738                 return;
3739
3740         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);
3741 }
3742
3743 static void
3744 dump_stats (void)
3745 {
3746         fprintf (outfile, "\nMlpd statistics\n");
3747         fprintf (outfile, "\tBuffer count %d\toverhead %d (%d bytes per header)\n", buffer_count, buffer_count * BUFFER_HEADER_SIZE, BUFFER_HEADER_SIZE);
3748         fprintf (outfile, "\nEvent details:\n");
3749
3750         DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_NO_BT);
3751         DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_BT);
3752
3753         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_EVENT);
3754         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_RESIZE);
3755         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_MOVE);
3756         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED);
3757         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED);
3758         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED_BT);
3759         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED_BT);
3760
3761         DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_LOAD);
3762         DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_UNLOAD);
3763
3764         DUMP_EVENT_STAT (TYPE_METHOD, TYPE_LEAVE);
3765         DUMP_EVENT_STAT (TYPE_METHOD, TYPE_ENTER);
3766         DUMP_EVENT_STAT (TYPE_METHOD, TYPE_EXC_LEAVE);
3767         DUMP_EVENT_STAT (TYPE_METHOD, TYPE_JIT);
3768
3769         DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_THROW);
3770         DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_CLAUSE);
3771         DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_EXCEPTION_BT);
3772
3773         DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_NO_BT);
3774         DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_BT);
3775
3776         DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_START);
3777         DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_END);
3778         DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_OBJECT);
3779         DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_ROOT);
3780
3781         DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_HIT);
3782         DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_USYM);
3783         DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_UBIN);
3784         DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS_DESC);
3785         DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS);
3786
3787         DUMP_EVENT_STAT (TYPE_RUNTIME, TYPE_JITHELPER);
3788
3789         DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_ASSEMBLY);
3790         DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_METHOD);
3791         DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_STATEMENT);
3792         DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_CLASS);
3793 }
3794
3795
3796
3797 static void
3798 flush_context (ProfContext *ctx)
3799 {
3800         ThreadContext *thread;
3801         /* FIXME: sometimes there are leftovers: indagate */
3802         for (thread = ctx->threads; thread; thread = thread->next) {
3803                 while (thread->stack_id) {
3804                         if (debug)
3805                                 fprintf (outfile, "thread %p has %d items on stack\n", (void*)thread->thread_id, thread->stack_id);
3806                         pop_method (thread, thread->stack [thread->stack_id - 1], thread->last_time);
3807                 }
3808         }
3809 }
3810
3811 static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,heapshot,counters,coverage";
3812
3813 static const char*
3814 match_option (const char *p, const char *opt)
3815 {
3816         int len = strlen (opt);
3817         if (strncmp (p, opt, len) == 0) {
3818                 if (p [len] == ',')
3819                         len++;
3820                 return p + len;
3821         }
3822         return p;
3823 }
3824
3825 static int
3826 print_reports (ProfContext *ctx, const char *reps, int parse_only)
3827 {
3828         const char *opt;
3829         const char *p;
3830         for (p = reps; *p; p = opt) {
3831                 if ((opt = match_option (p, "header")) != p) {
3832                         if (!parse_only)
3833                                 dump_header (ctx);
3834                         continue;
3835                 }
3836                 if ((opt = match_option (p, "thread")) != p) {
3837                         if (!parse_only)
3838                                 dump_threads (ctx);
3839                         continue;
3840                 }
3841                 if ((opt = match_option (p, "domain")) != p) {
3842                         if (!parse_only)
3843                                 dump_domains (ctx);
3844                         continue;
3845                 }
3846                 if ((opt = match_option (p, "context")) != p) {
3847                         if (!parse_only)
3848                                 dump_remctxs (ctx);
3849                         continue;
3850                 }
3851                 if ((opt = match_option (p, "gc")) != p) {
3852                         if (!parse_only)
3853                                 dump_gcs ();
3854                         continue;
3855                 }
3856                 if ((opt = match_option (p, "jit")) != p) {
3857                         if (!parse_only)
3858                                 dump_jit ();
3859                         continue;
3860                 }
3861                 if ((opt = match_option (p, "alloc")) != p) {
3862                         if (!parse_only)
3863                                 dump_allocations ();
3864                         continue;
3865                 }
3866                 if ((opt = match_option (p, "call")) != p) {
3867                         if (!parse_only)
3868                                 dump_methods ();
3869                         continue;
3870                 }
3871                 if ((opt = match_option (p, "metadata")) != p) {
3872                         if (!parse_only)
3873                                 dump_metadata ();
3874                         continue;
3875                 }
3876                 if ((opt = match_option (p, "exception")) != p) {
3877                         if (!parse_only)
3878                                 dump_exceptions ();
3879                         continue;
3880                 }
3881                 if ((opt = match_option (p, "monitor")) != p) {
3882                         if (!parse_only)
3883                                 dump_monitors ();
3884                         continue;
3885                 }
3886                 if ((opt = match_option (p, "heapshot")) != p) {
3887                         if (!parse_only)
3888                                 dump_heap_shots ();
3889                         continue;
3890                 }
3891                 if ((opt = match_option (p, "sample")) != p) {
3892                         if (!parse_only)
3893                                 dump_samples ();
3894                         continue;
3895                 }
3896                 if ((opt = match_option (p, "counters")) != p) {
3897                         if (!parse_only)
3898                                 dump_counters ();
3899                         continue;
3900                 }
3901                 if ((opt = match_option (p, "coverage")) != p) {
3902                         if (!parse_only)
3903                                 dump_coverage ();
3904                         continue;
3905                 }
3906                 if ((opt = match_option (p, "stats")) != p) {
3907                         if (!parse_only)
3908                                 dump_stats ();
3909                         continue;
3910                 }
3911                 return 0;
3912         }
3913         return 1;
3914 }
3915
3916 static int
3917 add_find_spec (const char *p)
3918 {
3919         if (p [0] == 'S' && p [1] == ':') {
3920                 char *vale;
3921                 find_size = strtoul (p + 2, &vale, 10);
3922                 return 1;
3923         } else if (p [0] == 'T' && p [1] == ':') {
3924                 find_name = p + 2;
3925                 return 1;
3926         }
3927         return 0;
3928 }
3929
3930 static void
3931 usage (void)
3932 {
3933         printf ("Mono log profiler report version %d.%d\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR);
3934         printf ("Usage: mprof-report [OPTIONS] FILENAME\n");
3935         printf ("FILENAME can be '-' to read from standard input.\n");
3936         printf ("Options:\n");
3937         printf ("\t--help               display this help\n");
3938         printf ("\t--out=FILE           write to FILE instead of stdout\n");
3939         printf ("\t--traces             collect and show backtraces\n");
3940         printf ("\t--maxframes=NUM      limit backtraces to NUM entries\n");
3941         printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
3942         printf ("\t                     %s\n", reports);
3943         printf ("\t--method-sort=MODE   sort methods according to MODE: total, self, calls\n");
3944         printf ("\t--alloc-sort=MODE    sort allocations according to MODE: bytes, count\n");
3945         printf ("\t--counters-sort=MODE sort counters according to MODE: time, category\n");
3946         printf ("\t                     only accessible in verbose mode\n");
3947         printf ("\t--track=OB1[,OB2...] track what happens to objects OBJ1, O2 etc.\n");
3948         printf ("\t--find=FINDSPEC      find and track objects matching FINFSPEC, where FINDSPEC is:\n");
3949         printf ("\t                     S:minimum_size or T:partial_name\n");
3950         printf ("\t--thread=THREADID    consider just the data for thread THREADID\n");
3951         printf ("\t--time=FROM-TO       consider data FROM seconds from startup up to TO seconds\n");
3952         printf ("\t--verbose            increase verbosity level\n");
3953         printf ("\t--debug              display decoding debug info for mprof-report devs\n");
3954         printf ("\t--coverage-out=FILE  write the coverage info to FILE as XML\n");
3955 }
3956
3957 int
3958 main (int argc, char *argv[])
3959 {
3960         ProfContext *ctx;
3961         int i;
3962         outfile = stdout;
3963         for (i = 1; i < argc; ++i) {
3964                 if (strcmp ("--debug", argv [i]) == 0) {
3965                         debug++;
3966                 } else if (strcmp ("--help", argv [i]) == 0) {
3967                         usage ();
3968                         return 0;
3969                 } else if (strncmp ("--alloc-sort=", argv [i], 13) == 0) {
3970                         const char *val = argv [i] + 13;
3971                         if (strcmp (val, "bytes") == 0) {
3972                                 alloc_sort_mode = ALLOC_SORT_BYTES;
3973                         } else if (strcmp (val, "count") == 0) {
3974                                 alloc_sort_mode = ALLOC_SORT_COUNT;
3975                         } else {
3976                                 usage ();
3977                                 return 1;
3978                         }
3979                 } else if (strncmp ("--method-sort=", argv [i], 14) == 0) {
3980                         const char *val = argv [i] + 14;
3981                         if (strcmp (val, "total") == 0) {
3982                                 method_sort_mode = METHOD_SORT_TOTAL;
3983                         } else if (strcmp (val, "self") == 0) {
3984                                 method_sort_mode = METHOD_SORT_SELF;
3985                         } else if (strcmp (val, "calls") == 0) {
3986                                 method_sort_mode = METHOD_SORT_CALLS;
3987                         } else {
3988                                 usage ();
3989                                 return 1;
3990                         }
3991                 } else if (strncmp ("--counters-sort=", argv [i], 16) == 0) {
3992                         const char *val = argv [i] + 16;
3993                         if (strcmp (val, "time") == 0) {
3994                                 counters_sort_mode = COUNTERS_SORT_TIME;
3995                         } else if (strcmp (val, "category") == 0) {
3996                                 counters_sort_mode = COUNTERS_SORT_CATEGORY;
3997                         } else {
3998                                 usage ();
3999                                 return 1;
4000                         }
4001                 } else if (strncmp ("--reports=", argv [i], 10) == 0) {
4002                         const char *val = argv [i] + 10;
4003                         if (!print_reports (NULL, val, 1)) {
4004                                 usage ();
4005                                 return 1;
4006                         }
4007                         reports = val;
4008                 } else if (strncmp ("--out=", argv [i], 6) == 0) {
4009                         const char *val = argv [i] + 6;
4010                         outfile = fopen (val, "w");
4011                         if (!outfile) {
4012                                 printf ("Cannot open output file: %s\n", val);
4013                                 return 1;
4014                         }
4015                 } else if (strncmp ("--maxframes=", argv [i], 12) == 0) {
4016                         const char *val = argv [i] + 12;
4017                         char *vale;
4018                         trace_max = strtoul (val, &vale, 10);
4019                 } else if (strncmp ("--find=", argv [i], 7) == 0) {
4020                         const char *val = argv [i] + 7;
4021                         if (!add_find_spec (val)) {
4022                                 usage ();
4023                                 return 1;
4024                         }
4025                 } else if (strncmp ("--track=", argv [i], 8) == 0) {
4026                         const char *val = argv [i] + 8;
4027                         char *vale;
4028                         while (*val) {
4029                                 uintptr_t tracked_obj;
4030                                 if (*val == ',') {
4031                                         val++;
4032                                         continue;
4033                                 }
4034                                 tracked_obj = strtoul (val, &vale, 0);
4035                                 found_object (tracked_obj);
4036                                 val = vale;
4037                         }
4038                 } else if (strncmp ("--thread=", argv [i], 9) == 0) {
4039                         const char *val = argv [i] + 9;
4040                         char *vale;
4041                         thread_filter = strtoul (val, &vale, 0);
4042                 } else if (strncmp ("--time=", argv [i], 7) == 0) {
4043                         char *val = pstrdup (argv [i] + 7);
4044                         double from_secs, to_secs;
4045                         char *top = strchr (val, '-');
4046                         if (!top) {
4047                                 usage ();
4048                                 return 1;
4049                         }
4050                         *top++ = 0;
4051                         from_secs = atof (val);
4052                         to_secs = atof (top);
4053                         free (val);
4054                         if (from_secs > to_secs) {
4055                                 usage ();
4056                                 return 1;
4057                         }
4058                         time_from = from_secs * 1000000000;
4059                         time_to = to_secs * 1000000000;
4060                         use_time_filter = 1;
4061                 } else if (strcmp ("--verbose", argv [i]) == 0) {
4062                         verbose++;
4063                 } else if (strcmp ("--traces", argv [i]) == 0) {
4064                         show_traces = 1;
4065                         collect_traces = 1;
4066                 } else if (strncmp ("--coverage-out=", argv [i], 15) == 0) {
4067                         const char *val = argv [i] + 15;
4068                         coverage_outfile = fopen (val, "w");
4069                         if (!coverage_outfile) {
4070                                 printf ("Cannot open output file: %s\n", val);
4071                                 return 1;
4072                         }
4073                 } else {
4074                         break;
4075                 }
4076         }
4077         if (i >= argc) {
4078                 usage ();
4079                 return 2;
4080         }
4081         ctx = load_file (argv [i]);
4082         if (!ctx) {
4083                 printf ("Not a log profiler data file (or unsupported version).\n");
4084                 return 1;
4085         }
4086         while (decode_buffer (ctx));
4087         flush_context (ctx);
4088         if (num_tracked_objects)
4089                 return 0;
4090         print_reports (ctx, reports, 0);
4091         return 0;
4092 }