Merge pull request #5095 from kumpera/fix_fullaot
[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                                 add_class (ptr_base + ptrdiff, (char*)p);
2484                                 while (*p) p++;
2485                                 p++;
2486                         } else if (mtype == TYPE_IMAGE) {
2487                                 if (ctx->data_version < 13)
2488                                         decode_uleb128 (p, &p); /* flags */
2489                                 if (debug)
2490                                         fprintf (outfile, "%s image %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
2491                                 if (subtype == TYPE_END_LOAD)
2492                                         add_image (ptr_base + ptrdiff, (char*)p);
2493                                 while (*p) p++;
2494                                 p++;
2495                         } else if (mtype == TYPE_ASSEMBLY) {
2496                                 if (ctx->data_version > 13)
2497                                         decode_sleb128 (p, &p); // image
2498                                 if (ctx->data_version < 13)
2499                                         decode_uleb128 (p, &p); /* flags */
2500                                 if (debug)
2501                                         fprintf (outfile, "%s assembly %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
2502                                 if (subtype == TYPE_END_LOAD)
2503                                         add_assembly (ptr_base + ptrdiff, (char*)p);
2504                                 while (*p) p++;
2505                                 p++;
2506                         } else if (mtype == TYPE_DOMAIN) {
2507                                 if (ctx->data_version < 13)
2508                                         decode_uleb128 (p, &p); /* flags */
2509                                 DomainContext *nd = get_domain (ctx, ptr_base + ptrdiff);
2510                                 /* no subtype means it's a name event, rather than start/stop */
2511                                 if (subtype == 0)
2512                                         nd->friendly_name = pstrdup ((char *) p);
2513                                 if (debug) {
2514                                         if (subtype == 0)
2515                                                 fprintf (outfile, "domain %p named at %llu: %s\n", (void *) (ptr_base + ptrdiff), (unsigned long long) time_base, p);
2516                                         else
2517                                                 fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
2518                                 }
2519                                 if (subtype == 0) {
2520                                         while (*p) p++;
2521                                         p++;
2522                                 }
2523                         } else if (mtype == TYPE_CONTEXT) {
2524                                 if (ctx->data_version < 13)
2525                                         decode_uleb128 (p, &p); /* flags */
2526                                 intptr_t domaindiff = decode_sleb128 (p, &p);
2527                                 if (debug)
2528                                         fprintf (outfile, "%s context %p (%p) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), (void *) (ptr_base + domaindiff), (unsigned long long) time_base);
2529                                 if (subtype == TYPE_END_LOAD)
2530                                         get_remctx (ctx, ptr_base + ptrdiff)->domain_id = ptr_base + domaindiff;
2531                         } else if (mtype == TYPE_THREAD) {
2532                                 if (ctx->data_version < 13)
2533                                         decode_uleb128 (p, &p); /* flags */
2534                                 ThreadContext *nt = get_thread (ctx, ptr_base + ptrdiff);
2535                                 /* no subtype means it's a name event, rather than start/stop */
2536                                 if (subtype == 0)
2537                                         nt->name = pstrdup ((char*)p);
2538                                 if (debug) {
2539                                         if (subtype == 0)
2540                                                 fprintf (outfile, "thread %p named at %llu: %s\n", (void*)(ptr_base + ptrdiff), (unsigned long long) time_base, p);
2541                                         else
2542                                                 fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
2543                                 }
2544                                 if (subtype == 0) {
2545                                         while (*p) p++;
2546                                         p++;
2547                                 }
2548                         }
2549                         break;
2550                 }
2551                 case TYPE_ALLOC: {
2552                         int has_bt = *p & TYPE_ALLOC_BT;
2553                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2554                         intptr_t ptrdiff = decode_sleb128 (p, &p);
2555                         intptr_t objdiff = decode_sleb128 (p, &p);
2556                         uint64_t len;
2557                         int num_bt = 0;
2558                         MethodDesc* sframes [8];
2559                         MethodDesc** frames = sframes;
2560                         ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
2561                         len = decode_uleb128 (p, &p);
2562                         LOG_TIME (time_base, tdiff);
2563                         time_base += tdiff;
2564                         if (debug)
2565                                 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);
2566                         if (has_bt) {
2567                                 num_bt = 8;
2568                                 frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2569                                 if (!frames) {
2570                                         fprintf (outfile, "Cannot load backtrace\n");
2571                                         return 0;
2572                                 }
2573                         }
2574                         if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2575                                 BackTrace *bt;
2576                                 cd->allocs++;
2577                                 cd->alloc_size += len;
2578                                 if (has_bt)
2579                                         bt = add_trace_methods (frames, num_bt, &cd->traces, len);
2580                                 else
2581                                         bt = add_trace_thread (thread, &cd->traces, len);
2582                                 if (find_size && len >= find_size) {
2583                                         if (!find_name || strstr (cd->name, find_name))
2584                                                 found_object (OBJ_ADDR (objdiff));
2585                                 } else if (!find_size && find_name && strstr (cd->name, find_name)) {
2586                                         found_object (OBJ_ADDR (objdiff));
2587                                 }
2588                                 if (num_tracked_objects)
2589                                         tracked_creation (OBJ_ADDR (objdiff), cd, len, bt, time_base);
2590                         }
2591                         if (frames != sframes)
2592                                 g_free (frames);
2593                         break;
2594                 }
2595                 case TYPE_METHOD: {
2596                         int subtype = *p & 0xf0;
2597                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2598                         int64_t ptrdiff = decode_sleb128 (p, &p);
2599                         LOG_TIME (time_base, tdiff);
2600                         time_base += tdiff;
2601                         method_base += ptrdiff;
2602                         if (subtype == TYPE_JIT) {
2603                                 intptr_t codediff = decode_sleb128 (p, &p);
2604                                 int codelen = decode_uleb128 (p, &p);
2605                                 MethodDesc *jitted_method;
2606                                 if (debug)
2607                                         fprintf (outfile, "jitted method %p (%s), size: %d, code: %p\n", (void*)(method_base), p, codelen, (void*)(ptr_base + codediff));
2608                                 jitted_method = add_method (method_base, (char*)p, ptr_base + codediff, codelen);
2609                                 if (!(time_base >= time_from && time_base < time_to))
2610                                         jitted_method->ignore_jit = 1;
2611                                 while (*p) p++;
2612                                 p++;
2613                         } else {
2614                                 MethodDesc *method;
2615                                 if ((thread_filter && thread_filter != thread->thread_id))
2616                                         break;
2617                                 if (!(time_base >= time_from && time_base < time_to))
2618                                         break;
2619                                 method = lookup_method (method_base);
2620                                 if (subtype == TYPE_ENTER) {
2621                                         add_trace_thread (thread, &method->traces, 1);
2622                                         push_method (thread, method, time_base);
2623                                 } else {
2624                                         pop_method (thread, method, time_base);
2625                                 }
2626                                 if (debug)
2627                                         fprintf (outfile, "%s method %s\n", subtype == TYPE_ENTER? "enter": subtype == TYPE_EXC_LEAVE? "exleave": "leave", method->name);
2628                         }
2629                         break;
2630                 }
2631                 case TYPE_HEAP: {
2632                         int subtype = *p & 0xf0;
2633                         if (subtype == TYPE_HEAP_OBJECT) {
2634                                 HeapObjectDesc *ho = NULL;
2635                                 int i;
2636                                 intptr_t objdiff;
2637                                 if (ctx->data_version > 12) {
2638                                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2639                                         LOG_TIME (time_base, tdiff);
2640                                         time_base += tdiff;
2641                                         objdiff = decode_sleb128 (p, &p);
2642                                 } else
2643                                         objdiff = decode_sleb128 (p + 1, &p);
2644                                 intptr_t ptrdiff = decode_sleb128 (p, &p);
2645                                 uint64_t size = decode_uleb128 (p, &p);
2646                                 uintptr_t num = decode_uleb128 (p, &p);
2647                                 uintptr_t ref_offset = 0;
2648                                 uintptr_t last_obj_offset = 0;
2649                                 ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
2650                                 if (size) {
2651                                         HeapClassDesc *hcd = add_heap_shot_class (thread->current_heap_shot, cd, size);
2652                                         if (collect_traces) {
2653                                                 ho = alloc_heap_obj (OBJ_ADDR (objdiff), hcd, num);
2654                                                 add_heap_shot_obj (thread->current_heap_shot, ho);
2655                                                 ref_offset = 0;
2656                                         }
2657                                 } else {
2658                                         if (collect_traces)
2659                                                 ho = heap_shot_obj_add_refs (thread->current_heap_shot, OBJ_ADDR (objdiff), num, &ref_offset);
2660                                 }
2661                                 for (i = 0; i < num; ++i) {
2662                                         /* FIXME: use object distance to measure how good
2663                                          * the GC is at keeping related objects close
2664                                          */
2665                                         uintptr_t offset = ctx->data_version > 1? last_obj_offset + decode_uleb128 (p, &p): -1;
2666                                         intptr_t obj1diff = decode_sleb128 (p, &p);
2667                                         last_obj_offset = offset;
2668                                         if (collect_traces)
2669                                                 ho->refs [ref_offset + i] = OBJ_ADDR (obj1diff);
2670                                         if (num_tracked_objects)
2671                                                 track_obj_reference (OBJ_ADDR (obj1diff), OBJ_ADDR (objdiff), cd);
2672                                 }
2673                                 if (debug && size)
2674                                         fprintf (outfile, "traced object %p, size %llu (%s), refs: %zd\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) size, cd->name, num);
2675                         } else if (subtype == TYPE_HEAP_ROOT) {
2676                                 uintptr_t num;
2677                                 if (ctx->data_version > 12) {
2678                                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2679                                         LOG_TIME (time_base, tdiff);
2680                                         time_base += tdiff;
2681                                         num = decode_uleb128 (p, &p);
2682                                 } else
2683                                         num = decode_uleb128 (p + 1, &p);
2684                                 uintptr_t gc_num G_GNUC_UNUSED = decode_uleb128 (p, &p);
2685                                 int i;
2686                                 for (i = 0; i < num; ++i) {
2687                                         intptr_t objdiff = decode_sleb128 (p, &p);
2688                                         int root_type;
2689                                         if (ctx->data_version > 12)
2690                                                 root_type = *p++;
2691                                         else
2692                                                 root_type = decode_uleb128 (p, &p);
2693                                         /* we just discard the extra info for now */
2694                                         uintptr_t extra_info = decode_uleb128 (p, &p);
2695                                         if (debug)
2696                                                 fprintf (outfile, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff), get_root_name (root_type));
2697                                         if (collect_traces)
2698                                                 thread_add_root (thread, OBJ_ADDR (objdiff), root_type, extra_info);
2699                                 }
2700                         } else if (subtype == TYPE_HEAP_END) {
2701                                 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2702                                 LOG_TIME (time_base, tdiff);
2703                                 time_base += tdiff;
2704                                 if (debug)
2705                                         fprintf (outfile, "heap shot end\n");
2706                                 if (collect_traces) {
2707                                         HeapShot *hs = thread->current_heap_shot;
2708                                         if (hs && thread->num_roots) {
2709                                                 /* transfer the root ownershipt to the heapshot */
2710                                                 hs->num_roots = thread->num_roots;
2711                                                 hs->roots = thread->roots;
2712                                                 hs->roots_extra = thread->roots_extra;
2713                                                 hs->roots_types = thread->roots_types;
2714                                         } else {
2715                                                 g_free (thread->roots);
2716                                                 g_free (thread->roots_extra);
2717                                                 g_free (thread->roots_types);
2718                                         }
2719                                         thread->num_roots = 0;
2720                                         thread->size_roots = 0;
2721                                         thread->roots = NULL;
2722                                         thread->roots_extra = NULL;
2723                                         thread->roots_types = NULL;
2724                                         heap_shot_resolve_reverse_refs (hs);
2725                                         heap_shot_mark_objects (hs);
2726                                         heap_shot_free_objects (hs);
2727                                 }
2728                                 thread->current_heap_shot = NULL;
2729                         } else if (subtype == TYPE_HEAP_START) {
2730                                 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2731                                 LOG_TIME (time_base, tdiff);
2732                                 time_base += tdiff;
2733                                 if (debug)
2734                                         fprintf (outfile, "heap shot start\n");
2735                                 thread->current_heap_shot = new_heap_shot (time_base);
2736                         }
2737                         break;
2738                 }
2739                 case TYPE_MONITOR: {
2740                         int has_bt = *p & TYPE_MONITOR_BT;
2741                         int event;
2742                         if (ctx->data_version < 13)
2743                                 event = (*p >> 4) & 0x3;
2744                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2745                         if (ctx->data_version > 13)
2746                                 event = *p++;
2747                         intptr_t objdiff = decode_sleb128 (p, &p);
2748                         MethodDesc* sframes [8];
2749                         MethodDesc** frames = sframes;
2750                         int record;
2751                         int num_bt = 0;
2752                         LOG_TIME (time_base, tdiff);
2753                         time_base += tdiff;
2754                         record = (!thread_filter || thread_filter == thread->thread_id);
2755                         if (!(time_base >= time_from && time_base < time_to))
2756                                 record = 0;
2757                         MonitorDesc *mdesc = lookup_monitor (OBJ_ADDR (objdiff));
2758                         if (event == MONO_PROFILER_MONITOR_CONTENTION) {
2759                                 if (record) {
2760                                         monitor_contention++;
2761                                         mdesc->contentions++;
2762                                         thread->monitor = mdesc;
2763                                         thread->contention_start = time_base;
2764                                 }
2765                         } else if (event == MONO_PROFILER_MONITOR_FAIL) {
2766                                 if (record) {
2767                                         monitor_failed++;
2768                                         if (thread->monitor && thread->contention_start) {
2769                                                 uint64_t wait_time = time_base - thread->contention_start;
2770                                                 if (wait_time > thread->monitor->max_wait_time)
2771                                                         thread->monitor->max_wait_time = wait_time;
2772                                                 thread->monitor->wait_time += wait_time;
2773                                                 thread->monitor = NULL;
2774                                                 thread->contention_start = 0;
2775                                         }
2776                                 }
2777                         } else if (event == MONO_PROFILER_MONITOR_DONE) {
2778                                 if (record) {
2779                                         monitor_acquired++;
2780                                         if (thread->monitor && thread->contention_start) {
2781                                                 uint64_t wait_time = time_base - thread->contention_start;
2782                                                 if (wait_time > thread->monitor->max_wait_time)
2783                                                         thread->monitor->max_wait_time = wait_time;
2784                                                 thread->monitor->wait_time += wait_time;
2785                                                 thread->monitor = NULL;
2786                                                 thread->contention_start = 0;
2787                                         }
2788                                 }
2789                         }
2790                         if (has_bt) {
2791                                 num_bt = 8;
2792                                 frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2793                                 if (!frames) {
2794                                         fprintf (outfile, "Cannot load backtrace\n");
2795                                         return 0;
2796                                 }
2797                                 if (record && event == MONO_PROFILER_MONITOR_CONTENTION)
2798                                         add_trace_methods (frames, num_bt, &mdesc->traces, 1);
2799                         } else {
2800                                 if (record)
2801                                         add_trace_thread (thread, &mdesc->traces, 1);
2802                         }
2803                         if (debug)
2804                                 fprintf (outfile, "monitor %s for object %p\n", monitor_ev_name (event), (void*)OBJ_ADDR (objdiff));
2805                         if (frames != sframes)
2806                                 g_free (frames);
2807                         break;
2808                 }
2809                 case TYPE_EXCEPTION: {
2810                         int subtype = *p & 0x70;
2811                         int has_bt = *p & TYPE_THROW_BT;
2812                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2813                         MethodDesc* sframes [8];
2814                         MethodDesc** frames = sframes;
2815                         int record;
2816                         LOG_TIME (time_base, tdiff);
2817                         time_base += tdiff;
2818                         record = (!thread_filter || thread_filter == thread->thread_id);
2819                         if (!(time_base >= time_from && time_base < time_to))
2820                                 record = 0;
2821                         if (subtype == TYPE_CLAUSE) {
2822                                 int clause_type;
2823                                 if (ctx->data_version > 12)
2824                                         clause_type = *p++;
2825                                 else
2826                                         clause_type = decode_uleb128 (p, &p);
2827                                 int clause_num = decode_uleb128 (p, &p);
2828                                 int64_t ptrdiff = decode_sleb128 (p, &p);
2829                                 method_base += ptrdiff;
2830                                 if (ctx->data_version > 13)
2831                                         decode_uleb128 (p, &p); // exception object
2832                                 if (record)
2833                                         clause_summary [clause_type]++;
2834                                 if (debug)
2835                                         fprintf (outfile, "clause %s (%d) in method %s\n", clause_name (clause_type), clause_num, lookup_method (method_base)->name);
2836                         } else {
2837                                 intptr_t objdiff = decode_sleb128 (p, &p);
2838                                 if (record)
2839                                         throw_count++;
2840                                 if (has_bt) {
2841                                         has_bt = 8;
2842                                         frames = decode_bt (ctx, sframes, &has_bt, p, &p, ptr_base, &method_base);
2843                                         if (!frames) {
2844                                                 fprintf (outfile, "Cannot load backtrace\n");
2845                                                 return 0;
2846                                         }
2847                                         if (record)
2848                                                 add_trace_methods (frames, has_bt, &exc_traces, 1);
2849                                 } else {
2850                                         if (record)
2851                                                 add_trace_thread (thread, &exc_traces, 1);
2852                                 }
2853                                 if (frames != sframes)
2854                                         g_free (frames);
2855                                 if (debug)
2856                                         fprintf (outfile, "throw %p\n", (void*)OBJ_ADDR (objdiff));
2857                         }
2858                         break;
2859                 }
2860                 case TYPE_RUNTIME: {
2861                         int subtype = *p & 0xf0;
2862                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2863                         LOG_TIME (time_base, tdiff);
2864                         time_base += tdiff;
2865                         if (subtype == TYPE_JITHELPER) {
2866                                 int type;
2867                                 if (ctx->data_version > 12)
2868                                         type = *p++;
2869                                 else
2870                                         type = decode_uleb128 (p, &p);
2871                                 intptr_t codediff = decode_sleb128 (p, &p);
2872                                 int codelen = decode_uleb128 (p, &p);
2873                                 const char *name;
2874                                 if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
2875                                         name = (const char *)p;
2876                                         while (*p) p++;
2877                                                 p++;
2878                                 } else {
2879                                         name = code_buffer_desc (type);
2880                                 }
2881                                 num_jit_helpers++;
2882                                 jit_helpers_code_size += codelen;
2883                                 if (debug)
2884                                         fprintf (outfile, "jit helper %s, size: %d, code: %p\n", name, codelen, (void*)(ptr_base + codediff));
2885                         }
2886                         break;
2887                 }
2888                 case TYPE_SAMPLE: {
2889                         int subtype = *p & 0xf0;
2890                         if (subtype == TYPE_SAMPLE_HIT) {
2891                                 int i;
2892                                 int sample_type;
2893                                 uint64_t tstamp;
2894                                 if (ctx->data_version > 12) {
2895                                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2896                                         LOG_TIME (time_base, tdiff);
2897                                         time_base += tdiff;
2898                                         sample_type = *p++;
2899                                         tstamp = time_base;
2900                                 } else {
2901                                         sample_type = decode_uleb128 (p + 1, &p);
2902                                         tstamp = decode_uleb128 (p, &p);
2903                                 }
2904                                 void *tid = (void *) thread_id;
2905                                 if (ctx->data_version > 10)
2906                                         tid = (void *) (ptr_base + decode_sleb128 (p, &p));
2907                                 int count = decode_uleb128 (p, &p);
2908                                 for (i = 0; i < count; ++i) {
2909                                         uintptr_t ip = ptr_base + decode_sleb128 (p, &p);
2910                                         if ((tstamp >= time_from && tstamp < time_to))
2911                                                 add_stat_sample (sample_type, ip);
2912                                         if (debug)
2913                                                 fprintf (outfile, "sample hit, type: %d at %p for thread %p\n", sample_type, (void*)ip, tid);
2914                                 }
2915                                 if (ctx->data_version > 5) {
2916                                         count = decode_uleb128 (p, &p);
2917                                         for (i = 0; i < count; ++i) {
2918                                                 MethodDesc *method;
2919                                                 int64_t ptrdiff = decode_sleb128 (p, &p);
2920                                                 method_base += ptrdiff;
2921                                                 method = lookup_method (method_base);
2922                                                 if (debug)
2923                                                         fprintf (outfile, "sample hit bt %d: %s\n", i, method->name);
2924                                                 if (ctx->data_version < 13) {
2925                                                         decode_sleb128 (p, &p); /* il offset */
2926                                                         decode_sleb128 (p, &p); /* native offset */
2927                                                 }
2928                                         }
2929                                 }
2930                         } else if (subtype == TYPE_SAMPLE_USYM) {
2931                                 /* un unmanaged symbol description */
2932                                 uintptr_t addr;
2933                                 if (ctx->data_version > 12) {
2934                                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2935                                         LOG_TIME (time_base, tdiff);
2936                                         time_base += tdiff;
2937                                         addr = ptr_base + decode_sleb128 (p, &p);
2938                                 } else
2939                                         addr = ptr_base + decode_sleb128 (p + 1, &p);
2940                                 uintptr_t size = decode_uleb128 (p, &p);
2941                                 char *name;
2942                                 name = pstrdup ((char*)p);
2943                                 add_unmanaged_symbol (addr, name, size);
2944                                 if (debug)
2945                                         fprintf (outfile, "unmanaged symbol %s at %p\n", name, (void*)addr);
2946                                 while (*p) p++;
2947                                 p++;
2948                         } else if (subtype == TYPE_SAMPLE_UBIN) {
2949                                 /* un unmanaged binary loaded in memory */
2950                                 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2951                                 uintptr_t addr = decode_sleb128 (p, &p);
2952                                 if (ctx->data_version > 13)
2953                                         addr += ptr_base;
2954                                 uint64_t offset G_GNUC_UNUSED = decode_uleb128 (p, &p);
2955                                 uintptr_t size = decode_uleb128 (p, &p);
2956                                 char *name;
2957                                 LOG_TIME (time_base, tdiff);
2958                                 time_base += tdiff;
2959                                 name = pstrdup ((char*)p);
2960                                 add_unmanaged_binary (addr, name, size);
2961                                 if (debug)
2962                                         fprintf (outfile, "unmanaged binary %s at %p\n", name, (void*)addr);
2963                                 while (*p) p++;
2964                                 p++;
2965                         } else if (subtype == TYPE_SAMPLE_COUNTERS_DESC) {
2966                                 uint64_t i, len;
2967                                 if (ctx->data_version > 12) {
2968                                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
2969                                         LOG_TIME (time_base, tdiff);
2970                                         time_base += tdiff;
2971                                         len = decode_uleb128 (p, &p);
2972                                 } else
2973                                         len = decode_uleb128 (p + 1, &p);
2974                                 for (i = 0; i < len; i++) {
2975                                         uint64_t type, unit, variance, index;
2976                                         uint64_t section = decode_uleb128 (p, &p);
2977                                         char *section_str, *name;
2978                                         if (section != MONO_COUNTER_PERFCOUNTERS) {
2979                                                 section_str = (char*) section_name (section);
2980                                         } else {
2981                                                 section_str = pstrdup ((char*)p);
2982                                                 while (*p++);
2983                                         }
2984                                         name = pstrdup ((char*)p);
2985                                         while (*p++);
2986                                         if (ctx->data_version > 12) {
2987                                                 type = *p++;
2988                                                 unit = *p++;
2989                                                 variance = *p++;
2990                                         } else {
2991                                                 type = decode_uleb128 (p, &p);
2992                                                 unit = decode_uleb128 (p, &p);
2993                                                 variance = decode_uleb128 (p, &p);
2994                                         }
2995                                         index = decode_uleb128 (p, &p);
2996                                         add_counter (section_str, name, (int)type, (int)unit, (int)variance, (int)index);
2997                                 }
2998                         } else if (subtype == TYPE_SAMPLE_COUNTERS) {
2999                                 int i;
3000                                 CounterValue *value, *previous = NULL;
3001                                 CounterList *list;
3002                                 uint64_t timestamp; // milliseconds since startup
3003                                 if (ctx->data_version > 12) {
3004                                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
3005                                         LOG_TIME (time_base, tdiff);
3006                                         time_base += tdiff;
3007                                         timestamp = (time_base - startup_time) / 1000 / 1000;
3008                                 } else
3009                                         timestamp = decode_uleb128 (p + 1, &p);
3010                                 uint64_t time_between = timestamp / 1000 * 1000 * 1000 * 1000 + startup_time;
3011                                 while (1) {
3012                                         uint64_t type, index = decode_uleb128 (p, &p);
3013                                         if (index == 0)
3014                                                 break;
3015
3016                                         for (list = counters; list; list = list->next) {
3017                                                 if (list->counter->index == (int)index) {
3018                                                         previous = list->counter->values_last;
3019                                                         break;
3020                                                 }
3021                                         }
3022
3023                                         if (ctx->data_version > 12)
3024                                                 type = *p++;
3025                                         else
3026                                                 type = decode_uleb128 (p, &p);
3027
3028                                         value = (CounterValue *) g_calloc (1, sizeof (CounterValue));
3029                                         value->timestamp = timestamp;
3030
3031                                         switch (type) {
3032                                         case MONO_COUNTER_INT:
3033 #if SIZEOF_VOID_P == 4
3034                                         case MONO_COUNTER_WORD:
3035 #endif
3036                                                 value->buffer = (unsigned char *)g_malloc (sizeof (int32_t));
3037                                                 *(int32_t*)value->buffer = (int32_t)decode_sleb128 (p, &p) + (previous ? (*(int32_t*)previous->buffer) : 0);
3038                                                 break;
3039                                         case MONO_COUNTER_UINT:
3040                                                 value->buffer = (unsigned char *) g_malloc (sizeof (uint32_t));
3041                                                 *(uint32_t*)value->buffer = (uint32_t)decode_uleb128 (p, &p) + (previous ? (*(uint32_t*)previous->buffer) : 0);
3042                                                 break;
3043                                         case MONO_COUNTER_LONG:
3044 #if SIZEOF_VOID_P == 8
3045                                         case MONO_COUNTER_WORD:
3046 #endif
3047                                         case MONO_COUNTER_TIME_INTERVAL:
3048                                                 value->buffer = (unsigned char *) g_malloc (sizeof (int64_t));
3049                                                 *(int64_t*)value->buffer = (int64_t)decode_sleb128 (p, &p) + (previous ? (*(int64_t*)previous->buffer) : 0);
3050                                                 break;
3051                                         case MONO_COUNTER_ULONG:
3052                                                 value->buffer = (unsigned char *) g_malloc (sizeof (uint64_t));
3053                                                 *(uint64_t*)value->buffer = (uint64_t)decode_uleb128 (p, &p) + (previous ? (*(uint64_t*)previous->buffer) : 0);
3054                                                 break;
3055                                         case MONO_COUNTER_DOUBLE:
3056                                                 value->buffer = (unsigned char *) g_malloc (sizeof (double));
3057 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
3058                                                 for (i = 0; i < sizeof (double); i++)
3059 #else
3060                                                 for (i = sizeof (double) - 1; i >= 0; i--)
3061 #endif
3062                                                         value->buffer[i] = *p++;
3063                                                 break;
3064                                         case MONO_COUNTER_STRING:
3065                                                 if (*p++ == 0) {
3066                                                         value->buffer = NULL;
3067                                                 } else {
3068                                                         value->buffer = (unsigned char*) pstrdup ((char*)p);
3069                                                         while (*p++);
3070                                                 }
3071                                                 break;
3072                                         }
3073                                         if (time_between >= time_from && time_between <= time_to)
3074                                                 add_counter_value (index, value);
3075                                 }
3076                         } else {
3077                                 return 0;
3078                         }
3079                         break;
3080                 }
3081                 case TYPE_COVERAGE:{
3082                         int subtype = *p & 0xf0;
3083                         switch (subtype) {
3084                         case TYPE_COVERAGE_METHOD: {
3085                                 CoverageMethod *method = g_new0 (CoverageMethod, 1);
3086                                 const char *assembly, *klass, *name, *sig, *filename;
3087                                 int token, n_offsets, method_id;
3088
3089                                 p++;
3090
3091                                 if (ctx->data_version > 12) {
3092                                         uint64_t tdiff = decode_uleb128 (p, &p);
3093                                         LOG_TIME (time_base, tdiff);
3094                                         time_base += tdiff;
3095                                 }
3096
3097                                 assembly = (const char *)p; while (*p) p++; p++;
3098                                 klass = (const char *)p; while (*p) p++; p++;
3099                                 name = (const char *)p; while (*p) p++; p++;
3100                                 sig = (const char *)p; while (*p) p++; p++;
3101                                 filename = (const char *)p; while (*p) p++; p++;
3102
3103                                 token = decode_uleb128 (p, &p);
3104                                 method_id = decode_uleb128 (p, &p);
3105                                 n_offsets = decode_uleb128 (p, &p);
3106
3107                                 method->assembly_name = g_strdup (assembly);
3108                                 method->class_name = g_strdup (klass);
3109                                 method->method_name = g_strdup (name);
3110                                 method->method_signature = g_strdup (sig);
3111                                 method->filename = g_strdup (filename);
3112                                 method->token = token;
3113                                 method->n_statements = n_offsets;
3114                                 method->coverage = g_ptr_array_new ();
3115                                 method->method_id = method_id;
3116
3117                                 coverage_add_method (method);
3118
3119                                 break;
3120                         }
3121                         case TYPE_COVERAGE_STATEMENT: {
3122                                 CoverageCoverage *coverage = g_new0 (CoverageCoverage, 1);
3123                                 int offset, count, line, column, method_id;
3124
3125                                 p++;
3126
3127                                 if (ctx->data_version > 12) {
3128                                         uint64_t tdiff = decode_uleb128 (p, &p);
3129                                         LOG_TIME (time_base, tdiff);
3130                                         time_base += tdiff;
3131                                 }
3132
3133                                 method_id = decode_uleb128 (p, &p);
3134                                 offset = decode_uleb128 (p, &p);
3135                                 count = decode_uleb128 (p, &p);
3136                                 line = decode_uleb128 (p, &p);
3137                                 column = decode_uleb128 (p, &p);
3138
3139                                 coverage->method_id = method_id;
3140                                 coverage->offset = offset;
3141                                 coverage->count = count;
3142                                 coverage->line = line;
3143                                 coverage->column = column;
3144
3145                                 coverage_add_coverage (coverage);
3146                                 break;
3147                         }
3148                         case TYPE_COVERAGE_ASSEMBLY: {
3149                                 CoverageAssembly *assembly = g_new0 (CoverageAssembly, 1);
3150                                 char *name, *guid, *filename;
3151                                 int number_of_methods, fully_covered, partially_covered;
3152                                 p++;
3153
3154                                 if (ctx->data_version > 12) {
3155                                         uint64_t tdiff = decode_uleb128 (p, &p);
3156                                         LOG_TIME (time_base, tdiff);
3157                                         time_base += tdiff;
3158                                 }
3159
3160                                 name = (char *)p; while (*p) p++; p++;
3161                                 guid = (char *)p; while (*p) p++; p++;
3162                                 filename = (char *)p; while (*p) p++; p++;
3163                                 number_of_methods = decode_uleb128 (p, &p);
3164                                 fully_covered = decode_uleb128 (p, &p);
3165                                 partially_covered = decode_uleb128 (p, &p);
3166
3167                                 assembly->name = g_strdup (name);
3168                                 assembly->guid = g_strdup (guid);
3169                                 assembly->filename = g_strdup (filename);
3170                                 assembly->number_of_methods = number_of_methods;
3171                                 assembly->fully_covered = fully_covered;
3172                                 assembly->partially_covered = partially_covered;
3173
3174                                 coverage_add_assembly (assembly);
3175                                 break;
3176                         }
3177                         case TYPE_COVERAGE_CLASS: {
3178                                 CoverageClass *klass = g_new0 (CoverageClass, 1);
3179                                 char *assembly_name, *class_name;
3180                                 int number_of_methods, fully_covered, partially_covered;
3181                                 p++;
3182
3183                                 if (ctx->data_version > 12) {
3184                                         uint64_t tdiff = decode_uleb128 (p, &p);
3185                                         LOG_TIME (time_base, tdiff);
3186                                         time_base += tdiff;
3187                                 }
3188
3189                                 assembly_name = (char *)p; while (*p) p++; p++;
3190                                 class_name = (char *)p; while (*p) p++; p++;
3191                                 number_of_methods = decode_uleb128 (p, &p);
3192                                 fully_covered = decode_uleb128 (p, &p);
3193                                 partially_covered = decode_uleb128 (p, &p);
3194
3195                                 klass->assembly_name = g_strdup (assembly_name);
3196                                 klass->class_name = g_strdup (class_name);
3197                                 klass->number_of_methods = number_of_methods;
3198                                 klass->fully_covered = fully_covered;
3199                                 klass->partially_covered = partially_covered;
3200
3201                                 coverage_add_class (klass);
3202                                 break;
3203                         }
3204
3205                         default:
3206                                 break;
3207                         }
3208                         break;
3209                 }
3210                 case TYPE_META: {
3211                         int subtype = *p & 0xf0;
3212                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
3213                         LOG_TIME (time_base, tdiff);
3214                         time_base += tdiff;
3215                         if (subtype == TYPE_SYNC_POINT) {
3216                                 int type = *p++;
3217                                 if (debug)
3218                                         fprintf (outfile, "sync point %i (%s)\n", type, sync_point_name (type));
3219                         }
3220                         break;
3221                 }
3222                 default:
3223                         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);
3224                         exit (1);
3225                 }
3226                 record_event_stats (event, p - start);
3227         }
3228         thread->last_time = time_base;
3229         for (i = 0; i < thread->stack_id; ++i)
3230                 thread->stack [i]->recurse_count = 0;
3231         return 1;
3232 }
3233
3234 static int
3235 read_header_string (ProfContext *ctx, char **field)
3236 {
3237         if (!load_data (ctx, 4))
3238                 return 0;
3239
3240         if (!load_data (ctx, read_int32 (ctx->buf)))
3241                 return 0;
3242
3243         *field = pstrdup ((const char *) ctx->buf);
3244
3245         return 1;
3246 }
3247
3248 static ProfContext*
3249 load_file (char *name)
3250 {
3251         unsigned char *p;
3252         ProfContext *ctx = (ProfContext *) g_calloc (sizeof (ProfContext), 1);
3253         if (strcmp (name, "-") == 0)
3254                 ctx->file = stdin;
3255         else
3256                 ctx->file = fopen (name, "rb");
3257         if (!ctx->file) {
3258                 printf ("Cannot open file: %s\n", name);
3259                 exit (1);
3260         }
3261 #if defined (HAVE_SYS_ZLIB)
3262         if (ctx->file != stdin)
3263                 ctx->gzfile = gzdopen (fileno (ctx->file), "rb");
3264 #endif
3265         if (!load_data (ctx, 30))
3266                 return NULL;
3267         p = ctx->buf;
3268         if (read_int32 (p) != LOG_HEADER_ID || p [6] > LOG_DATA_VERSION)
3269                 return NULL;
3270         ctx->version_major = p [4];
3271         ctx->version_minor = p [5];
3272         ctx->data_version = p [6];
3273         /* reading 64 bit files on 32 bit systems not supported yet */
3274         if (p [7] > sizeof (void*))
3275                 return NULL;
3276         if (read_int32 (p + 20)) /* flags must be 0 */
3277                 return NULL;
3278         ctx->startup_time = read_int64 (p + 8);
3279         ctx->timer_overhead = read_int32 (p + 16);
3280         ctx->pid = read_int32 (p + 24);
3281         ctx->port = read_int16 (p + 28);
3282         if (ctx->version_major >= 1) {
3283                 if (!read_header_string (ctx, &ctx->args))
3284                         return NULL;
3285                 if (!read_header_string (ctx, &ctx->arch))
3286                         return NULL;
3287                 if (!read_header_string (ctx, &ctx->os))
3288                         return NULL;
3289         } else {
3290                 if (!load_data (ctx, 2)) /* old opsys field, was never used */
3291                         return NULL;
3292         }
3293         return ctx;
3294 }
3295
3296 enum {
3297         ALLOC_SORT_BYTES,
3298         ALLOC_SORT_COUNT
3299 };
3300 static int alloc_sort_mode = ALLOC_SORT_BYTES;
3301
3302 static int
3303 compare_class (const void *a, const void *b)
3304 {
3305         ClassDesc *const *A = (ClassDesc *const *)a;
3306         ClassDesc *const *B = (ClassDesc *const *)b;
3307         uint64_t vala, valb;
3308         if (alloc_sort_mode == ALLOC_SORT_BYTES) {
3309                 vala = (*A)->alloc_size;
3310                 valb = (*B)->alloc_size;
3311         } else {
3312                 vala = (*A)->allocs;
3313                 valb = (*B)->allocs;
3314         }
3315         if (valb == vala)
3316                 return 0;
3317         if (valb < vala)
3318                 return -1;
3319         return 1;
3320 }
3321
3322 static void
3323 dump_header (ProfContext *ctx)
3324 {
3325         time_t st = ctx->startup_time / 1000;
3326         char *t = ctime (&st);
3327         fprintf (outfile, "\nMono log profiler data\n");
3328         fprintf (outfile, "\tProfiler version: %d.%d\n", ctx->version_major, ctx->version_minor);
3329         fprintf (outfile, "\tData version: %d\n", ctx->data_version);
3330         if (ctx->version_major >= 1) {
3331                 fprintf (outfile, "\tArguments: %s\n", ctx->args);
3332                 fprintf (outfile, "\tArchitecture: %s\n", ctx->arch);
3333                 fprintf (outfile, "\tOperating system: %s\n", ctx->os);
3334         }
3335         fprintf (outfile, "\tMean timer overhead: %d nanoseconds\n", ctx->timer_overhead);
3336         fprintf (outfile, "\tProgram startup: %s", t);
3337         if (ctx->pid)
3338                 fprintf (outfile, "\tProgram ID: %d\n", ctx->pid);
3339         if (ctx->port)
3340                 fprintf (outfile, "\tServer listening on: %d\n", ctx->port);
3341 }
3342
3343 static void
3344 dump_traces (TraceDesc *traces, const char *desc)
3345 {
3346         int j;
3347         if (!show_traces)
3348                 return;
3349         if (!traces->count)
3350                 return;
3351         sort_context_array (traces);
3352         for (j = 0; j < traces->count; ++j) {
3353                 int k;
3354                 BackTrace *bt;
3355                 bt = traces->traces [j].bt;
3356                 if (!bt->count)
3357                         continue;
3358                 fprintf (outfile, "\t%llu %s from:\n", (unsigned long long) traces->traces [j].count, desc);
3359                 for (k = 0; k < bt->count; ++k)
3360                         fprintf (outfile, "\t\t%s\n", bt->methods [k]->name);
3361         }
3362 }
3363
3364 static void
3365 dump_threads (ProfContext *ctx)
3366 {
3367         ThreadContext *thread;
3368         fprintf (outfile, "\nThread summary\n");
3369         for (thread = ctx->threads; thread; thread = thread->next) {
3370                 if (thread->thread_id) {
3371                         fprintf (outfile, "\tThread: %p, name: \"%s\"\n", (void*)thread->thread_id, thread->name? thread->name: "");
3372                 }
3373         }
3374 }
3375
3376 static void
3377 dump_domains (ProfContext *ctx)
3378 {
3379         fprintf (outfile, "\nDomain summary\n");
3380
3381         for (DomainContext *domain = ctx->domains; domain; domain = domain->next)
3382                 fprintf (outfile, "\tDomain: %p, friendly name: \"%s\"\n", (void *) domain->domain_id, domain->friendly_name);
3383 }
3384
3385 static void
3386 dump_remctxs (ProfContext *ctx)
3387 {
3388         fprintf (outfile, "\nContext summary\n");
3389
3390         for (RemCtxContext *remctx = ctx->remctxs; remctx; remctx = remctx->next)
3391                 fprintf (outfile, "\tContext: %p, domain: %p\n", (void *) remctx->remctx_id, (void *) remctx->domain_id);
3392 }
3393
3394 static void
3395 dump_exceptions (void)
3396 {
3397         int i;
3398         fprintf (outfile, "\nException summary\n");
3399         fprintf (outfile, "\tThrows: %llu\n", (unsigned long long) throw_count);
3400         dump_traces (&exc_traces, "throws");
3401         for (i = 0; i <= MONO_EXCEPTION_CLAUSE_FAULT; ++i) {
3402                 if (!clause_summary [i])
3403                         continue;
3404                 fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), (unsigned long long) clause_summary [i]);
3405         }
3406 }
3407
3408 static int
3409 compare_monitor (const void *a, const void *b)
3410 {
3411         MonitorDesc *const *A = (MonitorDesc *const *)a;
3412         MonitorDesc *const *B = (MonitorDesc *const *)b;
3413         if ((*B)->wait_time == (*A)->wait_time)
3414                 return 0;
3415         if ((*B)->wait_time < (*A)->wait_time)
3416                 return -1;
3417         return 1;
3418 }
3419
3420 static void
3421 dump_monitors (void)
3422 {
3423         MonitorDesc **monitors;
3424         int i, j;
3425         if (!num_monitors)
3426                 return;
3427         monitors = (MonitorDesc **) g_malloc (sizeof (void*) * num_monitors);
3428         for (i = 0, j = 0; i < SMALL_HASH_SIZE; ++i) {
3429                 MonitorDesc *mdesc = monitor_hash [i];
3430                 while (mdesc) {
3431                         monitors [j++] = mdesc;
3432                         mdesc = mdesc->next;
3433                 }
3434         }
3435         qsort (monitors, num_monitors, sizeof (void*), compare_monitor);
3436         fprintf (outfile, "\nMonitor lock summary\n");
3437         for (i = 0; i < num_monitors; ++i) {
3438                 MonitorDesc *mdesc = monitors [i];
3439                 fprintf (outfile, "\tLock object %p: %d contentions\n", (void*)mdesc->objid, (int)mdesc->contentions);
3440                 fprintf (outfile, "\t\t%.6f secs total wait time, %.6f max, %.6f average\n",
3441                         mdesc->wait_time/1000000000.0, mdesc->max_wait_time/1000000000.0, mdesc->wait_time/1000000000.0/mdesc->contentions);
3442                 dump_traces (&mdesc->traces, "contentions");
3443         }
3444         fprintf (outfile, "\tLock contentions: %llu\n", (unsigned long long) monitor_contention);
3445         fprintf (outfile, "\tLock acquired: %llu\n", (unsigned long long) monitor_acquired);
3446         fprintf (outfile, "\tLock failures: %llu\n", (unsigned long long) monitor_failed);
3447 }
3448
3449 static void
3450 dump_gcs (void)
3451 {
3452         int i;
3453         fprintf (outfile, "\nGC summary\n");
3454         fprintf (outfile, "\tGC resizes: %d\n", gc_resizes);
3455         fprintf (outfile, "\tMax heap size: %llu\n", (unsigned long long) max_heap_size);
3456         fprintf (outfile, "\tObject moves: %llu\n", (unsigned long long) gc_object_moves);
3457         for (i = 0; i < 3; ++i) {
3458                 if (!gc_info [i].count)
3459                         continue;
3460                 fprintf (outfile, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
3461                         i, gc_info [i].count,
3462                         (unsigned long long) (gc_info [i].max_time / 1000),
3463                         (unsigned long long) (gc_info [i].total_time / 1000),
3464                         (unsigned long long) (gc_info [i].total_time / gc_info [i].count / 1000));
3465         }
3466         for (i = 0; i < 3; ++i) {
3467                 if (!handle_info [i].max_live)
3468                         continue;
3469                 fprintf (outfile, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
3470                         get_handle_name (i),
3471                         (unsigned long long) (handle_info [i].created),
3472                         (unsigned long long) (handle_info [i].destroyed),
3473                         (unsigned long long) (handle_info [i].max_live));
3474                 dump_traces (&handle_info [i].traces, "created");
3475                 dump_traces (&handle_info [i].destroy_traces, "destroyed");
3476         }
3477 }
3478
3479 static void
3480 dump_jit (void)
3481 {
3482         int i;
3483         int code_size = 0;
3484         int compiled_methods = 0;
3485         MethodDesc* m;
3486         fprintf (outfile, "\nJIT summary\n");
3487         for (i = 0; i < HASH_SIZE; ++i) {
3488                 m = method_hash [i];
3489                 for (m = method_hash [i]; m; m = m->next) {
3490                         if (!m->code || m->ignore_jit)
3491                                 continue;
3492                         compiled_methods++;
3493                         code_size += m->len;
3494                 }
3495         }
3496         fprintf (outfile, "\tCompiled methods: %d\n", compiled_methods);
3497         fprintf (outfile, "\tGenerated code size: %d\n", code_size);
3498         fprintf (outfile, "\tJIT helpers: %d\n", num_jit_helpers);
3499         fprintf (outfile, "\tJIT helpers code size: %d\n", jit_helpers_code_size);
3500 }
3501
3502 static void
3503 dump_allocations (void)
3504 {
3505         int i, c;
3506         intptr_t allocs = 0;
3507         uint64_t size = 0;
3508         int header_done = 0;
3509         ClassDesc **classes = (ClassDesc **) g_malloc (num_classes * sizeof (void*));
3510         ClassDesc *cd;
3511         c = 0;
3512         for (i = 0; i < HASH_SIZE; ++i) {
3513                 cd = class_hash [i];
3514                 while (cd) {
3515                         classes [c++] = cd;
3516                         cd = cd->next;
3517                 }
3518         }
3519         qsort (classes, num_classes, sizeof (void*), compare_class);
3520         for (i = 0; i < num_classes; ++i) {
3521                 cd = classes [i];
3522                 if (!cd->allocs)
3523                         continue;
3524                 allocs += cd->allocs;
3525                 size += cd->alloc_size;
3526                 if (!header_done++) {
3527                         fprintf (outfile, "\nAllocation summary\n");
3528                         fprintf (outfile, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
3529                 }
3530                 fprintf (outfile, "%10llu %10zd %8llu %s\n",
3531                         (unsigned long long) (cd->alloc_size),
3532                         cd->allocs,
3533                         (unsigned long long) (cd->alloc_size / cd->allocs),
3534                         cd->name);
3535                 dump_traces (&cd->traces, "bytes");
3536         }
3537         if (allocs)
3538                 fprintf (outfile, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size, allocs);
3539 }
3540
3541 enum {
3542         METHOD_SORT_TOTAL,
3543         METHOD_SORT_SELF,
3544         METHOD_SORT_CALLS
3545 };
3546
3547 static int method_sort_mode = METHOD_SORT_TOTAL;
3548
3549 static int
3550 compare_method (const void *a, const void *b)
3551 {
3552         MethodDesc *const *A = (MethodDesc *const *)a;
3553         MethodDesc *const *B = (MethodDesc *const *)b;
3554         uint64_t vala, valb;
3555         if (method_sort_mode == METHOD_SORT_SELF) {
3556                 vala = (*A)->self_time;
3557                 valb = (*B)->self_time;
3558         } else if (method_sort_mode == METHOD_SORT_CALLS) {
3559                 vala = (*A)->calls;
3560                 valb = (*B)->calls;
3561         } else {
3562                 vala = (*A)->total_time;
3563                 valb = (*B)->total_time;
3564         }
3565         if (vala == valb)
3566                 return 0;
3567         if (valb < vala)
3568                 return -1;
3569         return 1;
3570 }
3571
3572 static void
3573 dump_metadata (void)
3574 {
3575         fprintf (outfile, "\nMetadata summary\n");
3576         fprintf (outfile, "\tLoaded images: %d\n", num_images);
3577         if (verbose) {
3578                 ImageDesc *image;
3579                 int i;
3580                 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
3581                         image = image_hash [i];
3582                         while (image) {
3583                                 fprintf (outfile, "\t\t%s\n", image->filename);
3584                                 image = image->next;
3585                         }
3586                 }
3587         }
3588         fprintf (outfile, "\tLoaded assemblies: %d\n", num_assemblies);
3589         if (verbose) {
3590                 AssemblyDesc *assembly;
3591                 int i;
3592                 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
3593                         assembly = assembly_hash [i];
3594                         while (assembly) {
3595                                 fprintf (outfile, "\t\t%s\n", assembly->asmname);
3596                                 assembly = assembly->next;
3597                         }
3598                 }
3599         }
3600 }
3601
3602 static void
3603 dump_methods (void)
3604 {
3605         int i, c;
3606         uint64_t calls = 0;
3607         int header_done = 0;
3608         MethodDesc **methods = (MethodDesc **) g_malloc (num_methods * sizeof (void*));
3609         MethodDesc *cd;
3610         c = 0;
3611         for (i = 0; i < HASH_SIZE; ++i) {
3612                 cd = method_hash [i];
3613                 while (cd) {
3614                         cd->total_time = cd->self_time + cd->callee_time;
3615                         methods [c++] = cd;
3616                         cd = cd->next;
3617                 }
3618         }
3619         qsort (methods, num_methods, sizeof (void*), compare_method);
3620         for (i = 0; i < num_methods; ++i) {
3621                 uint64_t msecs;
3622                 uint64_t smsecs;
3623                 cd = methods [i];
3624                 if (!cd->calls)
3625                         continue;
3626                 calls += cd->calls;
3627                 msecs = cd->total_time / 1000000;
3628                 smsecs = (cd->total_time - cd->callee_time) / 1000000;
3629                 if (!msecs && !verbose)
3630                         continue;
3631                 if (!header_done++) {
3632                         fprintf (outfile, "\nMethod call summary\n");
3633                         fprintf (outfile, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
3634                 }
3635                 fprintf (outfile, "%8llu %8llu %10llu %s\n",
3636                         (unsigned long long) (msecs),
3637                         (unsigned long long) (smsecs),
3638                         (unsigned long long) (cd->calls),
3639                         cd->name);
3640                 dump_traces (&cd->traces, "calls");
3641         }
3642         if (calls)
3643                 fprintf (outfile, "Total calls: %llu\n", (unsigned long long) calls);
3644 }
3645
3646 static int
3647 compare_heap_class (const void *a, const void *b)
3648 {
3649         HeapClassDesc *const *A = (HeapClassDesc *const *)a;
3650         HeapClassDesc *const *B = (HeapClassDesc *const *)b;
3651         uint64_t vala, valb;
3652         if (alloc_sort_mode == ALLOC_SORT_BYTES) {
3653                 vala = (*A)->total_size;
3654                 valb = (*B)->total_size;
3655         } else {
3656                 vala = (*A)->count;
3657                 valb = (*B)->count;
3658         }
3659         if (valb == vala)
3660                 return 0;
3661         if (valb < vala)
3662                 return -1;
3663         return 1;
3664 }
3665
3666 static int
3667 compare_rev_class (const void *a, const void *b)
3668 {
3669         const HeapClassRevRef *A = (const HeapClassRevRef *)a;
3670         const HeapClassRevRef *B = (const HeapClassRevRef *)b;
3671         if (B->count == A->count)
3672                 return 0;
3673         if (B->count < A->count)
3674                 return -1;
3675         return 1;
3676 }
3677
3678 static void
3679 dump_rev_claases (HeapClassRevRef *revs, int count)
3680 {
3681         int j;
3682         if (!show_traces)
3683                 return;
3684         if (!count)
3685                 return;
3686         for (j = 0; j < count; ++j) {
3687                 HeapClassDesc *cd = revs [j].klass;
3688                 fprintf (outfile, "\t\t%llu references from: %s\n",
3689                         (unsigned long long) (revs [j].count),
3690                         cd->klass->name);
3691         }
3692 }
3693
3694 static void
3695 heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
3696 {
3697         uint64_t size = 0;
3698         uint64_t count = 0;
3699         int ccount = 0;
3700         int i;
3701         HeapClassDesc *cd;
3702         HeapClassDesc **sorted;
3703         sorted = (HeapClassDesc **) g_malloc (sizeof (void*) * hs->class_count);
3704         for (i = 0; i < hs->hash_size; ++i) {
3705                 cd = hs->class_hash [i];
3706                 if (!cd)
3707                         continue;
3708                 count += cd->count;
3709                 size += cd->total_size;
3710                 sorted [ccount++] = cd;
3711         }
3712         hs->sorted = sorted;
3713         qsort (sorted, ccount, sizeof (void*), compare_heap_class);
3714         fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %zd\n",
3715                 hs_num,
3716                 (hs->timestamp - startup_time)/1000000000.0,
3717                 (unsigned long long) (size),
3718                 (unsigned long long) (count),
3719                 ccount, hs->num_roots);
3720         if (!verbose && ccount > 30)
3721                 ccount = 30;
3722         fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
3723         for (i = 0; i < ccount; ++i) {
3724                 HeapClassRevRef *rev_sorted;
3725                 int j, k;
3726                 HeapClassDesc *ocd = NULL;
3727                 cd = sorted [i];
3728                 if (last_hs)
3729                         ocd = heap_class_lookup (last_hs, cd->klass);
3730                 fprintf (outfile, "\t%10llu %10llu %8llu %s",
3731                         (unsigned long long) (cd->total_size),
3732                         (unsigned long long) (cd->count),
3733                         (unsigned long long) (cd->total_size / cd->count),
3734                         cd->klass->name);
3735                 if (ocd) {
3736                         int64_t bdiff = cd->total_size - ocd->total_size;
3737                         int64_t cdiff = cd->count - ocd->count;
3738                         fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", (long long) bdiff, (long long) cdiff);
3739                 } else {
3740                         fprintf (outfile, "\n");
3741                 }
3742                 if (!collect_traces)
3743                         continue;
3744                 rev_sorted = (HeapClassRevRef *) g_malloc (cd->rev_count * sizeof (HeapClassRevRef));
3745                 k = 0;
3746                 for (j = 0; j < cd->rev_hash_size; ++j) {
3747                         if (cd->rev_hash [j].klass)
3748                                 rev_sorted [k++] = cd->rev_hash [j];
3749                 }
3750                 assert (cd->rev_count == k);
3751                 qsort (rev_sorted, cd->rev_count, sizeof (HeapClassRevRef), compare_rev_class);
3752                 if (cd->root_references)
3753                         fprintf (outfile, "\t\t%zd root references (%zd pinning)\n", cd->root_references, cd->pinned_references);
3754                 dump_rev_claases (rev_sorted, cd->rev_count);
3755                 g_free (rev_sorted);
3756         }
3757         g_free (sorted);
3758 }
3759
3760 static int
3761 compare_heap_shots (const void *a, const void *b)
3762 {
3763         HeapShot *const *A = (HeapShot *const *)a;
3764         HeapShot *const *B = (HeapShot *const *)b;
3765         if ((*B)->timestamp == (*A)->timestamp)
3766                 return 0;
3767         if ((*B)->timestamp > (*A)->timestamp)
3768                 return -1;
3769         return 1;
3770 }
3771
3772 static void
3773 dump_heap_shots (void)
3774 {
3775         HeapShot **hs_sorted;
3776         HeapShot *hs;
3777         HeapShot *last_hs = NULL;
3778         int i;
3779         if (!heap_shots)
3780                 return;
3781         hs_sorted = (HeapShot **) g_malloc (num_heap_shots * sizeof (void*));
3782         fprintf (outfile, "\nHeap shot summary\n");
3783         i = 0;
3784         for (hs = heap_shots; hs; hs = hs->next)
3785                 hs_sorted [i++] = hs;
3786         qsort (hs_sorted, num_heap_shots, sizeof (void*), compare_heap_shots);
3787         for (i = 0; i < num_heap_shots; ++i) {
3788                 hs = hs_sorted [i];
3789                 heap_shot_summary (hs, i, last_hs);
3790                 last_hs = hs;
3791         }
3792 }
3793
3794 /* This is a very basic escape function that escapes < > and &
3795    Ideally we'd use g_markup_escape_string but that function isn't
3796          available in Mono's eglib. This was written without looking at the
3797          source of that function in glib. */
3798 static char *
3799 escape_string_for_xml (const char *string)
3800 {
3801         GString *string_builder = g_string_new (NULL);
3802         const char *start, *p;
3803
3804         start = p = string;
3805         while (*p) {
3806                 while (*p && *p != '&' && *p != '<' && *p != '>')
3807                         p++;
3808
3809                 g_string_append_len (string_builder, start, p - start);
3810
3811                 if (*p == '\0')
3812                         break;
3813
3814                 switch (*p) {
3815                 case '<':
3816                         g_string_append (string_builder, "&lt;");
3817                         break;
3818
3819                 case '>':
3820                         g_string_append (string_builder, "&gt;");
3821                         break;
3822
3823                 case '&':
3824                         g_string_append (string_builder, "&amp;");
3825                         break;
3826
3827                 default:
3828                         break;
3829                 }
3830
3831                 p++;
3832                 start = p;
3833         }
3834
3835         return g_string_free (string_builder, FALSE);
3836 }
3837
3838 static int
3839 sort_assemblies (gconstpointer a, gconstpointer b)
3840 {
3841         CoverageAssembly *assembly_a = *(CoverageAssembly **)a;
3842         CoverageAssembly *assembly_b = *(CoverageAssembly **)b;
3843
3844         if (assembly_a->name == NULL && assembly_b->name == NULL)
3845                 return 0;
3846         else if (assembly_a->name == NULL)
3847                 return -1;
3848         else if (assembly_b->name == NULL)
3849                 return 1;
3850
3851         return strcmp (assembly_a->name, assembly_b->name);
3852 }
3853
3854 static void
3855 dump_coverage (void)
3856 {
3857         if (!coverage_methods && !coverage_assemblies)
3858                 return;
3859
3860         gather_coverage_statements ();
3861         fprintf (outfile, "\nCoverage Summary:\n");
3862
3863         if (coverage_outfile) {
3864                 fprintf (coverage_outfile, "<?xml version=\"1.0\"?>\n");
3865                 fprintf (coverage_outfile, "<coverage version=\"0.3\">\n");
3866         }
3867
3868         g_ptr_array_sort (coverage_assemblies, sort_assemblies);
3869
3870         for (guint i = 0; i < coverage_assemblies->len; i++) {
3871                 CoverageAssembly *assembly = (CoverageAssembly *)coverage_assemblies->pdata[i];
3872                 GPtrArray *classes;
3873
3874                 if (assembly->number_of_methods != 0) {
3875                         int percentage = ((assembly->fully_covered + assembly->partially_covered) * 100) / assembly->number_of_methods;
3876                         fprintf (outfile, "\t%s (%s) %d%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, percentage, assembly->number_of_methods, assembly->fully_covered);
3877                 } else
3878                         fprintf (outfile, "\t%s (%s) ?%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, assembly->number_of_methods, assembly->fully_covered);
3879
3880                 if (coverage_outfile) {
3881                         char *escaped_name, *escaped_filename;
3882                         escaped_name = escape_string_for_xml (assembly->name);
3883                         escaped_filename = escape_string_for_xml (assembly->filename);
3884
3885                         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);
3886
3887                         g_free (escaped_name);
3888                         g_free (escaped_filename);
3889                 }
3890
3891                 classes = (GPtrArray *)g_hash_table_lookup (coverage_assembly_classes, assembly->name);
3892                 if (classes) {
3893                         for (guint j = 0; j < classes->len; j++) {
3894                                 CoverageClass *klass = (CoverageClass *)classes->pdata [j];
3895
3896                                 if (klass->number_of_methods > 0) {
3897                                         int percentage = ((klass->fully_covered + klass->partially_covered) * 100) / klass->number_of_methods;
3898                                         fprintf (outfile, "\t\t%s %d%% covered (%d methods - %d covered)\n", klass->class_name, percentage, klass->number_of_methods, klass->fully_covered);
3899                                 } else
3900                                         fprintf (outfile, "\t\t%s ?%% covered (%d methods - %d covered)\n", klass->class_name, klass->number_of_methods, klass->fully_covered);
3901
3902                                 if (coverage_outfile) {
3903                                         char *escaped_name;
3904                                         escaped_name = escape_string_for_xml (klass->class_name);
3905
3906                                         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);
3907                                         g_free (escaped_name);
3908                                 }
3909                         }
3910                 }
3911         }
3912
3913         for (guint i = 0; i < coverage_methods->len; i++) {
3914                 CoverageMethod *method = (CoverageMethod *)coverage_methods->pdata [i];
3915
3916                 if (coverage_outfile) {
3917                         char *escaped_assembly, *escaped_class, *escaped_method, *escaped_sig, *escaped_filename;
3918
3919                         escaped_assembly = escape_string_for_xml (method->assembly_name);
3920                         escaped_class = escape_string_for_xml (method->class_name);
3921                         escaped_method = escape_string_for_xml (method->method_name);
3922                         escaped_sig = escape_string_for_xml (method->method_signature);
3923                         escaped_filename = escape_string_for_xml (method->filename);
3924
3925                         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);
3926
3927                         g_free (escaped_assembly);
3928                         g_free (escaped_class);
3929                         g_free (escaped_method);
3930                         g_free (escaped_sig);
3931                         g_free (escaped_filename);
3932
3933                         for (guint j = 0; j < method->coverage->len; j++) {
3934                                 CoverageCoverage *coverage = (CoverageCoverage *)method->coverage->pdata [j];
3935                                 fprintf (coverage_outfile, "\t\t<statement offset=\"%d\" counter=\"%d\" line=\"%d\" column=\"%d\"/>\n", coverage->offset, coverage->count, coverage->line, coverage->column);
3936                         }
3937                         fprintf (coverage_outfile, "\t</method>\n");
3938                 }
3939         }
3940
3941         if (coverage_outfile) {
3942                 fprintf (coverage_outfile, "</coverage>\n");
3943                 fclose (coverage_outfile);
3944                 coverage_outfile = NULL;
3945         }
3946 }
3947
3948 #define DUMP_EVENT_STAT(EVENT,SUBTYPE) dump_event (#EVENT, #SUBTYPE, EVENT, SUBTYPE);
3949
3950 static void
3951 dump_event (const char *event_name, const char *subtype_name, int event, int subtype)
3952 {
3953         int idx = event | subtype;
3954         EventStat evt = stats [idx];
3955         if (!evt.count)
3956                 return;
3957
3958         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);
3959 }
3960
3961 static void
3962 dump_stats (void)
3963 {
3964         fprintf (outfile, "\nMlpd statistics\n");
3965         fprintf (outfile, "\tBuffer count %d\toverhead %d (%d bytes per header)\n", buffer_count, buffer_count * BUFFER_HEADER_SIZE, BUFFER_HEADER_SIZE);
3966         fprintf (outfile, "\nEvent details:\n");
3967
3968         DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_NO_BT);
3969         DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_BT);
3970
3971         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_EVENT);
3972         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_RESIZE);
3973         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_MOVE);
3974         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED);
3975         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED);
3976         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED_BT);
3977         DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED_BT);
3978
3979         DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_LOAD);
3980         DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_UNLOAD);
3981
3982         DUMP_EVENT_STAT (TYPE_METHOD, TYPE_LEAVE);
3983         DUMP_EVENT_STAT (TYPE_METHOD, TYPE_ENTER);
3984         DUMP_EVENT_STAT (TYPE_METHOD, TYPE_EXC_LEAVE);
3985         DUMP_EVENT_STAT (TYPE_METHOD, TYPE_JIT);
3986
3987         DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_THROW_NO_BT);
3988         DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_THROW_BT);
3989         DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_CLAUSE);
3990
3991         DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_NO_BT);
3992         DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_BT);
3993
3994         DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_START);
3995         DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_END);
3996         DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_OBJECT);
3997         DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_ROOT);
3998
3999         DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_HIT);
4000         DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_USYM);
4001         DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_UBIN);
4002         DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS_DESC);
4003         DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS);
4004
4005         DUMP_EVENT_STAT (TYPE_RUNTIME, TYPE_JITHELPER);
4006
4007         DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_ASSEMBLY);
4008         DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_METHOD);
4009         DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_STATEMENT);
4010         DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_CLASS);
4011
4012         DUMP_EVENT_STAT (TYPE_META, TYPE_SYNC_POINT);
4013 }
4014
4015
4016
4017 static void
4018 flush_context (ProfContext *ctx)
4019 {
4020         ThreadContext *thread;
4021         /* FIXME: sometimes there are leftovers: indagate */
4022         for (thread = ctx->threads; thread; thread = thread->next) {
4023                 while (thread->stack_id) {
4024                         if (debug)
4025                                 fprintf (outfile, "thread %p has %d items on stack\n", (void*)thread->thread_id, thread->stack_id);
4026                         pop_method (thread, thread->stack [thread->stack_id - 1], thread->last_time);
4027                 }
4028         }
4029 }
4030
4031 static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,heapshot,counters,coverage";
4032
4033 static const char*
4034 match_option (const char *p, const char *opt)
4035 {
4036         int len = strlen (opt);
4037         if (strncmp (p, opt, len) == 0) {
4038                 if (p [len] == ',')
4039                         len++;
4040                 return p + len;
4041         }
4042         return p;
4043 }
4044
4045 static int
4046 print_reports (ProfContext *ctx, const char *reps, int parse_only)
4047 {
4048         const char *opt;
4049         const char *p;
4050         for (p = reps; *p; p = opt) {
4051                 if ((opt = match_option (p, "header")) != p) {
4052                         if (!parse_only)
4053                                 dump_header (ctx);
4054                         continue;
4055                 }
4056                 if ((opt = match_option (p, "thread")) != p) {
4057                         if (!parse_only)
4058                                 dump_threads (ctx);
4059                         continue;
4060                 }
4061                 if ((opt = match_option (p, "domain")) != p) {
4062                         if (!parse_only)
4063                                 dump_domains (ctx);
4064                         continue;
4065                 }
4066                 if ((opt = match_option (p, "context")) != p) {
4067                         if (!parse_only)
4068                                 dump_remctxs (ctx);
4069                         continue;
4070                 }
4071                 if ((opt = match_option (p, "gc")) != p) {
4072                         if (!parse_only)
4073                                 dump_gcs ();
4074                         continue;
4075                 }
4076                 if ((opt = match_option (p, "jit")) != p) {
4077                         if (!parse_only)
4078                                 dump_jit ();
4079                         continue;
4080                 }
4081                 if ((opt = match_option (p, "alloc")) != p) {
4082                         if (!parse_only)
4083                                 dump_allocations ();
4084                         continue;
4085                 }
4086                 if ((opt = match_option (p, "call")) != p) {
4087                         if (!parse_only)
4088                                 dump_methods ();
4089                         continue;
4090                 }
4091                 if ((opt = match_option (p, "metadata")) != p) {
4092                         if (!parse_only)
4093                                 dump_metadata ();
4094                         continue;
4095                 }
4096                 if ((opt = match_option (p, "exception")) != p) {
4097                         if (!parse_only)
4098                                 dump_exceptions ();
4099                         continue;
4100                 }
4101                 if ((opt = match_option (p, "monitor")) != p) {
4102                         if (!parse_only)
4103                                 dump_monitors ();
4104                         continue;
4105                 }
4106                 if ((opt = match_option (p, "heapshot")) != p) {
4107                         if (!parse_only)
4108                                 dump_heap_shots ();
4109                         continue;
4110                 }
4111                 if ((opt = match_option (p, "sample")) != p) {
4112                         if (!parse_only)
4113                                 dump_samples ();
4114                         continue;
4115                 }
4116                 if ((opt = match_option (p, "counters")) != p) {
4117                         if (!parse_only)
4118                                 dump_counters ();
4119                         continue;
4120                 }
4121                 if ((opt = match_option (p, "coverage")) != p) {
4122                         if (!parse_only)
4123                                 dump_coverage ();
4124                         continue;
4125                 }
4126                 if ((opt = match_option (p, "stats")) != p) {
4127                         if (!parse_only)
4128                                 dump_stats ();
4129                         continue;
4130                 }
4131                 return 0;
4132         }
4133         return 1;
4134 }
4135
4136 static int
4137 add_find_spec (const char *p)
4138 {
4139         if (p [0] == 'S' && p [1] == ':') {
4140                 char *vale;
4141                 find_size = strtoul (p + 2, &vale, 10);
4142                 return 1;
4143         } else if (p [0] == 'T' && p [1] == ':') {
4144                 find_name = p + 2;
4145                 return 1;
4146         }
4147         return 0;
4148 }
4149
4150 static void
4151 usage (void)
4152 {
4153         printf ("Mono log profiler report version %d.%d\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR);
4154         printf ("Usage: mprof-report [OPTIONS] FILENAME\n");
4155         printf ("FILENAME can be '-' to read from standard input.\n");
4156         printf ("Options:\n");
4157         printf ("\t--help               display this help\n");
4158         printf ("\t--out=FILE           write to FILE instead of stdout\n");
4159         printf ("\t--traces             collect and show backtraces\n");
4160         printf ("\t--maxframes=NUM      limit backtraces to NUM entries\n");
4161         printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
4162         printf ("\t                     %s\n", reports);
4163         printf ("\t--method-sort=MODE   sort methods according to MODE: total, self, calls\n");
4164         printf ("\t--alloc-sort=MODE    sort allocations according to MODE: bytes, count\n");
4165         printf ("\t--counters-sort=MODE sort counters according to MODE: time, category\n");
4166         printf ("\t                     only accessible in verbose mode\n");
4167         printf ("\t--track=OB1[,OB2...] track what happens to objects OBJ1, O2 etc.\n");
4168         printf ("\t--find=FINDSPEC      find and track objects matching FINFSPEC, where FINDSPEC is:\n");
4169         printf ("\t                     S:minimum_size or T:partial_name\n");
4170         printf ("\t--thread=THREADID    consider just the data for thread THREADID\n");
4171         printf ("\t--time=FROM-TO       consider data FROM seconds from startup up to TO seconds\n");
4172         printf ("\t--verbose            increase verbosity level\n");
4173         printf ("\t--debug              display decoding debug info for mprof-report devs\n");
4174         printf ("\t--coverage-out=FILE  write the coverage info to FILE as XML\n");
4175 }
4176
4177 int
4178 main (int argc, char *argv[])
4179 {
4180         ProfContext *ctx;
4181         int i;
4182         outfile = stdout;
4183         for (i = 1; i < argc; ++i) {
4184                 if (strcmp ("--debug", argv [i]) == 0) {
4185                         debug++;
4186                 } else if (strcmp ("--help", argv [i]) == 0) {
4187                         usage ();
4188                         return 0;
4189                 } else if (strncmp ("--alloc-sort=", argv [i], 13) == 0) {
4190                         const char *val = argv [i] + 13;
4191                         if (strcmp (val, "bytes") == 0) {
4192                                 alloc_sort_mode = ALLOC_SORT_BYTES;
4193                         } else if (strcmp (val, "count") == 0) {
4194                                 alloc_sort_mode = ALLOC_SORT_COUNT;
4195                         } else {
4196                                 usage ();
4197                                 return 1;
4198                         }
4199                 } else if (strncmp ("--method-sort=", argv [i], 14) == 0) {
4200                         const char *val = argv [i] + 14;
4201                         if (strcmp (val, "total") == 0) {
4202                                 method_sort_mode = METHOD_SORT_TOTAL;
4203                         } else if (strcmp (val, "self") == 0) {
4204                                 method_sort_mode = METHOD_SORT_SELF;
4205                         } else if (strcmp (val, "calls") == 0) {
4206                                 method_sort_mode = METHOD_SORT_CALLS;
4207                         } else {
4208                                 usage ();
4209                                 return 1;
4210                         }
4211                 } else if (strncmp ("--counters-sort=", argv [i], 16) == 0) {
4212                         const char *val = argv [i] + 16;
4213                         if (strcmp (val, "time") == 0) {
4214                                 counters_sort_mode = COUNTERS_SORT_TIME;
4215                         } else if (strcmp (val, "category") == 0) {
4216                                 counters_sort_mode = COUNTERS_SORT_CATEGORY;
4217                         } else {
4218                                 usage ();
4219                                 return 1;
4220                         }
4221                 } else if (strncmp ("--reports=", argv [i], 10) == 0) {
4222                         const char *val = argv [i] + 10;
4223                         if (!print_reports (NULL, val, 1)) {
4224                                 usage ();
4225                                 return 1;
4226                         }
4227                         reports = val;
4228                 } else if (strncmp ("--out=", argv [i], 6) == 0) {
4229                         const char *val = argv [i] + 6;
4230                         outfile = fopen (val, "w");
4231                         if (!outfile) {
4232                                 printf ("Cannot open output file: %s\n", val);
4233                                 return 1;
4234                         }
4235                 } else if (strncmp ("--maxframes=", argv [i], 12) == 0) {
4236                         const char *val = argv [i] + 12;
4237                         char *vale;
4238                         trace_max = strtoul (val, &vale, 10);
4239                 } else if (strncmp ("--find=", argv [i], 7) == 0) {
4240                         const char *val = argv [i] + 7;
4241                         if (!add_find_spec (val)) {
4242                                 usage ();
4243                                 return 1;
4244                         }
4245                 } else if (strncmp ("--track=", argv [i], 8) == 0) {
4246                         const char *val = argv [i] + 8;
4247                         char *vale;
4248                         while (*val) {
4249                                 uintptr_t tracked_obj;
4250                                 if (*val == ',') {
4251                                         val++;
4252                                         continue;
4253                                 }
4254                                 tracked_obj = strtoul (val, &vale, 0);
4255                                 found_object (tracked_obj);
4256                                 val = vale;
4257                         }
4258                 } else if (strncmp ("--thread=", argv [i], 9) == 0) {
4259                         const char *val = argv [i] + 9;
4260                         char *vale;
4261                         thread_filter = strtoul (val, &vale, 0);
4262                 } else if (strncmp ("--time=", argv [i], 7) == 0) {
4263                         char *val = pstrdup (argv [i] + 7);
4264                         double from_secs, to_secs;
4265                         char *top = strchr (val, '-');
4266                         if (!top) {
4267                                 usage ();
4268                                 return 1;
4269                         }
4270                         *top++ = 0;
4271                         from_secs = atof (val);
4272                         to_secs = atof (top);
4273                         g_free (val);
4274                         if (from_secs > to_secs) {
4275                                 usage ();
4276                                 return 1;
4277                         }
4278                         time_from = from_secs * 1000000000;
4279                         time_to = to_secs * 1000000000;
4280                         use_time_filter = 1;
4281                 } else if (strcmp ("--verbose", argv [i]) == 0) {
4282                         verbose++;
4283                 } else if (strcmp ("--traces", argv [i]) == 0) {
4284                         show_traces = 1;
4285                         collect_traces = 1;
4286                 } else if (strncmp ("--coverage-out=", argv [i], 15) == 0) {
4287                         const char *val = argv [i] + 15;
4288                         coverage_outfile = fopen (val, "w");
4289                         if (!coverage_outfile) {
4290                                 printf ("Cannot open output file: %s\n", val);
4291                                 return 1;
4292                         }
4293                 } else {
4294                         break;
4295                 }
4296         }
4297         if (i >= argc) {
4298                 usage ();
4299                 return 2;
4300         }
4301         ctx = load_file (argv [i]);
4302         if (!ctx) {
4303                 printf ("Not a log profiler data file (or unsupported version).\n");
4304                 return 1;
4305         }
4306         while (decode_buffer (ctx));
4307         flush_context (ctx);
4308         if (num_tracked_objects)
4309                 return 0;
4310         print_reports (ctx, reports, 0);
4311         return 0;
4312 }