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