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