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