[mono-config] use right type for result of strlen
[mono.git] / mono / profiler / decode.c
1 /*
2  * decode.c: mprof-report program source: decode and analyze the log profiler data
3  *
4  * Authors:
5  *   Paolo Molaro (lupus@ximian.com)
6  *   Alex Rønne Petersen (alexrp@xamarin.com)
7  *
8  * Copyright 2010 Novell, Inc (http://www.novell.com)
9  */
10
11 /*
12  * The Coverage XML output schema
13  * <coverage>
14  *   <assembly/>
15  *   <class/>
16  *   <method>
17  *     <statement/>
18  *   </method>
19  * </coverage>
20  *
21  * Elements:
22  *   <coverage> - The root element of the documentation. It can contain any number of
23  *                <assembly>, <class> or <method> elements.
24  *                Attributes:
25  *                   - version: The version number for the file format - (eg: "0.3")
26  *   <assembly> - Contains data about assemblies. Has no child elements
27  *                Attributes:
28  *                   - name: The name of the assembly - (eg: "System.Xml")
29  *                   - guid: The GUID of the assembly
30  *                   - filename: The filename of the assembly
31  *                   - method-count: The number of methods in the assembly
32  *                   - full: The number of fully covered methods
33  *                   - partial: The number of partially covered methods
34  *   <class> - Contains data about classes. Has no child elements
35  *             Attributes:
36  *                - name: The name of the class
37  *                - method-count: The number of methods in the class
38  *                - full: The number of fully covered methods
39  *                - partial: The number of partially covered methods
40  *   <method> - Contains data about methods. Can contain any number of <statement> elements
41  *              Attributes:
42  *                 - assembly: The name of the parent assembly
43  *                 - class: The name of the parent class
44  *                 - name: The name of the method, with all it's parameters
45  *                 - filename: The name of the source file containing this method
46  *                 - token
47  *   <statement> - Contains data about IL statements. Has no child elements
48  *                 Attributes:
49  *                    - offset: The offset of the statement in the IL code after the previous
50  *                              statement's offset
51  *                    - counter: 1 if the line was covered, 0 if it was not
52  *                    - line: The line number in the parent method's file
53  *                    - column: The column on the line
54  */
55 #include <config.h>
56 #include "utils.c"
57 #include "proflog.h"
58 #include <string.h>
59 #include <assert.h>
60 #include <stdio.h>
61 #if !defined(__APPLE__) && !defined(__FreeBSD__)
62 #include <malloc.h>
63 #endif
64 #include <unistd.h>
65 #include <stdlib.h>
66 #if defined (HAVE_SYS_ZLIB)
67 #include <zlib.h>
68 #endif
69 #include <glib.h>
70 #include <mono/metadata/profiler.h>
71 #include <mono/metadata/object.h>
72 #include <mono/metadata/debug-helpers.h>
73 #include <mono/utils/mono-counters.h>
74
75 #define HASH_SIZE 9371
76 #define SMALL_HASH_SIZE 31
77
78 #if defined(__native_client__) || defined(__native_client_codegen__)
79 volatile int __nacl_thread_suspension_needed = 0;
80 void __nacl_suspend_thread_if_needed() {}
81 #endif
82
83 static int debug = 0;
84 static int collect_traces = 0;
85 static int show_traces = 0;
86 static int trace_max = 6;
87 static int verbose = 0;
88 static uintptr_t *tracked_objects = 0;
89 static int num_tracked_objects = 0;
90 static uintptr_t thread_filter = 0;
91 static uint64_t find_size = 0;
92 static const char* find_name = NULL;
93 static uint64_t time_from = 0;
94 static uint64_t time_to = 0xffffffffffffffffULL;
95 static int use_time_filter = 0;
96 static uint64_t startup_time = 0;
97 static FILE* outfile = NULL;
98 static FILE* coverage_outfile = NULL;
99
100 static int32_t
101 read_int16 (unsigned char *p)
102 {
103         int32_t value = *p++;
104         value |= (*p++) << 8;
105         return value;
106 }
107
108 static int32_t
109 read_int32 (unsigned char *p)
110 {
111         int32_t value = *p++;
112         value |= (*p++) << 8;
113         value |= (*p++) << 16;
114         value |= (uint32_t)(*p++) << 24;
115         return value;
116 }
117
118 static int64_t
119 read_int64 (unsigned char *p)
120 {
121         uint64_t value = *p++;
122         value |= (*p++) << 8;
123         value |= (*p++) << 16;
124         value |= (uint64_t)(*p++) << 24;
125         value |= (uint64_t)(*p++) << 32;
126         value |= (uint64_t)(*p++) << 40;
127         value |= (uint64_t)(*p++) << 48;
128         value |= (uint64_t)(*p++) << 54;
129         return value;
130 }
131
132 static char*
133 pstrdup (const char *s)
134 {
135         int len = strlen (s) + 1;
136         char *p = malloc (len);
137         memcpy (p, s, len);
138         return p;
139 }
140
141 typedef struct _CounterValue CounterValue;
142 struct _CounterValue {
143         uint64_t timestamp;
144         unsigned char *buffer;
145         CounterValue *next;
146 };
147
148 typedef struct _Counter Counter;
149 struct _Counter {
150         int index;
151         const char *section;
152         const char *name;
153         int type;
154         int unit;
155         int variance;
156         CounterValue *values;
157         CounterValue *values_last;
158 };
159
160 typedef struct _CounterList CounterList;
161 struct _CounterList {
162         Counter *counter;
163         CounterList *next;
164 };
165
166 typedef struct _CounterSection CounterSection;
167 struct _CounterSection {
168         const char *value;
169         CounterList *counters;
170         CounterList *counters_last;
171         CounterSection *next;
172 };
173
174 typedef struct _CounterTimestamp CounterTimestamp;
175 struct _CounterTimestamp {
176         uint64_t value;
177         CounterSection *sections;
178         CounterSection *sections_last;
179         CounterTimestamp *next;
180 };
181
182 static CounterList *counters = NULL;
183 static CounterSection *counters_sections = NULL;
184 static CounterTimestamp *counters_timestamps = NULL;
185
186 enum {
187         COUNTERS_SORT_TIME,
188         COUNTERS_SORT_CATEGORY
189 };
190
191 static int counters_sort_mode = COUNTERS_SORT_TIME;
192
193 static void
194 add_counter_to_section (Counter *counter)
195 {
196         CounterSection *csection, *s;
197         CounterList *clist;
198
199         clist = calloc (1, sizeof (CounterList));
200         clist->counter = counter;
201
202         for (csection = counters_sections; csection; csection = csection->next) {
203                 if (strcmp (csection->value, counter->section) == 0) {
204                         /* If section exist */
205                         if (!csection->counters)
206                                 csection->counters = clist;
207                         else
208                                 csection->counters_last->next = clist;
209                         csection->counters_last = clist;
210                         return;
211                 }
212         }
213
214         /* If section does not exist */
215         csection = calloc (1, sizeof (CounterSection));
216         csection->value = counter->section;
217         csection->counters = clist;
218         csection->counters_last = clist;
219
220         if (!counters_sections) {
221                 counters_sections = csection;
222         } else {
223                 s = counters_sections;
224                 while (s->next)
225                         s = s->next;
226                 s->next = csection;
227         }
228 }
229
230 static void
231 add_counter (const char *section, const char *name, int type, int unit, int variance, int index)
232 {
233         CounterList *list, *l;
234         Counter *counter;
235
236         for (list = counters; list; list = list->next)
237                 if (list->counter->index == index)
238                         return;
239
240         counter = calloc (1, sizeof (Counter));
241         counter->section = section;
242         counter->name = name;
243         counter->type = type;
244         counter->unit = unit;
245         counter->variance = variance;
246         counter->index = index;
247
248         list = calloc (1, sizeof (CounterList));
249         list->counter = counter;
250
251         if (!counters) {
252                 counters = list;
253         } else {
254                 l = counters;
255                 while (l->next)
256                         l = l->next;
257                 l->next = list;
258         }
259
260         if (counters_sort_mode == COUNTERS_SORT_CATEGORY || !verbose)
261                 add_counter_to_section (counter);
262 }
263
264 static void
265 add_counter_to_timestamp (uint64_t timestamp, Counter *counter)
266 {
267         CounterTimestamp *ctimestamp, *t;
268         CounterSection *csection;
269         CounterList *clist;
270
271         clist = calloc (1, sizeof (CounterList));
272         clist->counter = counter;
273
274         for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
275                 if (ctimestamp->value == timestamp) {
276                         for (csection = ctimestamp->sections; csection; csection = csection->next) {
277                                 if (strcmp (csection->value, counter->section) == 0) {
278                                         /* if timestamp exist and section exist */
279                                         if (!csection->counters)
280                                                 csection->counters = clist;
281                                         else
282                                                 csection->counters_last->next = clist;
283                                         csection->counters_last = clist;
284                                         return;
285                                 }
286                         }
287
288                         /* if timestamp exist and section does not exist */
289                         csection = calloc (1, sizeof (CounterSection));
290                         csection->value = counter->section;
291                         csection->counters = clist;
292                         csection->counters_last = clist;
293
294                         if (!ctimestamp->sections)
295                                 ctimestamp->sections = csection;
296                         else
297                                 ctimestamp->sections_last->next = csection;
298                         ctimestamp->sections_last = csection;
299                         return;
300                 }
301         }
302
303         /* If timestamp do not exist and section does not exist */
304         csection = calloc (1, sizeof (CounterSection));
305         csection->value = counter->section;
306         csection->counters = clist;
307         csection->counters_last = clist;
308
309         ctimestamp = calloc (1, sizeof (CounterTimestamp));
310         ctimestamp->value = timestamp;
311         ctimestamp->sections = csection;
312         ctimestamp->sections_last = csection;
313
314         if (!counters_timestamps) {
315                 counters_timestamps = ctimestamp;
316         } else {
317                 t = counters_timestamps;
318                 while (t->next)
319                         t = t->next;
320                 t->next = ctimestamp;
321         }
322 }
323
324 static void
325 add_counter_value (int index, CounterValue *value)
326 {
327         CounterList *list;
328
329         for (list = counters; list; list = list->next) {
330                 if (list->counter->index == index) {
331                         if (!list->counter->values)
332                                 list->counter->values = value;
333                         else
334                                 list->counter->values_last->next = value;
335                         list->counter->values_last = value;
336
337                         if (counters_sort_mode == COUNTERS_SORT_TIME)
338                                 add_counter_to_timestamp (value->timestamp, list->counter);
339
340                         return;
341                 }
342         }
343 }
344
345 static const char*
346 section_name (int section)
347 {
348         switch (section) {
349         case MONO_COUNTER_JIT: return "Mono JIT";
350         case MONO_COUNTER_GC: return "Mono GC";
351         case MONO_COUNTER_METADATA: return "Mono Metadata";
352         case MONO_COUNTER_GENERICS: return "Mono Generics";
353         case MONO_COUNTER_SECURITY: return "Mono Security";
354         case MONO_COUNTER_RUNTIME: return "Mono Runtime";
355         case MONO_COUNTER_SYSTEM: return "Mono System";
356         default: return "<unknown>";
357         }
358 }
359
360 static const char*
361 type_name (int type)
362 {
363         switch (type) {
364         case MONO_COUNTER_INT: return "Int";
365         case MONO_COUNTER_UINT: return "UInt";
366         case MONO_COUNTER_WORD: return "Word";
367         case MONO_COUNTER_LONG: return "Long";
368         case MONO_COUNTER_ULONG: return "ULong";
369         case MONO_COUNTER_DOUBLE: return "Double";
370         case MONO_COUNTER_STRING: return "String";
371         case MONO_COUNTER_TIME_INTERVAL: return "Time Interval";
372         default: return "<unknown>";
373         }
374 }
375
376 static const char*
377 unit_name (int unit)
378 {
379         switch (unit) {
380         case MONO_COUNTER_RAW: return "Raw";
381         case MONO_COUNTER_BYTES: return "Bytes";
382         case MONO_COUNTER_TIME: return "Time";
383         case MONO_COUNTER_COUNT: return "Count";
384         case MONO_COUNTER_PERCENTAGE: return "Percentage";
385         default: return "<unknown>";
386         }
387 }
388
389 static const char*
390 variance_name (int variance)
391 {
392         switch (variance) {
393         case MONO_COUNTER_MONOTONIC: return "Monotonic";
394         case MONO_COUNTER_CONSTANT: return "Constant";
395         case MONO_COUNTER_VARIABLE: return "Variable";
396         default: return "<unknown>";
397         }
398 }
399
400 static void
401 dump_counters_value (Counter *counter, const char *key_format, const char *key, void *value)
402 {
403         char format[32];
404
405         if (value == NULL) {
406                 snprintf (format, sizeof (format), "%s : %%s\n", key_format);
407                 fprintf (outfile, format, key, "<null>");
408         } else {
409                 switch (counter->type) {
410                 case MONO_COUNTER_INT:
411 #if SIZEOF_VOID_P == 4
412                 case MONO_COUNTER_WORD:
413 #endif
414                         snprintf (format, sizeof (format), "%s : %%d\n", key_format);
415                         fprintf (outfile, format, key, *(int32_t*)value);
416                         break;
417                 case MONO_COUNTER_UINT:
418                         snprintf (format, sizeof (format), "%s : %%u\n", key_format);
419                         fprintf (outfile, format, key, *(uint32_t*)value);
420                         break;
421                 case MONO_COUNTER_LONG:
422 #if SIZEOF_VOID_P == 8
423                 case MONO_COUNTER_WORD:
424 #endif
425                 case MONO_COUNTER_TIME_INTERVAL:
426                         if (counter->type == MONO_COUNTER_LONG && counter->unit == MONO_COUNTER_TIME) {
427                                 snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
428                                 fprintf (outfile, format, key, (double)*(int64_t*)value / 10000.0);
429                         } else if (counter->type == MONO_COUNTER_TIME_INTERVAL) {
430                                 snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
431                                 fprintf (outfile, format, key, (double)*(int64_t*)value / 1000.0);
432                         } else {
433                                 snprintf (format, sizeof (format), "%s : %%u\n", key_format);
434                                 fprintf (outfile, format, key, *(int64_t*)value);
435                         }
436                         break;
437                 case MONO_COUNTER_ULONG:
438                         snprintf (format, sizeof (format), "%s : %%llu\n", key_format);
439                         fprintf (outfile, format, key, *(uint64_t*)value);
440                         break;
441                 case MONO_COUNTER_DOUBLE:
442                         snprintf (format, sizeof (format), "%s : %%f\n", key_format);
443                         fprintf (outfile, format, key, *(double*)value);
444                         break;
445                 case MONO_COUNTER_STRING:
446                         snprintf (format, sizeof (format), "%s : %%s\n", key_format);
447                         fprintf (outfile, format, key, *(char*)value);
448                         break;
449                 }
450         }
451 }
452
453 static void
454 dump_counters (void)
455 {
456         Counter *counter;
457         CounterValue *cvalue;
458         CounterTimestamp *ctimestamp;
459         CounterSection *csection;
460         CounterList *clist;
461         char strtimestamp[17];
462         int i, section_printed;
463
464         fprintf (outfile, "\nCounters:\n");
465
466         if (!verbose) {
467                 char counters_to_print[][64] = {
468                         "Methods from AOT",
469                         "Methods JITted using mono JIT",
470                         "Methods JITted using LLVM",
471                         "Total time spent JITting (sec)",
472                         "User Time",
473                         "System Time",
474                         "Total Time",
475                         "Working Set",
476                         "Private Bytes",
477                         "Virtual Bytes",
478                         "Page Faults",
479                         "CPU Load Average - 1min",
480                         "CPU Load Average - 5min",
481                         "CPU Load Average - 15min",
482                         ""
483                 };
484
485                 for (csection = counters_sections; csection; csection = csection->next) {
486                         section_printed = 0;
487
488                         for (clist = csection->counters; clist; clist = clist->next) {
489                                 counter = clist->counter;
490                                 if (!counter->values_last)
491                                         continue;
492
493                                 for (i = 0; counters_to_print [i][0] != 0; i++) {
494                                         if (strcmp (counters_to_print [i], counter->name) == 0) {
495                                                 if (!section_printed) {
496                                                         fprintf (outfile, "\t%s:\n", csection->value);
497                                                         section_printed = 1;
498                                                 }
499
500                                                 dump_counters_value (counter, "\t\t%-30s", counter->name, counter->values_last->buffer);
501                                                 break;
502                                         }
503                                 }
504                         }
505                 }
506         } else if (counters_sort_mode == COUNTERS_SORT_TIME) {
507                 for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
508                         fprintf (outfile, "\t%llu:%02llu:%02llu:%02llu.%03llu:\n",
509                                 (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 / 24 % 1000),
510                                 (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 % 24),
511                                 (unsigned long long) (ctimestamp->value / 1000 / 60 % 60),
512                                 (unsigned long long) (ctimestamp->value / 1000 % 60),
513                                 (unsigned long long) (ctimestamp->value % 1000));
514
515                         for (csection = ctimestamp->sections; csection; csection = csection->next) {
516                                 fprintf (outfile, "\t\t%s:\n", csection->value);
517
518                                 for (clist = csection->counters; clist; clist = clist->next) {
519                                         counter = clist->counter;
520                                         for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
521                                                 if (cvalue->timestamp != ctimestamp->value)
522                                                         continue;
523
524                                                 dump_counters_value (counter, "\t\t\t%-30s", counter->name, cvalue->buffer);
525                                         }
526                                 }
527                         }
528                 }
529         } else if (counters_sort_mode == COUNTERS_SORT_CATEGORY) {
530                 for (csection = counters_sections; csection; csection = csection->next) {
531                         fprintf (outfile, "\t%s:\n", csection->value);
532
533                         for (clist = csection->counters; clist; clist = clist->next) {
534                                 counter = clist->counter;
535                                 fprintf (outfile, "\t\t%s: [type: %s, unit: %s, variance: %s]\n",
536                                         counter->name, type_name (counter->type), unit_name (counter->unit), variance_name (counter->variance));
537
538                                 for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
539                                         snprintf (strtimestamp, sizeof (strtimestamp), "%llu:%02llu:%02llu:%02llu.%03llu",
540                                                 (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 / 24 % 1000),
541                                                 (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 % 24),
542                                                 (unsigned long long) (cvalue->timestamp / 1000 / 60 % 60),
543                                                 (unsigned long long) (cvalue->timestamp / 1000 % 60),
544                                                 (unsigned long long) (cvalue->timestamp % 1000));
545
546                                         dump_counters_value (counter, "\t\t\t%s", strtimestamp, cvalue->buffer);
547                                 }
548                         }
549                 }
550         }
551 }
552
553 static int num_images;
554 typedef struct _ImageDesc ImageDesc;
555 struct _ImageDesc {
556         ImageDesc *next;
557         intptr_t image;
558         char *filename;
559 };
560
561 static ImageDesc* image_hash [SMALL_HASH_SIZE] = {0};
562
563 static void
564 add_image (intptr_t image, char *name)
565 {
566         int slot = ((image >> 2) & 0xffff) % SMALL_HASH_SIZE;
567         ImageDesc *cd = malloc (sizeof (ImageDesc));
568         cd->image = image;
569         cd->filename = pstrdup (name);
570         cd->next = image_hash [slot];
571         image_hash [slot] = cd;
572         num_images++;
573 }
574
575 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 = 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 = 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 = 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 = realloc (stat_samples, size_stat_samples * sizeof (uintptr_t));
747                 stat_sample_desc = 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 = a;
776         MethodDesc *const*B = 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 = a;
802         UnmanagedSymbol *const*B = 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 = a;
814         UnmanagedSymbol *const*B = 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 = realloc (usymbols, sizeof (void*) * new_size);
831                 usymbols_size = new_size;
832         }
833         sym = 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 = realloc (ubinaries, sizeof (void*) * new_size);
879                 ubinaries_size = new_size;
880         }
881         sym = 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 = 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 = 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 = 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 = calloc (sizeof (HeapShot), 1);
1137         hs->hash_size = 4;
1138         hs->class_hash = 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] = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = malloc (thread->stack_size * sizeof (void*));
1611         thread->time_stack = malloc (thread->stack_size * sizeof (uint64_t));
1612         thread->callee_time_stack = 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 = 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 = 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 = realloc (thread->stack, thread->stack_size * sizeof (void*));
1676                 thread->time_stack = realloc (thread->time_stack, thread->stack_size * sizeof (uint64_t));
1677                 thread->callee_time_stack = 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 = 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 = realloc (ctx->roots, new_size * sizeof (uintptr_t));
1765                 ctx->roots_extra = realloc (ctx->roots_extra, new_size * sizeof (uintptr_t));
1766                 ctx->roots_types = 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 = a;
1778         const CallContext *B = 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 } HandleInfo;
1849 static HandleInfo handle_info [4];
1850
1851 static const char*
1852 gc_event_name (int ev)
1853 {
1854         switch (ev) {
1855         case MONO_GC_EVENT_START: return "start";
1856         case MONO_GC_EVENT_MARK_START: return "mark start";
1857         case MONO_GC_EVENT_MARK_END: return "mark end";
1858         case MONO_GC_EVENT_RECLAIM_START: return "reclaim start";
1859         case MONO_GC_EVENT_RECLAIM_END: return "reclaim end";
1860         case MONO_GC_EVENT_END: return "end";
1861         case MONO_GC_EVENT_PRE_STOP_WORLD: return "pre stop";
1862         case MONO_GC_EVENT_POST_STOP_WORLD: return "post stop";
1863         case MONO_GC_EVENT_PRE_START_WORLD: return "pre start";
1864         case MONO_GC_EVENT_POST_START_WORLD: return "post start";
1865         default:
1866                 return "unknown";
1867         }
1868 }
1869
1870 static uint64_t clause_summary [MONO_EXCEPTION_CLAUSE_FAULT + 1];
1871 static uint64_t throw_count = 0;
1872 static TraceDesc exc_traces;
1873
1874 static const char*
1875 clause_name (int type)
1876 {
1877         switch (type) {
1878         case MONO_EXCEPTION_CLAUSE_NONE: return "catch";
1879         case MONO_EXCEPTION_CLAUSE_FILTER: return "filter";
1880         case MONO_EXCEPTION_CLAUSE_FINALLY: return "finally";
1881         case MONO_EXCEPTION_CLAUSE_FAULT: return "fault";
1882         default: return "invalid";
1883         }
1884 }
1885
1886 static uint64_t monitor_contention;
1887 static uint64_t monitor_failed;
1888 static uint64_t monitor_acquired;
1889
1890 struct _MonitorDesc {
1891         MonitorDesc *next;
1892         uintptr_t objid;
1893         uintptr_t contentions;
1894         uint64_t wait_time;
1895         uint64_t max_wait_time;
1896         TraceDesc traces;
1897 };
1898
1899 static MonitorDesc* monitor_hash [SMALL_HASH_SIZE] = {0};
1900 static int num_monitors = 0;
1901
1902 static MonitorDesc*
1903 lookup_monitor (uintptr_t objid)
1904 {
1905         int slot = ((objid >> 3) & 0xffff) % SMALL_HASH_SIZE;
1906         MonitorDesc *cd = monitor_hash [slot];
1907         while (cd && cd->objid != objid)
1908                 cd = cd->next;
1909         if (!cd) {
1910                 cd = calloc (sizeof (MonitorDesc), 1);
1911                 cd->objid = objid;
1912                 cd->next = monitor_hash [slot];
1913                 monitor_hash [slot] = cd;
1914                 num_monitors++;
1915         }
1916         return cd;
1917 }
1918
1919 static const char*
1920 monitor_ev_name (int ev)
1921 {
1922         switch (ev) {
1923         case MONO_PROFILER_MONITOR_CONTENTION: return "contended";
1924         case MONO_PROFILER_MONITOR_DONE: return "acquired";
1925         case MONO_PROFILER_MONITOR_FAIL: return "not taken";
1926         default: return "invalid";
1927         }
1928 }
1929
1930 static const char*
1931 get_handle_name (int htype)
1932 {
1933         switch (htype) {
1934         case 0: return "weak";
1935         case 1: return "weaktrack";
1936         case 2: return "normal";
1937         case 3: return "pinned";
1938         default: return "unknown";
1939         }
1940 }
1941
1942 static const char*
1943 get_root_name (int rtype)
1944 {
1945         switch (rtype & MONO_PROFILE_GC_ROOT_TYPEMASK) {
1946         case MONO_PROFILE_GC_ROOT_STACK: return "stack";
1947         case MONO_PROFILE_GC_ROOT_FINALIZER: return "finalizer";
1948         case MONO_PROFILE_GC_ROOT_HANDLE: return "handle";
1949         case MONO_PROFILE_GC_ROOT_OTHER: return "other";
1950         case MONO_PROFILE_GC_ROOT_MISC: return "misc";
1951         default: return "unknown";
1952         }
1953 }
1954
1955 static MethodDesc**
1956 decode_bt (MethodDesc** sframes, int *size, unsigned char *p, unsigned char **endp, intptr_t ptr_base)
1957 {
1958         MethodDesc **frames;
1959         int i;
1960         int flags = decode_uleb128 (p, &p);
1961         int count = decode_uleb128 (p, &p);
1962         if (flags != 0)
1963                 return NULL;
1964         if (count > *size)
1965                 frames = malloc (count * sizeof (void*));
1966         else
1967                 frames = sframes;
1968         for (i = 0; i < count; ++i) {
1969                 intptr_t ptrdiff = decode_sleb128 (p, &p);
1970                 frames [i] = lookup_method (ptr_base + ptrdiff);
1971         }
1972         *size = count;
1973         *endp = p;
1974         return frames;
1975 }
1976
1977 static void
1978 tracked_creation (uintptr_t obj, ClassDesc *cd, uint64_t size, BackTrace *bt, uint64_t timestamp)
1979 {
1980         int i;
1981         for (i = 0; i < num_tracked_objects; ++i) {
1982                 if (tracked_objects [i] != obj)
1983                         continue;
1984                 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);
1985                 if (bt && bt->count) {
1986                         int k;
1987                         for (k = 0; k < bt->count; ++k)
1988                                 fprintf (outfile, "\t%s\n", bt->methods [k]->name);
1989                 }
1990         }
1991 }
1992
1993 static void
1994 track_handle (uintptr_t obj, int htype, uint32_t handle)
1995 {
1996         int i;
1997         for (i = 0; i < num_tracked_objects; ++i) {
1998                 if (tracked_objects [i] == obj)
1999                         fprintf (outfile, "Object %p referenced from handle %u\n", (void*)obj, handle);
2000         }
2001 }
2002
2003 static void
2004 track_move (uintptr_t src, uintptr_t dst)
2005 {
2006         int i;
2007         for (i = 0; i < num_tracked_objects; ++i) {
2008                 if (tracked_objects [i] == src)
2009                         fprintf (outfile, "Object %p moved to %p\n", (void*)src, (void*)dst);
2010                 else if (tracked_objects [i] == dst)
2011                         fprintf (outfile, "Object %p moved from %p\n", (void*)dst, (void*)src);
2012         }
2013 }
2014
2015 static void
2016 track_obj_reference (uintptr_t obj, uintptr_t parent, ClassDesc *cd)
2017 {
2018         int i;
2019         for (i = 0; i < num_tracked_objects; ++i) {
2020                 if (tracked_objects [i] == obj)
2021                         fprintf (outfile, "Object %p referenced from %p (%s).\n", (void*)obj, (void*)parent, cd->name);
2022         }
2023 }
2024
2025 static void
2026 found_object (uintptr_t obj)
2027 {
2028         num_tracked_objects ++;
2029         tracked_objects = realloc (tracked_objects, num_tracked_objects * sizeof (tracked_objects [0]));
2030         tracked_objects [num_tracked_objects - 1] = obj;
2031 }
2032
2033 static int num_jit_helpers = 0;
2034 static int jit_helpers_code_size = 0;
2035
2036 static const char*
2037 code_buffer_desc (int type)
2038 {
2039         switch (type) {
2040         case MONO_PROFILER_CODE_BUFFER_METHOD:
2041                 return "method";
2042         case MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE:
2043                 return "method trampoline";
2044         case MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE:
2045                 return "unbox trampoline";
2046         case MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE:
2047                 return "imt trampoline";
2048         case MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE:
2049                 return "generics trampoline";
2050         case MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE:
2051                 return "specific trampoline";
2052         case MONO_PROFILER_CODE_BUFFER_HELPER:
2053                 return "misc helper";
2054         case MONO_PROFILER_CODE_BUFFER_MONITOR:
2055                 return "monitor/lock";
2056         case MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE:
2057                 return "delegate invoke";
2058         case MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING:
2059                 return "exception handling";
2060         default:
2061                 return "unspecified";
2062         }
2063 }
2064
2065 typedef struct _CoverageAssembly CoverageAssembly;
2066 struct _CoverageAssembly {
2067         char *name;
2068         char *guid;
2069         char *filename;
2070         int number_of_methods;
2071         int fully_covered;
2072         int partially_covered;
2073 };
2074
2075 typedef struct _CoverageClass CoverageClass;
2076 struct _CoverageClass {
2077         char *assembly_name;
2078         char *class_name;
2079         int number_of_methods;
2080         int fully_covered;
2081         int partially_covered;
2082 };
2083
2084 typedef struct _CoverageCoverage CoverageCoverage;
2085 struct _CoverageCoverage {
2086         int method_id;
2087         int offset;
2088         int count;
2089         int line;
2090         int column;
2091 };
2092
2093 typedef struct _CoverageMethod CoverageMethod;
2094 struct _CoverageMethod {
2095         char *assembly_name;
2096         char *class_name;
2097         char *method_name;
2098         char *method_signature;
2099         char *filename;
2100         int token;
2101         int n_statements;
2102         int method_id;
2103         GPtrArray *coverage;
2104 };
2105 static GPtrArray *coverage_assemblies = NULL;
2106 static GPtrArray *coverage_methods = NULL;
2107 static GPtrArray *coverage_statements = NULL;
2108 static GHashTable *coverage_methods_hash = NULL;
2109 static GPtrArray *coverage_classes = NULL;
2110 static GHashTable *coverage_assembly_classes = NULL;
2111
2112 static void
2113 gather_coverage_statements (void)
2114 {
2115         for (guint i = 0; i < coverage_statements->len; i++) {
2116                 CoverageCoverage *coverage = coverage_statements->pdata[i];
2117                 CoverageMethod *method = g_hash_table_lookup (coverage_methods_hash, GINT_TO_POINTER (coverage->method_id));
2118                 if (method == NULL) {
2119                         fprintf (outfile, "Cannot find method with ID: %d\n", coverage->method_id);
2120                         continue;
2121                 }
2122
2123                 g_ptr_array_add (method->coverage, coverage);
2124         }
2125 }
2126
2127 static void
2128 coverage_add_assembly (CoverageAssembly *assembly)
2129 {
2130         if (coverage_assemblies == NULL)
2131                 coverage_assemblies = g_ptr_array_new ();
2132
2133         g_ptr_array_add (coverage_assemblies, assembly);
2134 }
2135
2136 static void
2137 coverage_add_method (CoverageMethod *method)
2138 {
2139         if (coverage_methods == NULL) {
2140                 coverage_methods = g_ptr_array_new ();
2141                 coverage_methods_hash = g_hash_table_new (NULL, NULL);
2142         }
2143
2144         g_ptr_array_add (coverage_methods, method);
2145         g_hash_table_insert (coverage_methods_hash, GINT_TO_POINTER (method->method_id), method);
2146 }
2147
2148 static void
2149 coverage_add_class (CoverageClass *klass)
2150 {
2151         GPtrArray *classes = NULL;
2152
2153         if (coverage_classes == NULL) {
2154                 coverage_classes = g_ptr_array_new ();
2155                 coverage_assembly_classes = g_hash_table_new (g_str_hash, g_str_equal);
2156         }
2157
2158         g_ptr_array_add (coverage_classes, klass);
2159         classes = g_hash_table_lookup (coverage_assembly_classes, klass->assembly_name);
2160         if (classes == NULL) {
2161                 classes = g_ptr_array_new ();
2162                 g_hash_table_insert (coverage_assembly_classes, klass->assembly_name, classes);
2163         }
2164         g_ptr_array_add (classes, klass);
2165 }
2166
2167 static void
2168 coverage_add_coverage (CoverageCoverage *coverage)
2169 {
2170         if (coverage_statements == NULL)
2171                 coverage_statements = g_ptr_array_new ();
2172
2173         g_ptr_array_add (coverage_statements, coverage);
2174 }
2175
2176 #define OBJ_ADDR(diff) ((obj_base + diff) << 3)
2177 #define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
2178
2179 static int
2180 decode_buffer (ProfContext *ctx)
2181 {
2182         unsigned char *p;
2183         unsigned char *end;
2184         intptr_t thread_id;
2185         intptr_t ptr_base;
2186         intptr_t obj_base;
2187         intptr_t method_base;
2188         uint64_t time_base;
2189         uint64_t file_offset;
2190         int len, i;
2191         ThreadContext *thread;
2192
2193 #ifdef HAVE_SYS_ZLIB
2194         if (ctx->gzfile)
2195                 file_offset = gztell (ctx->gzfile);
2196         else
2197 #endif
2198                 file_offset = ftell (ctx->file);
2199         if (!load_data (ctx, 48))
2200                 return 0;
2201         p = ctx->buf;
2202         if (read_int32 (p) != BUF_ID) {
2203                 fprintf (outfile, "Incorrect buffer id: 0x%x\n", read_int32 (p));
2204                 for (i = 0; i < 48; ++i) {
2205                         fprintf (outfile, "0x%x%s", p [i], i % 8?" ":"\n");
2206                 }
2207                 return 0;
2208         }
2209         len = read_int32 (p + 4);
2210         time_base = read_int64 (p + 8);
2211         ptr_base = read_int64 (p + 16);
2212         obj_base = read_int64 (p + 24);
2213         thread_id = read_int64 (p + 32);
2214         method_base = read_int64 (p + 40);
2215         if (debug)
2216                 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);
2217         thread = load_thread (ctx, thread_id);
2218         if (!load_data (ctx, len))
2219                 return 0;
2220         if (!startup_time) {
2221                 startup_time = time_base;
2222                 if (use_time_filter) {
2223                         time_from += startup_time;
2224                         time_to += startup_time;
2225                 }
2226                 if (!thread->name)
2227                         thread->name = pstrdup ("Main");
2228         }
2229         for (i = 0; i < thread->stack_id; ++i)
2230                 thread->stack [i]->recurse_count++;
2231         p = ctx->buf;
2232         end = p + len;
2233         while (p < end) {
2234                 switch (*p & 0xf) {
2235                 case TYPE_GC: {
2236                         int subtype = *p & 0xf0;
2237                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2238                         LOG_TIME (time_base, tdiff);
2239                         time_base += tdiff;
2240                         if (subtype == TYPE_GC_RESIZE) {
2241                                 uint64_t new_size = decode_uleb128 (p, &p);
2242                                 if (debug)
2243                                         fprintf (outfile, "gc heap resized to %llu\n", (unsigned long long) new_size);
2244                                 gc_resizes++;
2245                                 if (new_size > max_heap_size)
2246                                         max_heap_size = new_size;
2247                         } else if (subtype == TYPE_GC_EVENT) {
2248                                 uint64_t ev = decode_uleb128 (p, &p);
2249                                 int gen = decode_uleb128 (p, &p);
2250                                 if (debug)
2251                                         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);
2252                                 if (gen > 2) {
2253                                         fprintf (outfile, "incorrect gc gen: %d\n", gen);
2254                                         break;
2255                                 }
2256                                 if (ev == MONO_GC_EVENT_START) {
2257                                         thread->gc_start_times [gen] = time_base;
2258                                         gc_info [gen].count++;
2259                                 } else if (ev == MONO_GC_EVENT_END) {
2260                                         tdiff = time_base - thread->gc_start_times [gen];
2261                                         gc_info [gen].total_time += tdiff;
2262                                         if (tdiff > gc_info [gen].max_time)
2263                                                 gc_info [gen].max_time = tdiff;
2264                                 }
2265                         } else if (subtype == TYPE_GC_MOVE) {
2266                                 int j, num = decode_uleb128 (p, &p);
2267                                 gc_object_moves += num / 2;
2268                                 for (j = 0; j < num; j += 2) {
2269                                         intptr_t obj1diff = decode_sleb128 (p, &p);
2270                                         intptr_t obj2diff = decode_sleb128 (p, &p);
2271                                         if (num_tracked_objects)
2272                                                 track_move (OBJ_ADDR (obj1diff), OBJ_ADDR (obj2diff));
2273                                         if (debug) {
2274                                                 fprintf (outfile, "moved obj %p to %p\n", (void*)OBJ_ADDR (obj1diff), (void*)OBJ_ADDR (obj2diff));
2275                                         }
2276                                 }
2277                         } else if (subtype == TYPE_GC_HANDLE_CREATED) {
2278                                 int htype = decode_uleb128 (p, &p);
2279                                 uint32_t handle = decode_uleb128 (p, &p);
2280                                 intptr_t objdiff = decode_sleb128 (p, &p);
2281                                 if (htype > 3)
2282                                         return 0;
2283                                 handle_info [htype].created++;
2284                                 handle_info [htype].live++;
2285                                 add_trace_thread (thread, &handle_info [htype].traces, 1);
2286                                 /* FIXME: we don't take into account timing here */
2287                                 if (handle_info [htype].live > handle_info [htype].max_live)
2288                                         handle_info [htype].max_live = handle_info [htype].live;
2289                                 if (num_tracked_objects)
2290                                         track_handle (OBJ_ADDR (objdiff), htype, handle);
2291                                 if (debug)
2292                                         fprintf (outfile, "handle (%s) %u created for object %p\n", get_handle_name (htype), handle, (void*)OBJ_ADDR (objdiff));
2293                         } else if (subtype == TYPE_GC_HANDLE_DESTROYED) {
2294                                 int htype = decode_uleb128 (p, &p);
2295                                 uint32_t handle = decode_uleb128 (p, &p);
2296                                 if (htype > 3)
2297                                         return 0;
2298                                 handle_info [htype].destroyed ++;
2299                                 handle_info [htype].live--;
2300                                 if (debug)
2301                                         fprintf (outfile, "handle (%s) %u destroyed\n", get_handle_name (htype), handle);
2302                         }
2303                         break;
2304                 }
2305                 case TYPE_METADATA: {
2306                         int subtype = *p & 0xf0;
2307                         const char *load_str = subtype == TYPE_END_LOAD ? "loaded" : "unloaded";
2308                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2309                         int mtype = *p++;
2310                         intptr_t ptrdiff = decode_sleb128 (p, &p);
2311                         LOG_TIME (time_base, tdiff);
2312                         time_base += tdiff;
2313                         if (mtype == TYPE_CLASS) {
2314                                 intptr_t imptrdiff = decode_sleb128 (p, &p);
2315                                 uint64_t flags = decode_uleb128 (p, &p);
2316                                 if (flags) {
2317                                         fprintf (outfile, "non-zero flags in class\n");
2318                                         return 0;
2319                                 }
2320                                 if (debug)
2321                                         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);
2322                                 if (subtype == TYPE_END_LOAD)
2323                                         add_class (ptr_base + ptrdiff, (char*)p);
2324                                 while (*p) p++;
2325                                 p++;
2326                         } else if (mtype == TYPE_IMAGE) {
2327                                 uint64_t flags = decode_uleb128 (p, &p);
2328                                 if (flags) {
2329                                         fprintf (outfile, "non-zero flags in image\n");
2330                                         return 0;
2331                                 }
2332                                 if (debug)
2333                                         fprintf (outfile, "%s image %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
2334                                 if (subtype == TYPE_END_LOAD)
2335                                         add_image (ptr_base + ptrdiff, (char*)p);
2336                                 while (*p) p++;
2337                                 p++;
2338                         } else if (mtype == TYPE_ASSEMBLY) {
2339                                 uint64_t flags = decode_uleb128 (p, &p);
2340                                 if (flags) {
2341                                         fprintf (outfile, "non-zero flags in assembly\n");
2342                                         return 0;
2343                                 }
2344                                 if (debug)
2345                                         fprintf (outfile, "%s assembly %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
2346                                 if (subtype == TYPE_END_LOAD)
2347                                         add_assembly (ptr_base + ptrdiff, (char*)p);
2348                                 while (*p) p++;
2349                                 p++;
2350                         } else if (mtype == TYPE_DOMAIN) {
2351                                 uint64_t flags = decode_uleb128 (p, &p);
2352                                 if (flags) {
2353                                         fprintf (outfile, "non-zero flags in domain\n");
2354                                         return 0;
2355                                 }
2356                                 DomainContext *nd = get_domain (ctx, ptr_base + ptrdiff);
2357                                 /* no subtype means it's a name event, rather than start/stop */
2358                                 if (subtype == 0)
2359                                         nd->friendly_name = pstrdup ((char *) p);
2360                                 if (debug) {
2361                                         if (subtype == 0)
2362                                                 fprintf (outfile, "domain %p named at %llu: %s\n", (void *) (ptr_base + ptrdiff), (unsigned long long) time_base, p);
2363                                         else
2364                                                 fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
2365                                 }
2366                                 if (subtype == 0) {
2367                                         while (*p) p++;
2368                                         p++;
2369                                 }
2370                         } else if (mtype == TYPE_CONTEXT) {
2371                                 uint64_t flags = decode_uleb128 (p, &p);
2372                                 if (flags) {
2373                                         fprintf (outfile, "non-zero flags in context\n");
2374                                         return 0;
2375                                 }
2376                                 intptr_t domaindiff = decode_sleb128 (p, &p);
2377                                 if (debug)
2378                                         fprintf (outfile, "%s context %p (%p) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), (void *) (ptr_base + domaindiff), (unsigned long long) time_base);
2379                                 if (subtype == TYPE_END_LOAD)
2380                                         get_remctx (ctx, ptr_base + ptrdiff)->domain_id = ptr_base + domaindiff;
2381                         } else if (mtype == TYPE_THREAD) {
2382                                 uint64_t flags = decode_uleb128 (p, &p);
2383                                 if (flags) {
2384                                         fprintf (outfile, "non-zero flags in thread\n");
2385                                         return 0;
2386                                 }
2387                                 ThreadContext *nt = get_thread (ctx, ptr_base + ptrdiff);
2388                                 /* no subtype means it's a name event, rather than start/stop */
2389                                 if (subtype == 0)
2390                                         nt->name = pstrdup ((char*)p);
2391                                 if (debug) {
2392                                         if (subtype == 0)
2393                                                 fprintf (outfile, "thread %p named at %llu: %s\n", (void*)(ptr_base + ptrdiff), (unsigned long long) time_base, p);
2394                                         else
2395                                                 fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
2396                                 }
2397                                 if (subtype == 0) {
2398                                         while (*p) p++;
2399                                         p++;
2400                                 }
2401                         }
2402                         break;
2403                 }
2404                 case TYPE_ALLOC: {
2405                         int has_bt = *p & TYPE_ALLOC_BT;
2406                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2407                         intptr_t ptrdiff = decode_sleb128 (p, &p);
2408                         intptr_t objdiff = decode_sleb128 (p, &p);
2409                         uint64_t len;
2410                         int num_bt = 0;
2411                         MethodDesc* sframes [8];
2412                         MethodDesc** frames = sframes;
2413                         ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
2414                         len = decode_uleb128 (p, &p);
2415                         LOG_TIME (time_base, tdiff);
2416                         time_base += tdiff;
2417                         if (debug)
2418                                 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);
2419                         if (has_bt) {
2420                                 num_bt = 8;
2421                                 frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
2422                                 if (!frames) {
2423                                         fprintf (outfile, "Cannot load backtrace\n");
2424                                         return 0;
2425                                 }
2426                         }
2427                         if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2428                                 BackTrace *bt;
2429                                 cd->allocs++;
2430                                 cd->alloc_size += len;
2431                                 if (has_bt)
2432                                         bt = add_trace_methods (frames, num_bt, &cd->traces, len);
2433                                 else
2434                                         bt = add_trace_thread (thread, &cd->traces, len);
2435                                 if (find_size && len >= find_size) {
2436                                         if (!find_name || strstr (cd->name, find_name))
2437                                                 found_object (OBJ_ADDR (objdiff));
2438                                 } else if (!find_size && find_name && strstr (cd->name, find_name)) {
2439                                         found_object (OBJ_ADDR (objdiff));
2440                                 }
2441                                 if (num_tracked_objects)
2442                                         tracked_creation (OBJ_ADDR (objdiff), cd, len, bt, time_base);
2443                         }
2444                         if (frames != sframes)
2445                                 free (frames);
2446                         break;
2447                 }
2448                 case TYPE_METHOD: {
2449                         int subtype = *p & 0xf0;
2450                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2451                         int64_t ptrdiff = decode_sleb128 (p, &p);
2452                         LOG_TIME (time_base, tdiff);
2453                         time_base += tdiff;
2454                         method_base += ptrdiff;
2455                         if (subtype == TYPE_JIT) {
2456                                 intptr_t codediff = decode_sleb128 (p, &p);
2457                                 int codelen = decode_uleb128 (p, &p);
2458                                 MethodDesc *jitted_method;
2459                                 if (debug)
2460                                         fprintf (outfile, "jitted method %p (%s), size: %d, code: %p\n", (void*)(method_base), p, codelen, (void*)(ptr_base + codediff));
2461                                 jitted_method = add_method (method_base, (char*)p, ptr_base + codediff, codelen);
2462                                 if (!(time_base >= time_from && time_base < time_to))
2463                                         jitted_method->ignore_jit = 1;
2464                                 while (*p) p++;
2465                                 p++;
2466                         } else {
2467                                 MethodDesc *method;
2468                                 if ((thread_filter && thread_filter != thread->thread_id))
2469                                         break;
2470                                 if (!(time_base >= time_from && time_base < time_to))
2471                                         break;
2472                                 method = lookup_method (method_base);
2473                                 if (subtype == TYPE_ENTER) {
2474                                         add_trace_thread (thread, &method->traces, 1);
2475                                         push_method (thread, method, time_base);
2476                                 } else {
2477                                         pop_method (thread, method, time_base);
2478                                 }
2479                                 if (debug)
2480                                         fprintf (outfile, "%s method %s\n", subtype == TYPE_ENTER? "enter": subtype == TYPE_EXC_LEAVE? "exleave": "leave", method->name);
2481                         }
2482                         break;
2483                 }
2484                 case TYPE_HEAP: {
2485                         int subtype = *p & 0xf0;
2486                         if (subtype == TYPE_HEAP_OBJECT) {
2487                                 HeapObjectDesc *ho = NULL;
2488                                 int i;
2489                                 intptr_t objdiff = decode_sleb128 (p + 1, &p);
2490                                 intptr_t ptrdiff = decode_sleb128 (p, &p);
2491                                 uint64_t size = decode_uleb128 (p, &p);
2492                                 uintptr_t num = decode_uleb128 (p, &p);
2493                                 uintptr_t ref_offset = 0;
2494                                 uintptr_t last_obj_offset = 0;
2495                                 ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
2496                                 if (size) {
2497                                         HeapClassDesc *hcd = add_heap_shot_class (thread->current_heap_shot, cd, size);
2498                                         if (collect_traces) {
2499                                                 ho = alloc_heap_obj (OBJ_ADDR (objdiff), hcd, num);
2500                                                 add_heap_shot_obj (thread->current_heap_shot, ho);
2501                                                 ref_offset = 0;
2502                                         }
2503                                 } else {
2504                                         if (collect_traces)
2505                                                 ho = heap_shot_obj_add_refs (thread->current_heap_shot, OBJ_ADDR (objdiff), num, &ref_offset);
2506                                 }
2507                                 for (i = 0; i < num; ++i) {
2508                                         /* FIXME: use object distance to measure how good
2509                                          * the GC is at keeping related objects close
2510                                          */
2511                                         uintptr_t offset = ctx->data_version > 1? last_obj_offset + decode_uleb128 (p, &p): -1;
2512                                         intptr_t obj1diff = decode_sleb128 (p, &p);
2513                                         last_obj_offset = offset;
2514                                         if (collect_traces)
2515                                                 ho->refs [ref_offset + i] = OBJ_ADDR (obj1diff);
2516                                         if (num_tracked_objects)
2517                                                 track_obj_reference (OBJ_ADDR (obj1diff), OBJ_ADDR (objdiff), cd);
2518                                 }
2519                                 if (debug && size)
2520                                         fprintf (outfile, "traced object %p, size %llu (%s), refs: %zd\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) size, cd->name, num);
2521                         } else if (subtype == TYPE_HEAP_ROOT) {
2522                                 uintptr_t num = decode_uleb128 (p + 1, &p);
2523                                 uintptr_t gc_num G_GNUC_UNUSED = decode_uleb128 (p, &p);
2524                                 int i;
2525                                 for (i = 0; i < num; ++i) {
2526                                         intptr_t objdiff = decode_sleb128 (p, &p);
2527                                         int root_type = decode_uleb128 (p, &p);
2528                                         /* we just discard the extra info for now */
2529                                         uintptr_t extra_info = decode_uleb128 (p, &p);
2530                                         if (debug)
2531                                                 fprintf (outfile, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff), get_root_name (root_type));
2532                                         if (collect_traces)
2533                                                 thread_add_root (thread, OBJ_ADDR (objdiff), root_type, extra_info);
2534                                 }
2535                         } else if (subtype == TYPE_HEAP_END) {
2536                                 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2537                                 LOG_TIME (time_base, tdiff);
2538                                 time_base += tdiff;
2539                                 if (debug)
2540                                         fprintf (outfile, "heap shot end\n");
2541                                 if (collect_traces) {
2542                                         HeapShot *hs = thread->current_heap_shot;
2543                                         if (hs && thread->num_roots) {
2544                                                 /* transfer the root ownershipt to the heapshot */
2545                                                 hs->num_roots = thread->num_roots;
2546                                                 hs->roots = thread->roots;
2547                                                 hs->roots_extra = thread->roots_extra;
2548                                                 hs->roots_types = thread->roots_types;
2549                                         } else {
2550                                                 free (thread->roots);
2551                                                 free (thread->roots_extra);
2552                                                 free (thread->roots_types);
2553                                         }
2554                                         thread->num_roots = 0;
2555                                         thread->size_roots = 0;
2556                                         thread->roots = NULL;
2557                                         thread->roots_extra = NULL;
2558                                         thread->roots_types = NULL;
2559                                         heap_shot_resolve_reverse_refs (hs);
2560                                         heap_shot_mark_objects (hs);
2561                                         heap_shot_free_objects (hs);
2562                                 }
2563                                 thread->current_heap_shot = NULL;
2564                         } else if (subtype == TYPE_HEAP_START) {
2565                                 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2566                                 LOG_TIME (time_base, tdiff);
2567                                 time_base += tdiff;
2568                                 if (debug)
2569                                         fprintf (outfile, "heap shot start\n");
2570                                 thread->current_heap_shot = new_heap_shot (time_base);
2571                         }
2572                         break;
2573                 }
2574                 case TYPE_MONITOR: {
2575                         int event = (*p >> 4) & 0x3;
2576                         int has_bt = *p & TYPE_MONITOR_BT;
2577                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2578                         intptr_t objdiff = decode_sleb128 (p, &p);
2579                         MethodDesc* sframes [8];
2580                         MethodDesc** frames = sframes;
2581                         int record;
2582                         int num_bt = 0;
2583                         LOG_TIME (time_base, tdiff);
2584                         time_base += tdiff;
2585                         record = (!thread_filter || thread_filter == thread->thread_id);
2586                         if (!(time_base >= time_from && time_base < time_to))
2587                                 record = 0;
2588                         if (event == MONO_PROFILER_MONITOR_CONTENTION) {
2589                                 MonitorDesc *mdesc = lookup_monitor (OBJ_ADDR (objdiff));
2590                                 if (record) {
2591                                         monitor_contention++;
2592                                         mdesc->contentions++;
2593                                         thread->monitor = mdesc;
2594                                         thread->contention_start = time_base;
2595                                 }
2596                                 if (has_bt) {
2597                                         num_bt = 8;
2598                                         frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
2599                                         if (!frames) {
2600                                                 fprintf (outfile, "Cannot load backtrace\n");
2601                                                 return 0;
2602                                         }
2603                                         if (record)
2604                                                 add_trace_methods (frames, num_bt, &mdesc->traces, 1);
2605                                 } else {
2606                                         if (record)
2607                                                 add_trace_thread (thread, &mdesc->traces, 1);
2608                                 }
2609                         } else if (event == MONO_PROFILER_MONITOR_FAIL) {
2610                                 if (record) {
2611                                         monitor_failed++;
2612                                         if (thread->monitor && thread->contention_start) {
2613                                                 uint64_t wait_time = time_base - thread->contention_start;
2614                                                 if (wait_time > thread->monitor->max_wait_time)
2615                                                         thread->monitor->max_wait_time = wait_time;
2616                                                 thread->monitor->wait_time += wait_time;
2617                                                 thread->monitor = NULL;
2618                                                 thread->contention_start = 0;
2619                                         }
2620                                 }
2621                         } else if (event == MONO_PROFILER_MONITOR_DONE) {
2622                                 if (record) {
2623                                         monitor_acquired++;
2624                                         if (thread->monitor && thread->contention_start) {
2625                                                 uint64_t wait_time = time_base - thread->contention_start;
2626                                                 if (wait_time > thread->monitor->max_wait_time)
2627                                                         thread->monitor->max_wait_time = wait_time;
2628                                                 thread->monitor->wait_time += wait_time;
2629                                                 thread->monitor = NULL;
2630                                                 thread->contention_start = 0;
2631                                         }
2632                                 }
2633                         }
2634                         if (debug)
2635                                 fprintf (outfile, "monitor %s for object %p\n", monitor_ev_name (event), (void*)OBJ_ADDR (objdiff));
2636                         if (frames != sframes)
2637                                 free (frames);
2638                         break;
2639                 }
2640                 case TYPE_EXCEPTION: {
2641                         int subtype = *p & 0x70;
2642                         int has_bt = *p & TYPE_EXCEPTION_BT;
2643                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2644                         MethodDesc* sframes [8];
2645                         MethodDesc** frames = sframes;
2646                         int record;
2647                         LOG_TIME (time_base, tdiff);
2648                         time_base += tdiff;
2649                         record = (!thread_filter || thread_filter == thread->thread_id);
2650                         if (!(time_base >= time_from && time_base < time_to))
2651                                 record = 0;
2652                         if (subtype == TYPE_CLAUSE) {
2653                                 int clause_type = decode_uleb128 (p, &p);
2654                                 int clause_num = decode_uleb128 (p, &p);
2655                                 int64_t ptrdiff = decode_sleb128 (p, &p);
2656                                 method_base += ptrdiff;
2657                                 if (record)
2658                                         clause_summary [clause_type]++;
2659                                 if (debug)
2660                                         fprintf (outfile, "clause %s (%d) in method %s\n", clause_name (clause_type), clause_num, lookup_method (method_base)->name);
2661                         } else {
2662                                 intptr_t objdiff = decode_sleb128 (p, &p);
2663                                 if (record)
2664                                         throw_count++;
2665                                 if (has_bt) {
2666                                         has_bt = 8;
2667                                         frames = decode_bt (sframes, &has_bt, p, &p, ptr_base);
2668                                         if (!frames) {
2669                                                 fprintf (outfile, "Cannot load backtrace\n");
2670                                                 return 0;
2671                                         }
2672                                         if (record)
2673                                                 add_trace_methods (frames, has_bt, &exc_traces, 1);
2674                                 } else {
2675                                         if (record)
2676                                                 add_trace_thread (thread, &exc_traces, 1);
2677                                 }
2678                                 if (frames != sframes)
2679                                         free (frames);
2680                                 if (debug)
2681                                         fprintf (outfile, "throw %p\n", (void*)OBJ_ADDR (objdiff));
2682                         }
2683                         break;
2684                 }
2685                 case TYPE_RUNTIME: {
2686                         int subtype = *p & 0xf0;
2687                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2688                         LOG_TIME (time_base, tdiff);
2689                         time_base += tdiff;
2690                         if (subtype == TYPE_JITHELPER) {
2691                                 int type = decode_uleb128 (p, &p);
2692                                 intptr_t codediff = decode_sleb128 (p, &p);
2693                                 int codelen = decode_uleb128 (p, &p);
2694                                 const char *name;
2695                                 if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
2696                                         name = (void*)p;
2697                                         while (*p) p++;
2698                                                 p++;
2699                                 } else {
2700                                         name = code_buffer_desc (type);
2701                                 }
2702                                 num_jit_helpers++;
2703                                 jit_helpers_code_size += codelen;
2704                                 if (debug)
2705                                         fprintf (outfile, "jit helper %s, size: %d, code: %p\n", name, codelen, (void*)(ptr_base + codediff));
2706                         }
2707                         break;
2708                 }
2709                 case TYPE_SAMPLE: {
2710                         int subtype = *p & 0xf0;
2711                         if (subtype == TYPE_SAMPLE_HIT) {
2712                                 int i;
2713                                 int sample_type = decode_uleb128 (p + 1, &p);
2714                                 uint64_t tstamp = decode_uleb128 (p, &p);
2715                                 void *tid = (void *) thread_id;
2716                                 if (ctx->data_version > 10)
2717                                         tid = (void *) (ptr_base + decode_sleb128 (p, &p));
2718                                 int count = decode_uleb128 (p, &p);
2719                                 for (i = 0; i < count; ++i) {
2720                                         uintptr_t ip = ptr_base + decode_sleb128 (p, &p);
2721                                         if ((tstamp >= time_from && tstamp < time_to))
2722                                                 add_stat_sample (sample_type, ip);
2723                                         if (debug)
2724                                                 fprintf (outfile, "sample hit, type: %d at %p for thread %p\n", sample_type, (void*)ip, tid);
2725                                 }
2726                                 if (ctx->data_version > 5) {
2727                                         count = decode_uleb128 (p, &p);
2728                                         for (i = 0; i < count; ++i) {
2729                                                 MethodDesc *method;
2730                                                 int64_t ptrdiff = decode_sleb128 (p, &p);
2731                                                 int il_offset = decode_sleb128 (p, &p);
2732                                                 int native_offset = decode_sleb128 (p, &p);
2733                                                 method_base += ptrdiff;
2734                                                 method = lookup_method (method_base);
2735                                                 if (debug)
2736                                                         fprintf (outfile, "sample hit bt %d: %s at IL offset %d (native: %d)\n", i, method->name, il_offset, native_offset);
2737                                         }
2738                                 }
2739                         } else if (subtype == TYPE_SAMPLE_USYM) {
2740                                 /* un unmanaged symbol description */
2741                                 uintptr_t addr = ptr_base + decode_sleb128 (p + 1, &p);
2742                                 uintptr_t size = decode_uleb128 (p, &p);
2743                                 char *name;
2744                                 name = pstrdup ((char*)p);
2745                                 add_unmanaged_symbol (addr, name, size);
2746                                 if (debug)
2747                                         fprintf (outfile, "unmanaged symbol %s at %p\n", name, (void*)addr);
2748                                 while (*p) p++;
2749                                 p++;
2750                         } else if (subtype == TYPE_SAMPLE_UBIN) {
2751                                 /* un unmanaged binary loaded in memory */
2752                                 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2753                                 uintptr_t addr = decode_sleb128 (p, &p);
2754                                 uint64_t offset G_GNUC_UNUSED = decode_uleb128 (p, &p);
2755                                 uintptr_t size = decode_uleb128 (p, &p);
2756                                 char *name;
2757                                 LOG_TIME (time_base, tdiff);
2758                                 time_base += tdiff;
2759                                 name = pstrdup ((char*)p);
2760                                 add_unmanaged_binary (addr, name, size);
2761                                 if (debug)
2762                                         fprintf (outfile, "unmanaged binary %s at %p\n", name, (void*)addr);
2763                                 while (*p) p++;
2764                                 p++;
2765                         } else if (subtype == TYPE_SAMPLE_COUNTERS_DESC) {
2766                                 uint64_t i, len = decode_uleb128 (p + 1, &p);
2767                                 for (i = 0; i < len; i++) {
2768                                         uint64_t type, unit, variance, index;
2769                                         uint64_t section = decode_uleb128 (p, &p);
2770                                         char *section_str, *name;
2771                                         if (section != MONO_COUNTER_PERFCOUNTERS) {
2772                                                 section_str = (char*) section_name (section);
2773                                         } else {
2774                                                 section_str = pstrdup ((char*)p);
2775                                                 while (*p++);
2776                                         }
2777                                         name = pstrdup ((char*)p);
2778                                         while (*p++);
2779                                         type = decode_uleb128 (p, &p);
2780                                         unit = decode_uleb128 (p, &p);
2781                                         variance = decode_uleb128 (p, &p);
2782                                         index = decode_uleb128 (p, &p);
2783                                         add_counter (section_str, name, (int)type, (int)unit, (int)variance, (int)index);
2784                                 }
2785                         } else if (subtype == TYPE_SAMPLE_COUNTERS) {
2786                                 int i;
2787                                 CounterValue *value, *previous = NULL;
2788                                 CounterList *list;
2789                                 uint64_t timestamp = decode_uleb128 (p + 1, &p);
2790                                 uint64_t time_between = timestamp / 1000 * 1000 * 1000 * 1000 + startup_time;
2791                                 while (1) {
2792                                         uint64_t type, index = decode_uleb128 (p, &p);
2793                                         if (index == 0)
2794                                                 break;
2795
2796                                         for (list = counters; list; list = list->next) {
2797                                                 if (list->counter->index == (int)index) {
2798                                                         previous = list->counter->values_last;
2799                                                         break;
2800                                                 }
2801                                         }
2802
2803                                         type = decode_uleb128 (p, &p);
2804
2805                                         value = calloc (1, sizeof (CounterValue));
2806                                         value->timestamp = timestamp;
2807
2808                                         switch (type) {
2809                                         case MONO_COUNTER_INT:
2810 #if SIZEOF_VOID_P == 4
2811                                         case MONO_COUNTER_WORD:
2812 #endif
2813                                                 value->buffer = malloc (sizeof (int32_t));
2814                                                 *(int32_t*)value->buffer = (int32_t)decode_sleb128 (p, &p) + (previous ? (*(int32_t*)previous->buffer) : 0);
2815                                                 break;
2816                                         case MONO_COUNTER_UINT:
2817                                                 value->buffer = malloc (sizeof (uint32_t));
2818                                                 *(uint32_t*)value->buffer = (uint32_t)decode_uleb128 (p, &p) + (previous ? (*(uint32_t*)previous->buffer) : 0);
2819                                                 break;
2820                                         case MONO_COUNTER_LONG:
2821 #if SIZEOF_VOID_P == 8
2822                                         case MONO_COUNTER_WORD:
2823 #endif
2824                                         case MONO_COUNTER_TIME_INTERVAL:
2825                                                 value->buffer = malloc (sizeof (int64_t));
2826                                                 *(int64_t*)value->buffer = (int64_t)decode_sleb128 (p, &p) + (previous ? (*(int64_t*)previous->buffer) : 0);
2827                                                 break;
2828                                         case MONO_COUNTER_ULONG:
2829                                                 value->buffer = malloc (sizeof (uint64_t));
2830                                                 *(uint64_t*)value->buffer = (uint64_t)decode_uleb128 (p, &p) + (previous ? (*(uint64_t*)previous->buffer) : 0);
2831                                                 break;
2832                                         case MONO_COUNTER_DOUBLE:
2833                                                 value->buffer = malloc (sizeof (double));
2834 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
2835                                                 for (i = 0; i < sizeof (double); i++)
2836 #else
2837                                                 for (i = sizeof (double) - 1; i >= 0; i--)
2838 #endif
2839                                                         value->buffer[i] = *p++;
2840                                                 break;
2841                                         case MONO_COUNTER_STRING:
2842                                                 if (*p++ == 0) {
2843                                                         value->buffer = NULL;
2844                                                 } else {
2845                                                         value->buffer = (unsigned char*) pstrdup ((char*)p);
2846                                                         while (*p++);
2847                                                 }
2848                                                 break;
2849                                         }
2850                                         if (time_between >= time_from && time_between <= time_to)
2851                                                 add_counter_value (index, value);
2852                                 }
2853                         } else {
2854                                 return 0;
2855                         }
2856                         break;
2857                 }
2858                 case TYPE_COVERAGE:{
2859                         int subtype = *p & 0xf0;
2860                         switch (subtype) {
2861                         case TYPE_COVERAGE_METHOD: {
2862                                 CoverageMethod *method = g_new0 (CoverageMethod, 1);
2863                                 const char *assembly, *klass, *name, *sig, *filename;
2864                                 int token, n_offsets, method_id;
2865
2866                                 p++;
2867                                 assembly = (void *)p; while (*p) p++; p++;
2868                                 klass = (void *)p; while (*p) p++; p++;
2869                                 name = (void *)p; while (*p) p++; p++;
2870                                 sig = (void *)p; while (*p) p++; p++;
2871                                 filename = (void *)p; while (*p) p++; p++;
2872
2873                                 token = decode_uleb128 (p, &p);
2874                                 method_id = decode_uleb128 (p, &p);
2875                                 n_offsets = decode_uleb128 (p, &p);
2876
2877                                 method->assembly_name = g_strdup (assembly);
2878                                 method->class_name = g_strdup (klass);
2879                                 method->method_name = g_strdup (name);
2880                                 method->method_signature = g_strdup (sig);
2881                                 method->filename = g_strdup (filename);
2882                                 method->token = token;
2883                                 method->n_statements = n_offsets;
2884                                 method->coverage = g_ptr_array_new ();
2885                                 method->method_id = method_id;
2886
2887                                 coverage_add_method (method);
2888
2889                                 break;
2890                         }
2891                         case TYPE_COVERAGE_STATEMENT: {
2892                                 CoverageCoverage *coverage = g_new0 (CoverageCoverage, 1);
2893                                 int offset, count, line, column, method_id;
2894
2895                                 p++;
2896                                 method_id = decode_uleb128 (p, &p);
2897                                 offset = decode_uleb128 (p, &p);
2898                                 count = decode_uleb128 (p, &p);
2899                                 line = decode_uleb128 (p, &p);
2900                                 column = decode_uleb128 (p, &p);
2901
2902                                 coverage->method_id = method_id;
2903                                 coverage->offset = offset;
2904                                 coverage->count = count;
2905                                 coverage->line = line;
2906                                 coverage->column = column;
2907
2908                                 coverage_add_coverage (coverage);
2909                                 break;
2910                         }
2911                         case TYPE_COVERAGE_ASSEMBLY: {
2912                                 CoverageAssembly *assembly = g_new0 (CoverageAssembly, 1);
2913                                 char *name, *guid, *filename;
2914                                 int number_of_methods, fully_covered, partially_covered;
2915                                 p++;
2916
2917                                 name = (void *)p; while (*p) p++; p++;
2918                                 guid = (void *)p; while (*p) p++; p++;
2919                                 filename = (void *)p; while (*p) p++; p++;
2920                                 number_of_methods = decode_uleb128 (p, &p);
2921                                 fully_covered = decode_uleb128 (p, &p);
2922                                 partially_covered = decode_uleb128 (p, &p);
2923
2924                                 assembly->name = g_strdup (name);
2925                                 assembly->guid = g_strdup (guid);
2926                                 assembly->filename = g_strdup (filename);
2927                                 assembly->number_of_methods = number_of_methods;
2928                                 assembly->fully_covered = fully_covered;
2929                                 assembly->partially_covered = partially_covered;
2930
2931                                 coverage_add_assembly (assembly);
2932                                 break;
2933                         }
2934                         case TYPE_COVERAGE_CLASS: {
2935                                 CoverageClass *klass = g_new0 (CoverageClass, 1);
2936                                 char *assembly_name, *class_name;
2937                                 int number_of_methods, fully_covered, partially_covered;
2938                                 p++;
2939
2940                                 assembly_name = (void *)p; while (*p) p++; p++;
2941                                 class_name = (void *)p; while (*p) p++; p++;
2942                                 number_of_methods = decode_uleb128 (p, &p);
2943                                 fully_covered = decode_uleb128 (p, &p);
2944                                 partially_covered = decode_uleb128 (p, &p);
2945
2946                                 klass->assembly_name = g_strdup (assembly_name);
2947                                 klass->class_name = g_strdup (class_name);
2948                                 klass->number_of_methods = number_of_methods;
2949                                 klass->fully_covered = fully_covered;
2950                                 klass->partially_covered = partially_covered;
2951
2952                                 coverage_add_class (klass);
2953                                 break;
2954                         }
2955
2956                         default:
2957                                 break;
2958                         }
2959                         break;
2960                 }
2961                 default:
2962                         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);
2963                         exit (1);
2964                 }
2965         }
2966         thread->last_time = time_base;
2967         for (i = 0; i < thread->stack_id; ++i)
2968                 thread->stack [i]->recurse_count = 0;
2969         return 1;
2970 }
2971
2972 static ProfContext*
2973 load_file (char *name)
2974 {
2975         unsigned char *p;
2976         ProfContext *ctx = calloc (sizeof (ProfContext), 1);
2977         if (strcmp (name, "-") == 0)
2978                 ctx->file = stdin;
2979         else
2980                 ctx->file = fopen (name, "rb");
2981         if (!ctx->file) {
2982                 printf ("Cannot open file: %s\n", name);
2983                 exit (1);
2984         }
2985 #if defined (HAVE_SYS_ZLIB)
2986         if (ctx->file != stdin)
2987                 ctx->gzfile = gzdopen (fileno (ctx->file), "rb");
2988 #endif
2989         if (!load_data (ctx, 32))
2990                 return NULL;
2991         p = ctx->buf;
2992         if (read_int32 (p) != LOG_HEADER_ID || p [6] > LOG_DATA_VERSION)
2993                 return NULL;
2994         ctx->version_major = p [4];
2995         ctx->version_minor = p [5];
2996         ctx->data_version = p [6];
2997         /* reading 64 bit files on 32 bit systems not supported yet */
2998         if (p [7] > sizeof (void*))
2999                 return NULL;
3000         if (read_int32 (p + 20)) /* flags must be 0 */
3001                 return NULL;
3002         ctx->startup_time = read_int64 (p + 8);
3003         ctx->timer_overhead = read_int32 (p + 16);
3004         ctx->pid = read_int32 (p + 24);
3005         ctx->port = read_int16 (p + 28);
3006         return ctx;
3007 }
3008
3009 enum {
3010         ALLOC_SORT_BYTES,
3011         ALLOC_SORT_COUNT
3012 };
3013 static int alloc_sort_mode = ALLOC_SORT_BYTES;
3014
3015 static int
3016 compare_class (const void *a, const void *b)
3017 {
3018         ClassDesc *const*A = a;
3019         ClassDesc *const*B = b;
3020         uint64_t vala, valb;
3021         if (alloc_sort_mode == ALLOC_SORT_BYTES) {
3022                 vala = (*A)->alloc_size;
3023                 valb = (*B)->alloc_size;
3024         } else {
3025                 vala = (*A)->allocs;
3026                 valb = (*B)->allocs;
3027         }
3028         if (valb == vala)
3029                 return 0;
3030         if (valb < vala)
3031                 return -1;
3032         return 1;
3033 }
3034
3035 static void
3036 dump_header (ProfContext *ctx)
3037 {
3038         time_t st = ctx->startup_time / 1000;
3039         char *t = ctime (&st);
3040         fprintf (outfile, "\nMono log profiler data\n");
3041         fprintf (outfile, "\tProfiler version: %d.%d\n", ctx->version_major, ctx->version_minor);
3042         fprintf (outfile, "\tData version: %d\n", ctx->data_version);
3043         fprintf (outfile, "\tMean timer overhead: %d nanoseconds\n", ctx->timer_overhead);
3044         fprintf (outfile, "\tProgram startup: %s", t);
3045         if (ctx->pid)
3046                 fprintf (outfile, "\tProgram ID: %d\n", ctx->pid);
3047         if (ctx->port)
3048                 fprintf (outfile, "\tServer listening on: %d\n", ctx->port);
3049 }
3050
3051 static void
3052 dump_traces (TraceDesc *traces, const char *desc)
3053 {
3054         int j;
3055         if (!show_traces)
3056                 return;
3057         if (!traces->count)
3058                 return;
3059         sort_context_array (traces);
3060         for (j = 0; j < traces->count; ++j) {
3061                 int k;
3062                 BackTrace *bt;
3063                 bt = traces->traces [j].bt;
3064                 if (!bt->count)
3065                         continue;
3066                 fprintf (outfile, "\t%llu %s from:\n", (unsigned long long) traces->traces [j].count, desc);
3067                 for (k = 0; k < bt->count; ++k)
3068                         fprintf (outfile, "\t\t%s\n", bt->methods [k]->name);
3069         }
3070 }
3071
3072 static void
3073 dump_threads (ProfContext *ctx)
3074 {
3075         ThreadContext *thread;
3076         fprintf (outfile, "\nThread summary\n");
3077         for (thread = ctx->threads; thread; thread = thread->next) {
3078                 fprintf (outfile, "\tThread: %p, name: \"%s\"\n", (void*)thread->thread_id, thread->name? thread->name: "");
3079         }
3080 }
3081
3082 static void
3083 dump_domains (ProfContext *ctx)
3084 {
3085         fprintf (outfile, "\nDomain summary\n");
3086
3087         for (DomainContext *domain = ctx->domains; domain; domain = domain->next)
3088                 fprintf (outfile, "\tDomain: %p, friendly name: \"%s\"\n", (void *) domain->domain_id, domain->friendly_name);
3089 }
3090
3091 static void
3092 dump_remctxs (ProfContext *ctx)
3093 {
3094         fprintf (outfile, "\nContext summary\n");
3095
3096         for (RemCtxContext *remctx = ctx->remctxs; remctx; remctx = remctx->next)
3097                 fprintf (outfile, "\tContext: %p, domain: %p\n", (void *) remctx->remctx_id, (void *) remctx->domain_id);
3098 }
3099
3100 static void
3101 dump_exceptions (void)
3102 {
3103         int i;
3104         fprintf (outfile, "\nException summary\n");
3105         fprintf (outfile, "\tThrows: %llu\n", (unsigned long long) throw_count);
3106         dump_traces (&exc_traces, "throws");
3107         for (i = 0; i <= MONO_EXCEPTION_CLAUSE_FAULT; ++i) {
3108                 if (!clause_summary [i])
3109                         continue;
3110                 fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), (unsigned long long) clause_summary [i]);
3111         }
3112 }
3113
3114 static int
3115 compare_monitor (const void *a, const void *b)
3116 {
3117         MonitorDesc *const*A = a;
3118         MonitorDesc *const*B = b;
3119         if ((*B)->wait_time == (*A)->wait_time)
3120                 return 0;
3121         if ((*B)->wait_time < (*A)->wait_time)
3122                 return -1;
3123         return 1;
3124 }
3125
3126 static void
3127 dump_monitors (void)
3128 {
3129         MonitorDesc **monitors;
3130         int i, j;
3131         if (!num_monitors)
3132                 return;
3133         monitors = malloc (sizeof (void*) * num_monitors);
3134         for (i = 0, j = 0; i < SMALL_HASH_SIZE; ++i) {
3135                 MonitorDesc *mdesc = monitor_hash [i];
3136                 while (mdesc) {
3137                         monitors [j++] = mdesc;
3138                         mdesc = mdesc->next;
3139                 }
3140         }
3141         qsort (monitors, num_monitors, sizeof (void*), compare_monitor);
3142         fprintf (outfile, "\nMonitor lock summary\n");
3143         for (i = 0; i < num_monitors; ++i) {
3144                 MonitorDesc *mdesc = monitors [i];
3145                 fprintf (outfile, "\tLock object %p: %d contentions\n", (void*)mdesc->objid, (int)mdesc->contentions);
3146                 fprintf (outfile, "\t\t%.6f secs total wait time, %.6f max, %.6f average\n",
3147                         mdesc->wait_time/1000000000.0, mdesc->max_wait_time/1000000000.0, mdesc->wait_time/1000000000.0/mdesc->contentions);
3148                 dump_traces (&mdesc->traces, "contentions");
3149         }
3150         fprintf (outfile, "\tLock contentions: %llu\n", (unsigned long long) monitor_contention);
3151         fprintf (outfile, "\tLock acquired: %llu\n", (unsigned long long) monitor_acquired);
3152         fprintf (outfile, "\tLock failures: %llu\n", (unsigned long long) monitor_failed);
3153 }
3154
3155 static void
3156 dump_gcs (void)
3157 {
3158         int i;
3159         fprintf (outfile, "\nGC summary\n");
3160         fprintf (outfile, "\tGC resizes: %d\n", gc_resizes);
3161         fprintf (outfile, "\tMax heap size: %llu\n", (unsigned long long) max_heap_size);
3162         fprintf (outfile, "\tObject moves: %llu\n", (unsigned long long) gc_object_moves);
3163         for (i = 0; i < 3; ++i) {
3164                 if (!gc_info [i].count)
3165                         continue;
3166                 fprintf (outfile, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
3167                         i, gc_info [i].count,
3168                         (unsigned long long) (gc_info [i].max_time / 1000),
3169                         (unsigned long long) (gc_info [i].total_time / 1000),
3170                         (unsigned long long) (gc_info [i].total_time / gc_info [i].count / 1000));
3171         }
3172         for (i = 0; i < 3; ++i) {
3173                 if (!handle_info [i].max_live)
3174                         continue;
3175                 fprintf (outfile, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
3176                         get_handle_name (i),
3177                         (unsigned long long) (handle_info [i].created),
3178                         (unsigned long long) (handle_info [i].destroyed),
3179                         (unsigned long long) (handle_info [i].max_live));
3180                 dump_traces (&handle_info [i].traces, "created");
3181         }
3182 }
3183
3184 static void
3185 dump_jit (void)
3186 {
3187         int i;
3188         int code_size = 0;
3189         int compiled_methods = 0;
3190         MethodDesc* m;
3191         fprintf (outfile, "\nJIT summary\n");
3192         for (i = 0; i < HASH_SIZE; ++i) {
3193                 m = method_hash [i];
3194                 for (m = method_hash [i]; m; m = m->next) {
3195                         if (!m->code || m->ignore_jit)
3196                                 continue;
3197                         compiled_methods++;
3198                         code_size += m->len;
3199                 }
3200         }
3201         fprintf (outfile, "\tCompiled methods: %d\n", compiled_methods);
3202         fprintf (outfile, "\tGenerated code size: %d\n", code_size);
3203         fprintf (outfile, "\tJIT helpers: %d\n", num_jit_helpers);
3204         fprintf (outfile, "\tJIT helpers code size: %d\n", jit_helpers_code_size);
3205 }
3206
3207 static void
3208 dump_allocations (void)
3209 {
3210         int i, c;
3211         intptr_t allocs = 0;
3212         uint64_t size = 0;
3213         int header_done = 0;
3214         ClassDesc **classes = malloc (num_classes * sizeof (void*));
3215         ClassDesc *cd;
3216         c = 0;
3217         for (i = 0; i < HASH_SIZE; ++i) {
3218                 cd = class_hash [i];
3219                 while (cd) {
3220                         classes [c++] = cd;
3221                         cd = cd->next;
3222                 }
3223         }
3224         qsort (classes, num_classes, sizeof (void*), compare_class);
3225         for (i = 0; i < num_classes; ++i) {
3226                 cd = classes [i];
3227                 if (!cd->allocs)
3228                         continue;
3229                 allocs += cd->allocs;
3230                 size += cd->alloc_size;
3231                 if (!header_done++) {
3232                         fprintf (outfile, "\nAllocation summary\n");
3233                         fprintf (outfile, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
3234                 }
3235                 fprintf (outfile, "%10llu %10zd %8llu %s\n",
3236                         (unsigned long long) (cd->alloc_size),
3237                         cd->allocs,
3238                         (unsigned long long) (cd->alloc_size / cd->allocs),
3239                         cd->name);
3240                 dump_traces (&cd->traces, "bytes");
3241         }
3242         if (allocs)
3243                 fprintf (outfile, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size, allocs);
3244 }
3245
3246 enum {
3247         METHOD_SORT_TOTAL,
3248         METHOD_SORT_SELF,
3249         METHOD_SORT_CALLS
3250 };
3251
3252 static int method_sort_mode = METHOD_SORT_TOTAL;
3253
3254 static int
3255 compare_method (const void *a, const void *b)
3256 {
3257         MethodDesc *const*A = a;
3258         MethodDesc *const*B = b;
3259         uint64_t vala, valb;
3260         if (method_sort_mode == METHOD_SORT_SELF) {
3261                 vala = (*A)->self_time;
3262                 valb = (*B)->self_time;
3263         } else if (method_sort_mode == METHOD_SORT_CALLS) {
3264                 vala = (*A)->calls;
3265                 valb = (*B)->calls;
3266         } else {
3267                 vala = (*A)->total_time;
3268                 valb = (*B)->total_time;
3269         }
3270         if (vala == valb)
3271                 return 0;
3272         if (valb < vala)
3273                 return -1;
3274         return 1;
3275 }
3276
3277 static void
3278 dump_metadata (void)
3279 {
3280         fprintf (outfile, "\nMetadata summary\n");
3281         fprintf (outfile, "\tLoaded images: %d\n", num_images);
3282         if (verbose) {
3283                 ImageDesc *image;
3284                 int i;
3285                 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
3286                         image = image_hash [i];
3287                         while (image) {
3288                                 fprintf (outfile, "\t\t%s\n", image->filename);
3289                                 image = image->next;
3290                         }
3291                 }
3292         }
3293         fprintf (outfile, "\tLoaded assemblies: %d\n", num_assemblies);
3294         if (verbose) {
3295                 AssemblyDesc *assembly;
3296                 int i;
3297                 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
3298                         assembly = assembly_hash [i];
3299                         while (assembly) {
3300                                 fprintf (outfile, "\t\t%s\n", assembly->asmname);
3301                                 assembly = assembly->next;
3302                         }
3303                 }
3304         }
3305 }
3306
3307 static void
3308 dump_methods (void)
3309 {
3310         int i, c;
3311         uint64_t calls = 0;
3312         int header_done = 0;
3313         MethodDesc **methods = malloc (num_methods * sizeof (void*));
3314         MethodDesc *cd;
3315         c = 0;
3316         for (i = 0; i < HASH_SIZE; ++i) {
3317                 cd = method_hash [i];
3318                 while (cd) {
3319                         cd->total_time = cd->self_time + cd->callee_time;
3320                         methods [c++] = cd;
3321                         cd = cd->next;
3322                 }
3323         }
3324         qsort (methods, num_methods, sizeof (void*), compare_method);
3325         for (i = 0; i < num_methods; ++i) {
3326                 uint64_t msecs;
3327                 uint64_t smsecs;
3328                 cd = methods [i];
3329                 if (!cd->calls)
3330                         continue;
3331                 calls += cd->calls;
3332                 msecs = cd->total_time / 1000000;
3333                 smsecs = (cd->total_time - cd->callee_time) / 1000000;
3334                 if (!msecs && !verbose)
3335                         continue;
3336                 if (!header_done++) {
3337                         fprintf (outfile, "\nMethod call summary\n");
3338                         fprintf (outfile, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
3339                 }
3340                 fprintf (outfile, "%8llu %8llu %10llu %s\n",
3341                         (unsigned long long) (msecs),
3342                         (unsigned long long) (smsecs),
3343                         (unsigned long long) (cd->calls),
3344                         cd->name);
3345                 dump_traces (&cd->traces, "calls");
3346         }
3347         if (calls)
3348                 fprintf (outfile, "Total calls: %llu\n", (unsigned long long) calls);
3349 }
3350
3351 static int
3352 compare_heap_class (const void *a, const void *b)
3353 {
3354         HeapClassDesc *const*A = a;
3355         HeapClassDesc *const*B = b;
3356         uint64_t vala, valb;
3357         if (alloc_sort_mode == ALLOC_SORT_BYTES) {
3358                 vala = (*A)->total_size;
3359                 valb = (*B)->total_size;
3360         } else {
3361                 vala = (*A)->count;
3362                 valb = (*B)->count;
3363         }
3364         if (valb == vala)
3365                 return 0;
3366         if (valb < vala)
3367                 return -1;
3368         return 1;
3369 }
3370
3371 static int
3372 compare_rev_class (const void *a, const void *b)
3373 {
3374         const HeapClassRevRef *A = a;
3375         const HeapClassRevRef *B = b;
3376         if (B->count == A->count)
3377                 return 0;
3378         if (B->count < A->count)
3379                 return -1;
3380         return 1;
3381 }
3382
3383 static void
3384 dump_rev_claases (HeapClassRevRef *revs, int count)
3385 {
3386         int j;
3387         if (!show_traces)
3388                 return;
3389         if (!count)
3390                 return;
3391         for (j = 0; j < count; ++j) {
3392                 HeapClassDesc *cd = revs [j].klass;
3393                 fprintf (outfile, "\t\t%llu references from: %s\n",
3394                         (unsigned long long) (revs [j].count),
3395                         cd->klass->name);
3396         }
3397 }
3398
3399 static void
3400 heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
3401 {
3402         uint64_t size = 0;
3403         uint64_t count = 0;
3404         int ccount = 0;
3405         int i;
3406         HeapClassDesc *cd;
3407         HeapClassDesc **sorted;
3408         sorted = malloc (sizeof (void*) * hs->class_count);
3409         for (i = 0; i < hs->hash_size; ++i) {
3410                 cd = hs->class_hash [i];
3411                 if (!cd)
3412                         continue;
3413                 count += cd->count;
3414                 size += cd->total_size;
3415                 sorted [ccount++] = cd;
3416         }
3417         hs->sorted = sorted;
3418         qsort (sorted, ccount, sizeof (void*), compare_heap_class);
3419         fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %zd\n",
3420                 hs_num,
3421                 (hs->timestamp - startup_time)/1000000000.0,
3422                 (unsigned long long) (size),
3423                 (unsigned long long) (count),
3424                 ccount, hs->num_roots);
3425         if (!verbose && ccount > 30)
3426                 ccount = 30;
3427         fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
3428         for (i = 0; i < ccount; ++i) {
3429                 HeapClassRevRef *rev_sorted;
3430                 int j, k;
3431                 HeapClassDesc *ocd = NULL;
3432                 cd = sorted [i];
3433                 if (last_hs)
3434                         ocd = heap_class_lookup (last_hs, cd->klass);
3435                 fprintf (outfile, "\t%10llu %10llu %8llu %s",
3436                         (unsigned long long) (cd->total_size),
3437                         (unsigned long long) (cd->count),
3438                         (unsigned long long) (cd->total_size / cd->count),
3439                         cd->klass->name);
3440                 if (ocd) {
3441                         int64_t bdiff = cd->total_size - ocd->total_size;
3442                         int64_t cdiff = cd->count - ocd->count;
3443                         fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", (long long) bdiff, (long long) cdiff);
3444                 } else {
3445                         fprintf (outfile, "\n");
3446                 }
3447                 if (!collect_traces)
3448                         continue;
3449                 rev_sorted = malloc (cd->rev_count * sizeof (HeapClassRevRef));
3450                 k = 0;
3451                 for (j = 0; j < cd->rev_hash_size; ++j) {
3452                         if (cd->rev_hash [j].klass)
3453                                 rev_sorted [k++] = cd->rev_hash [j];
3454                 }
3455                 assert (cd->rev_count == k);
3456                 qsort (rev_sorted, cd->rev_count, sizeof (HeapClassRevRef), compare_rev_class);
3457                 if (cd->root_references)
3458                         fprintf (outfile, "\t\t%zd root references (%zd pinning)\n", cd->root_references, cd->pinned_references);
3459                 dump_rev_claases (rev_sorted, cd->rev_count);
3460                 free (rev_sorted);
3461         }
3462         free (sorted);
3463 }
3464
3465 static int
3466 compare_heap_shots (const void *a, const void *b)
3467 {
3468         HeapShot *const*A = a;
3469         HeapShot *const*B = b;
3470         if ((*B)->timestamp == (*A)->timestamp)
3471                 return 0;
3472         if ((*B)->timestamp > (*A)->timestamp)
3473                 return -1;
3474         return 1;
3475 }
3476
3477 static void
3478 dump_heap_shots (void)
3479 {
3480         HeapShot **hs_sorted;
3481         HeapShot *hs;
3482         HeapShot *last_hs = NULL;
3483         int i;
3484         if (!heap_shots)
3485                 return;
3486         hs_sorted = malloc (num_heap_shots * sizeof (void*));
3487         fprintf (outfile, "\nHeap shot summary\n");
3488         i = 0;
3489         for (hs = heap_shots; hs; hs = hs->next)
3490                 hs_sorted [i++] = hs;
3491         qsort (hs_sorted, num_heap_shots, sizeof (void*), compare_heap_shots);
3492         for (i = 0; i < num_heap_shots; ++i) {
3493                 hs = hs_sorted [i];
3494                 heap_shot_summary (hs, i, last_hs);
3495                 last_hs = hs;
3496         }
3497 }
3498
3499 /* This is a very basic escape function that escapes < > and &
3500    Ideally we'd use g_markup_escape_string but that function isn't
3501          available in Mono's eglib. This was written without looking at the
3502          source of that function in glib. */
3503 static char *
3504 escape_string_for_xml (const char *string)
3505 {
3506         GString *string_builder = g_string_new (NULL);
3507         const char *start, *p;
3508
3509         start = p = string;
3510         while (*p) {
3511                 while (*p && *p != '&' && *p != '<' && *p != '>')
3512                         p++;
3513
3514                 g_string_append_len (string_builder, start, p - start);
3515
3516                 if (*p == '\0')
3517                         break;
3518
3519                 switch (*p) {
3520                 case '<':
3521                         g_string_append (string_builder, "&lt;");
3522                         break;
3523
3524                 case '>':
3525                         g_string_append (string_builder, "&gt;");
3526                         break;
3527
3528                 case '&':
3529                         g_string_append (string_builder, "&amp;");
3530                         break;
3531
3532                 default:
3533                         break;
3534                 }
3535
3536                 p++;
3537                 start = p;
3538         }
3539
3540         return g_string_free (string_builder, FALSE);
3541 }
3542
3543 static int
3544 sort_assemblies (gconstpointer a, gconstpointer b)
3545 {
3546         CoverageAssembly *assembly_a = *(CoverageAssembly **)a;
3547         CoverageAssembly *assembly_b = *(CoverageAssembly **)b;
3548
3549         if (assembly_a->name == NULL && assembly_b->name == NULL)
3550                 return 0;
3551         else if (assembly_a->name == NULL)
3552                 return -1;
3553         else if (assembly_b->name == NULL)
3554                 return 1;
3555
3556         return strcmp (assembly_a->name, assembly_b->name);
3557 }
3558
3559 static void
3560 dump_coverage (void)
3561 {
3562         if (!coverage_methods && !coverage_assemblies)
3563                 return;
3564
3565         gather_coverage_statements ();
3566         fprintf (outfile, "\nCoverage Summary:\n");
3567
3568         if (coverage_outfile) {
3569                 fprintf (coverage_outfile, "<?xml version=\"1.0\"?>\n");
3570                 fprintf (coverage_outfile, "<coverage version=\"0.3\">\n");
3571         }
3572
3573         g_ptr_array_sort (coverage_assemblies, sort_assemblies);
3574
3575         for (guint i = 0; i < coverage_assemblies->len; i++) {
3576                 CoverageAssembly *assembly = coverage_assemblies->pdata[i];
3577                 GPtrArray *classes;
3578
3579                 if (assembly->number_of_methods != 0) {
3580                         int percentage = ((assembly->fully_covered + assembly->partially_covered) * 100) / assembly->number_of_methods;
3581                         fprintf (outfile, "\t%s (%s) %d%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, percentage, assembly->number_of_methods, assembly->fully_covered);
3582                 } else
3583                         fprintf (outfile, "\t%s (%s) ?%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, assembly->number_of_methods, assembly->fully_covered);
3584
3585                 if (coverage_outfile) {
3586                         char *escaped_name, *escaped_filename;
3587                         escaped_name = escape_string_for_xml (assembly->name);
3588                         escaped_filename = escape_string_for_xml (assembly->filename);
3589
3590                         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);
3591
3592                         g_free (escaped_name);
3593                         g_free (escaped_filename);
3594                 }
3595
3596                 classes = g_hash_table_lookup (coverage_assembly_classes, assembly->name);
3597                 if (classes) {
3598                         for (guint j = 0; j < classes->len; j++) {
3599                                 CoverageClass *klass = classes->pdata[j];
3600
3601                                 if (klass->number_of_methods > 0) {
3602                                         int percentage = ((klass->fully_covered + klass->partially_covered) * 100) / klass->number_of_methods;
3603                                         fprintf (outfile, "\t\t%s %d%% covered (%d methods - %d covered)\n", klass->class_name, percentage, klass->number_of_methods, klass->fully_covered);
3604                                 } else
3605                                         fprintf (outfile, "\t\t%s ?%% covered (%d methods - %d covered)\n", klass->class_name, klass->number_of_methods, klass->fully_covered);
3606
3607                                 if (coverage_outfile) {
3608                                         char *escaped_name;
3609                                         escaped_name = escape_string_for_xml (klass->class_name);
3610
3611                                         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);
3612                                         g_free (escaped_name);
3613                                 }
3614                         }
3615                 }
3616         }
3617
3618         for (guint i = 0; i < coverage_methods->len; i++) {
3619                 CoverageMethod *method = coverage_methods->pdata[i];
3620
3621                 if (coverage_outfile) {
3622                         char *escaped_assembly, *escaped_class, *escaped_method, *escaped_sig, *escaped_filename;
3623
3624                         escaped_assembly = escape_string_for_xml (method->assembly_name);
3625                         escaped_class = escape_string_for_xml (method->class_name);
3626                         escaped_method = escape_string_for_xml (method->method_name);
3627                         escaped_sig = escape_string_for_xml (method->method_signature);
3628                         escaped_filename = escape_string_for_xml (method->filename);
3629
3630                         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);
3631
3632                         g_free (escaped_assembly);
3633                         g_free (escaped_class);
3634                         g_free (escaped_method);
3635                         g_free (escaped_sig);
3636                         g_free (escaped_filename);
3637
3638                         for (guint j = 0; j < method->coverage->len; j++) {
3639                                 CoverageCoverage *coverage = method->coverage->pdata[j];
3640                                 fprintf (coverage_outfile, "\t\t<statement offset=\"%d\" counter=\"%d\" line=\"%d\" column=\"%d\"/>\n", coverage->offset, coverage->count, coverage->line, coverage->column);
3641                         }
3642                         fprintf (coverage_outfile, "\t</method>\n");
3643                 }
3644         }
3645
3646         if (coverage_outfile) {
3647                 fprintf (coverage_outfile, "</coverage>\n");
3648                 fclose (coverage_outfile);
3649                 coverage_outfile = NULL;
3650         }
3651 }
3652
3653 static void
3654 flush_context (ProfContext *ctx)
3655 {
3656         ThreadContext *thread;
3657         /* FIXME: sometimes there are leftovers: indagate */
3658         for (thread = ctx->threads; thread; thread = thread->next) {
3659                 while (thread->stack_id) {
3660                         if (debug)
3661                                 fprintf (outfile, "thread %p has %d items on stack\n", (void*)thread->thread_id, thread->stack_id);
3662                         pop_method (thread, thread->stack [thread->stack_id - 1], thread->last_time);
3663                 }
3664         }
3665 }
3666
3667 static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,heapshot,counters,coverage";
3668
3669 static const char*
3670 match_option (const char *p, const char *opt)
3671 {
3672         int len = strlen (opt);
3673         if (strncmp (p, opt, len) == 0) {
3674                 if (p [len] == ',')
3675                         len++;
3676                 return p + len;
3677         }
3678         return p;
3679 }
3680
3681 static int
3682 print_reports (ProfContext *ctx, const char *reps, int parse_only)
3683 {
3684         const char *opt;
3685         const char *p;
3686         for (p = reps; *p; p = opt) {
3687                 if ((opt = match_option (p, "header")) != p) {
3688                         if (!parse_only)
3689                                 dump_header (ctx);
3690                         continue;
3691                 }
3692                 if ((opt = match_option (p, "thread")) != p) {
3693                         if (!parse_only)
3694                                 dump_threads (ctx);
3695                         continue;
3696                 }
3697                 if ((opt = match_option (p, "domain")) != p) {
3698                         if (!parse_only)
3699                                 dump_domains (ctx);
3700                         continue;
3701                 }
3702                 if ((opt = match_option (p, "context")) != p) {
3703                         if (!parse_only)
3704                                 dump_remctxs (ctx);
3705                         continue;
3706                 }
3707                 if ((opt = match_option (p, "gc")) != p) {
3708                         if (!parse_only)
3709                                 dump_gcs ();
3710                         continue;
3711                 }
3712                 if ((opt = match_option (p, "jit")) != p) {
3713                         if (!parse_only)
3714                                 dump_jit ();
3715                         continue;
3716                 }
3717                 if ((opt = match_option (p, "alloc")) != p) {
3718                         if (!parse_only)
3719                                 dump_allocations ();
3720                         continue;
3721                 }
3722                 if ((opt = match_option (p, "call")) != p) {
3723                         if (!parse_only)
3724                                 dump_methods ();
3725                         continue;
3726                 }
3727                 if ((opt = match_option (p, "metadata")) != p) {
3728                         if (!parse_only)
3729                                 dump_metadata ();
3730                         continue;
3731                 }
3732                 if ((opt = match_option (p, "exception")) != p) {
3733                         if (!parse_only)
3734                                 dump_exceptions ();
3735                         continue;
3736                 }
3737                 if ((opt = match_option (p, "monitor")) != p) {
3738                         if (!parse_only)
3739                                 dump_monitors ();
3740                         continue;
3741                 }
3742                 if ((opt = match_option (p, "heapshot")) != p) {
3743                         if (!parse_only)
3744                                 dump_heap_shots ();
3745                         continue;
3746                 }
3747                 if ((opt = match_option (p, "sample")) != p) {
3748                         if (!parse_only)
3749                                 dump_samples ();
3750                         continue;
3751                 }
3752                 if ((opt = match_option (p, "counters")) != p) {
3753                         if (!parse_only)
3754                                 dump_counters ();
3755                         continue;
3756                 }
3757                 if ((opt = match_option (p, "coverage")) != p) {
3758                         if (!parse_only)
3759                                 dump_coverage ();
3760                         continue;
3761                 }
3762                 return 0;
3763         }
3764         return 1;
3765 }
3766
3767 static int
3768 add_find_spec (const char *p)
3769 {
3770         if (p [0] == 'S' && p [1] == ':') {
3771                 char *vale;
3772                 find_size = strtoul (p + 2, &vale, 10);
3773                 return 1;
3774         } else if (p [0] == 'T' && p [1] == ':') {
3775                 find_name = p + 2;
3776                 return 1;
3777         }
3778         return 0;
3779 }
3780
3781 static void
3782 usage (void)
3783 {
3784         printf ("Mono log profiler report version %d.%d\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR);
3785         printf ("Usage: mprof-report [OPTIONS] FILENAME\n");
3786         printf ("FILENAME can be '-' to read from standard input.\n");
3787         printf ("Options:\n");
3788         printf ("\t--help               display this help\n");
3789         printf ("\t--out=FILE           write to FILE instead of stdout\n");
3790         printf ("\t--traces             collect and show backtraces\n");
3791         printf ("\t--maxframes=NUM      limit backtraces to NUM entries\n");
3792         printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
3793         printf ("\t                     %s\n", reports);
3794         printf ("\t--method-sort=MODE   sort methods according to MODE: total, self, calls\n");
3795         printf ("\t--alloc-sort=MODE    sort allocations according to MODE: bytes, count\n");
3796         printf ("\t--counters-sort=MODE sort counters according to MODE: time, category\n");
3797         printf ("\t                     only accessible in verbose mode\n");
3798         printf ("\t--track=OB1[,OB2...] track what happens to objects OBJ1, O2 etc.\n");
3799         printf ("\t--find=FINDSPEC      find and track objects matching FINFSPEC, where FINDSPEC is:\n");
3800         printf ("\t                     S:minimum_size or T:partial_name\n");
3801         printf ("\t--thread=THREADID    consider just the data for thread THREADID\n");
3802         printf ("\t--time=FROM-TO       consider data FROM seconds from startup up to TO seconds\n");
3803         printf ("\t--verbose            increase verbosity level\n");
3804         printf ("\t--debug              display decoding debug info for mprof-report devs\n");
3805         printf ("\t--coverage-out=FILE  write the coverage info to FILE as XML\n");
3806 }
3807
3808 int
3809 main (int argc, char *argv[])
3810 {
3811         ProfContext *ctx;
3812         int i;
3813         outfile = stdout;
3814         for (i = 1; i < argc; ++i) {
3815                 if (strcmp ("--debug", argv [i]) == 0) {
3816                         debug++;
3817                 } else if (strcmp ("--help", argv [i]) == 0) {
3818                         usage ();
3819                         return 0;
3820                 } else if (strncmp ("--alloc-sort=", argv [i], 13) == 0) {
3821                         const char *val = argv [i] + 13;
3822                         if (strcmp (val, "bytes") == 0) {
3823                                 alloc_sort_mode = ALLOC_SORT_BYTES;
3824                         } else if (strcmp (val, "count") == 0) {
3825                                 alloc_sort_mode = ALLOC_SORT_COUNT;
3826                         } else {
3827                                 usage ();
3828                                 return 1;
3829                         }
3830                 } else if (strncmp ("--method-sort=", argv [i], 14) == 0) {
3831                         const char *val = argv [i] + 14;
3832                         if (strcmp (val, "total") == 0) {
3833                                 method_sort_mode = METHOD_SORT_TOTAL;
3834                         } else if (strcmp (val, "self") == 0) {
3835                                 method_sort_mode = METHOD_SORT_SELF;
3836                         } else if (strcmp (val, "calls") == 0) {
3837                                 method_sort_mode = METHOD_SORT_CALLS;
3838                         } else {
3839                                 usage ();
3840                                 return 1;
3841                         }
3842                 } else if (strncmp ("--counters-sort=", argv [i], 16) == 0) {
3843                         const char *val = argv [i] + 16;
3844                         if (strcmp (val, "time") == 0) {
3845                                 counters_sort_mode = COUNTERS_SORT_TIME;
3846                         } else if (strcmp (val, "category") == 0) {
3847                                 counters_sort_mode = COUNTERS_SORT_CATEGORY;
3848                         } else {
3849                                 usage ();
3850                                 return 1;
3851                         }
3852                 } else if (strncmp ("--reports=", argv [i], 10) == 0) {
3853                         const char *val = argv [i] + 10;
3854                         if (!print_reports (NULL, val, 1)) {
3855                                 usage ();
3856                                 return 1;
3857                         }
3858                         reports = val;
3859                 } else if (strncmp ("--out=", argv [i], 6) == 0) {
3860                         const char *val = argv [i] + 6;
3861                         outfile = fopen (val, "w");
3862                         if (!outfile) {
3863                                 printf ("Cannot open output file: %s\n", val);
3864                                 return 1;
3865                         }
3866                 } else if (strncmp ("--maxframes=", argv [i], 12) == 0) {
3867                         const char *val = argv [i] + 12;
3868                         char *vale;
3869                         trace_max = strtoul (val, &vale, 10);
3870                 } else if (strncmp ("--find=", argv [i], 7) == 0) {
3871                         const char *val = argv [i] + 7;
3872                         if (!add_find_spec (val)) {
3873                                 usage ();
3874                                 return 1;
3875                         }
3876                 } else if (strncmp ("--track=", argv [i], 8) == 0) {
3877                         const char *val = argv [i] + 8;
3878                         char *vale;
3879                         while (*val) {
3880                                 uintptr_t tracked_obj;
3881                                 if (*val == ',') {
3882                                         val++;
3883                                         continue;
3884                                 }
3885                                 tracked_obj = strtoul (val, &vale, 0);
3886                                 found_object (tracked_obj);
3887                                 val = vale;
3888                         }
3889                 } else if (strncmp ("--thread=", argv [i], 9) == 0) {
3890                         const char *val = argv [i] + 9;
3891                         char *vale;
3892                         thread_filter = strtoul (val, &vale, 0);
3893                 } else if (strncmp ("--time=", argv [i], 7) == 0) {
3894                         char *val = pstrdup (argv [i] + 7);
3895                         double from_secs, to_secs;
3896                         char *top = strchr (val, '-');
3897                         if (!top) {
3898                                 usage ();
3899                                 return 1;
3900                         }
3901                         *top++ = 0;
3902                         from_secs = atof (val);
3903                         to_secs = atof (top);
3904                         free (val);
3905                         if (from_secs > to_secs) {
3906                                 usage ();
3907                                 return 1;
3908                         }
3909                         time_from = from_secs * 1000000000;
3910                         time_to = to_secs * 1000000000;
3911                         use_time_filter = 1;
3912                 } else if (strcmp ("--verbose", argv [i]) == 0) {
3913                         verbose++;
3914                 } else if (strcmp ("--traces", argv [i]) == 0) {
3915                         show_traces = 1;
3916                         collect_traces = 1;
3917                 } else if (strncmp ("--coverage-out=", argv [i], 15) == 0) {
3918                         const char *val = argv [i] + 15;
3919                         coverage_outfile = fopen (val, "w");
3920                         if (!coverage_outfile) {
3921                                 printf ("Cannot open output file: %s\n", val);
3922                                 return 1;
3923                         }
3924                 } else {
3925                         break;
3926                 }
3927         }
3928         if (i >= argc) {
3929                 usage ();
3930                 return 2;
3931         }
3932         ctx = load_file (argv [i]);
3933         if (!ctx) {
3934                 printf ("Not a log profiler data file (or unsupported version).\n");
3935                 return 1;
3936         }
3937         while (decode_buffer (ctx));
3938         flush_context (ctx);
3939         if (num_tracked_objects)
3940                 return 0;
3941         print_reports (ctx, reports, 0);
3942         return 0;
3943 }