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