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