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