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