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