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