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