Log profiler: be more specific about unknown methods and classes.
[mono.git] / mono / profiler / decode.c
1 /*
2  * decode.c: mprof-report program source: decode and analyze the log profiler data
3  *
4  * Author:
5  *   Paolo Molaro (lupus@ximian.com)
6  *
7  * Copyright 2010 Novell, Inc (http://www.novell.com)
8  */
9 #include <config.h>
10 #include "utils.c"
11 #include "proflog.h"
12 #include <string.h>
13 #include <assert.h>
14 #include <stdio.h>
15 #if !defined(__APPLE__) && !defined(__FreeBSD__)
16 #include <malloc.h>
17 #endif
18 #include <unistd.h>
19 #include <stdlib.h>
20 #if defined (HAVE_SYS_ZLIB)
21 #include <zlib.h>
22 #endif
23 #include <mono/metadata/profiler.h>
24 #include <mono/metadata/object.h>
25 #include <mono/metadata/debug-helpers.h>
26
27 #define HASH_SIZE 9371
28 #define SMALL_HASH_SIZE 31
29
30 static int debug = 0;
31 static int collect_traces = 0;
32 static int show_traces = 0;
33 static int trace_max = 6;
34 static int verbose = 0;
35 static uintptr_t *tracked_objects = 0;
36 static int num_tracked_objects = 0;
37 static uintptr_t thread_filter = 0;
38 static uint64_t find_size = 0;
39 static const char* find_name = NULL;
40 static uint64_t time_from = 0;
41 static uint64_t time_to = 0xffffffffffffffffULL;
42 static uint64_t startup_time = 0;
43 static FILE* outfile = NULL;
44
45 static int32_t
46 read_int16 (unsigned char *p)
47 {
48         int32_t value = *p++;
49         value |= (*p++) << 8;
50         return value;
51 }
52
53 static int32_t
54 read_int32 (unsigned char *p)
55 {
56         int32_t value = *p++;
57         value |= (*p++) << 8;
58         value |= (*p++) << 16;
59         value |= (uint32_t)(*p++) << 24;
60         return value;
61 }
62
63 static int64_t
64 read_int64 (unsigned char *p)
65 {
66         uint64_t value = *p++;
67         value |= (*p++) << 8;
68         value |= (*p++) << 16;
69         value |= (uint64_t)(*p++) << 24;
70         value |= (uint64_t)(*p++) << 32;
71         value |= (uint64_t)(*p++) << 40;
72         value |= (uint64_t)(*p++) << 48;
73         value |= (uint64_t)(*p++) << 54;
74         return value;
75 }
76
77 static char*
78 pstrdup (const char *s)
79 {
80         int len = strlen (s) + 1;
81         char *p = malloc (len);
82         memcpy (p, s, len);
83         return p;
84 }
85
86 static int num_images;
87 typedef struct _ImageDesc ImageDesc;
88 struct _ImageDesc {
89         ImageDesc *next;
90         intptr_t image;
91         char *filename;
92 };
93
94 static ImageDesc* image_hash [SMALL_HASH_SIZE] = {0};
95
96 static void
97 add_image (intptr_t image, char *name)
98 {
99         int slot = ((image >> 2) & 0xffff) % SMALL_HASH_SIZE;
100         ImageDesc *cd = malloc (sizeof (ImageDesc));
101         cd->image = image;
102         cd->filename = pstrdup (name);
103         cd->next = image_hash [slot];
104         image_hash [slot] = cd;
105         num_images++;
106 }
107
108 typedef struct _BackTrace BackTrace;
109 typedef struct {
110         uint64_t count;
111         BackTrace *bt;
112 } CallContext;
113
114 typedef struct {
115         int count;
116         int size;
117         CallContext *traces;
118 } TraceDesc;
119
120 typedef struct _ClassDesc ClassDesc;
121 struct _ClassDesc {
122         ClassDesc *next;
123         intptr_t klass;
124         char *name;
125         intptr_t allocs;
126         uint64_t alloc_size;
127         TraceDesc traces;
128 };
129
130 static ClassDesc* class_hash [HASH_SIZE] = {0};
131 static int num_classes = 0;
132
133 static ClassDesc*
134 add_class (intptr_t klass, const char *name)
135 {
136         int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
137         ClassDesc *cd;
138         cd = class_hash [slot];
139         while (cd && cd->klass != klass)
140                 cd = cd->next;
141         /* we resolved an unknown class (unless we had the code unloaded) */
142         if (cd) {
143                 /*printf ("resolved unknown: %s\n", name);*/
144                 free (cd->name);
145                 cd->name = pstrdup (name);
146                 return cd;
147         }
148         cd = calloc (sizeof (ClassDesc), 1);
149         cd->klass = klass;
150         cd->name = pstrdup (name);
151         cd->next = class_hash [slot];
152         cd->allocs = 0;
153         cd->alloc_size = 0;
154         cd->traces.count = 0;
155         cd->traces.size = 0;
156         cd->traces.traces = NULL;
157         class_hash [slot] = cd;
158         num_classes++;
159         return cd;
160 }
161
162 static ClassDesc *
163 lookup_class (intptr_t klass)
164 {
165         int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
166         ClassDesc *cd = class_hash [slot];
167         while (cd && cd->klass != klass)
168                 cd = cd->next;
169         if (!cd) {
170                 char buf [128];
171                 snprintf (buf, sizeof (buf), "unresolved class %p", (void*)klass);
172                 return add_class (klass, buf);
173         }
174         return cd;
175 }
176
177 typedef struct _MethodDesc MethodDesc;
178 struct _MethodDesc {
179         MethodDesc *next;
180         intptr_t method;
181         char *name;
182         intptr_t code;
183         int len;
184         int recurse_count;
185         uint64_t calls;
186         uint64_t total_time;
187         uint64_t callee_time;
188         uint64_t self_time;
189         TraceDesc traces;
190 };
191
192 static MethodDesc* method_hash [HASH_SIZE] = {0};
193 static int num_methods = 0;
194
195 static MethodDesc*
196 add_method (intptr_t method, const char *name, intptr_t code, int len)
197 {
198         int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
199         MethodDesc *cd;
200         cd = method_hash [slot];
201         while (cd && cd->method != method)
202                 cd = cd->next;
203         /* we resolved an unknown method (unless we had the code unloaded) */
204         if (cd) {
205                 cd->code = code;
206                 cd->len = len;
207                 /*printf ("resolved unknown: %s\n", name);*/
208                 free (cd->name);
209                 cd->name = pstrdup (name);
210                 return cd;
211         }
212         cd = calloc (sizeof (MethodDesc), 1);
213         cd->method = method;
214         cd->name = pstrdup (name);
215         cd->code = code;
216         cd->len = len;
217         cd->calls = 0;
218         cd->total_time = 0;
219         cd->traces.count = 0;
220         cd->traces.size = 0;
221         cd->traces.traces = NULL;
222         cd->next = method_hash [slot];
223         method_hash [slot] = cd;
224         num_methods++;
225         return cd;
226 }
227
228 static MethodDesc *
229 lookup_method (intptr_t method)
230 {
231         int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
232         MethodDesc *cd = method_hash [slot];
233         while (cd && cd->method != method)
234                 cd = cd->next;
235         if (!cd) {
236                 char buf [128];
237                 snprintf (buf, sizeof (buf), "unknown method %p", (void*)method);
238                 return add_method (method, buf, 0, 0);
239         }
240         return cd;
241 }
242
243 typedef struct _HeapClassDesc HeapClassDesc;
244 typedef struct {
245         HeapClassDesc *klass;
246         uint64_t count;
247 } HeapClassRevRef;
248
249 struct _HeapClassDesc {
250         ClassDesc *klass;
251         int64_t count;
252         int64_t total_size;
253         HeapClassRevRef *rev_hash;
254         int rev_hash_size;
255         int rev_count;
256         uintptr_t pinned_references;
257         uintptr_t root_references;
258 };
259
260 static int
261 add_rev_class_hashed (HeapClassRevRef *rev_hash, uintptr_t size, HeapClassDesc *hklass, uint64_t value)
262 {
263         uintptr_t i;
264         uintptr_t start_pos;
265         start_pos = (hklass->klass->klass >> 2) % size;
266         assert (start_pos < size);
267         i = start_pos;
268         do {
269                 if (rev_hash [i].klass == hklass) {
270                         rev_hash [i].count += value;
271                         return 0;
272                 } else if (!rev_hash [i].klass) {
273                         rev_hash [i].klass = hklass;
274                         rev_hash [i].count += value;
275                         start_pos = 0;
276                         for (i = 0; i < size; ++i)
277                                 if (rev_hash [i].klass && rev_hash [i].klass->klass == hklass->klass)
278                                         start_pos ++;
279                         assert (start_pos == 1);
280                         return 1;
281                 }
282                 /* wrap around */
283                 if (++i == size)
284                         i = 0;
285         } while (i != start_pos);
286         /* should not happen */
287         printf ("failed revref store\n");
288         return 0;
289 }
290
291 static void
292 add_heap_class_rev (HeapClassDesc *from, HeapClassDesc *to)
293 {
294         uintptr_t i;
295         if (to->rev_count * 2 >= to->rev_hash_size) {
296                 HeapClassRevRef *n;
297                 uintptr_t old_size = to->rev_hash_size;
298                 to->rev_hash_size *= 2;
299                 if (to->rev_hash_size == 0)
300                         to->rev_hash_size = 4;
301                 n = calloc (sizeof (HeapClassRevRef) * to->rev_hash_size, 1);
302                 for (i = 0; i < old_size; ++i) {
303                         if (to->rev_hash [i].klass)
304                                 add_rev_class_hashed (n, to->rev_hash_size, to->rev_hash [i].klass, to->rev_hash [i].count);
305                 }
306                 if (to->rev_hash)
307                         free (to->rev_hash);
308                 to->rev_hash = n;
309         }
310         to->rev_count += add_rev_class_hashed (to->rev_hash, to->rev_hash_size, from, 1);
311 }
312
313 typedef struct {
314         uintptr_t objaddr;
315         HeapClassDesc *hklass;
316         uintptr_t num_refs;
317         uintptr_t refs [0];
318 } HeapObjectDesc;
319
320 typedef struct _HeapShot HeapShot;
321 struct _HeapShot {
322         HeapShot *next;
323         uint64_t timestamp;
324         int class_count;
325         int hash_size;
326         HeapClassDesc **class_hash;
327         HeapClassDesc **sorted;
328         HeapObjectDesc **objects_hash;
329         uintptr_t objects_count;
330         uintptr_t objects_hash_size;
331         uintptr_t num_roots;
332         uintptr_t *roots;
333         uintptr_t *roots_extra;
334         int *roots_types;
335 };
336
337 static HeapShot *heap_shots = NULL;
338 static int num_heap_shots = 0;
339
340 static HeapShot*
341 new_heap_shot (uint64_t timestamp)
342 {
343         HeapShot *hs = calloc (sizeof (HeapShot), 1);
344         hs->hash_size = 4;
345         hs->class_hash = calloc (sizeof (void*), hs->hash_size);
346         hs->timestamp = timestamp;
347         num_heap_shots++;
348         hs->next = heap_shots;
349         heap_shots = hs;
350         return hs;
351 }
352
353 static HeapClassDesc*
354 heap_class_lookup (HeapShot *hs, ClassDesc *klass)
355 {
356         int i;
357         unsigned int start_pos;
358         start_pos = ((uintptr_t)klass->klass >> 2) % hs->hash_size;
359         i = start_pos;
360         do {
361                 HeapClassDesc* cd = hs->class_hash [i];
362                 if (!cd)
363                         return NULL;
364                 if (cd->klass == klass)
365                         return cd;
366                 /* wrap around */
367                 if (++i == hs->hash_size)
368                         i = 0;
369         } while (i != start_pos);
370         return NULL;
371 }
372
373 static int
374 add_heap_hashed (HeapClassDesc **hash, HeapClassDesc **retv, uintptr_t hsize, ClassDesc *klass, uint64_t size, uint64_t count)
375 {
376         uintptr_t i;
377         uintptr_t start_pos;
378         start_pos = ((uintptr_t)klass->klass >> 2) % hsize;
379         i = start_pos;
380         do {
381                 if (hash [i] && hash [i]->klass == klass) {
382                         hash [i]->total_size += size;
383                         hash [i]->count += count;
384                         *retv = hash [i];
385                         return 0;
386                 } else if (!hash [i]) {
387                         if (*retv) {
388                                 hash [i] = *retv;
389                                 return 1;
390                         }
391                         hash [i] = calloc (sizeof (HeapClassDesc), 1);
392                         hash [i]->klass = klass;
393                         hash [i]->total_size += size;
394                         hash [i]->count += count;
395                         *retv = hash [i];
396                         return 1;
397                 }
398                 /* wrap around */
399                 if (++i == hsize)
400                         i = 0;
401         } while (i != start_pos);
402         /* should not happen */
403         printf ("failed heap class store\n");
404         return 0;
405 }
406
407 static HeapClassDesc*
408 add_heap_shot_class (HeapShot *hs, ClassDesc *klass, uint64_t size)
409 {
410         HeapClassDesc *res;
411         int i;
412         if (hs->class_count * 2 >= hs->hash_size) {
413                 HeapClassDesc **n;
414                 int old_size = hs->hash_size;
415                 hs->hash_size *= 2;
416                 if (hs->hash_size == 0)
417                         hs->hash_size = 4;
418                 n = calloc (sizeof (void*) * hs->hash_size, 1);
419                 for (i = 0; i < old_size; ++i) {
420                         res = hs->class_hash [i];
421                         if (hs->class_hash [i])
422                                 add_heap_hashed (n, &res, hs->hash_size, hs->class_hash [i]->klass, hs->class_hash [i]->total_size, hs->class_hash [i]->count);
423                 }
424                 if (hs->class_hash)
425                         free (hs->class_hash);
426                 hs->class_hash = n;
427         }
428         res = NULL;
429         hs->class_count += add_heap_hashed (hs->class_hash, &res, hs->hash_size, klass, size, 1);
430         //if (res->count == 1)
431         //      printf ("added heap class: %s\n", res->klass->name);
432         return res;
433 }
434
435 static HeapObjectDesc*
436 alloc_heap_obj (uintptr_t objaddr, HeapClassDesc *hklass, uintptr_t num_refs)
437 {
438         HeapObjectDesc* ho = calloc (sizeof (HeapObjectDesc) + num_refs * sizeof (uintptr_t), 1);
439         ho->objaddr = objaddr;
440         ho->hklass = hklass;
441         ho->num_refs = num_refs;
442         return ho;
443 }
444
445 static uintptr_t
446 heap_shot_find_obj_slot (HeapShot *hs, uintptr_t objaddr)
447 {
448         uintptr_t i;
449         uintptr_t start_pos;
450         HeapObjectDesc **hash = hs->objects_hash;
451         start_pos = ((uintptr_t)objaddr >> 3) % hs->objects_hash_size;
452         i = start_pos;
453         do {
454                 if (hash [i] && hash [i]->objaddr == objaddr) {
455                         return i;
456                 } else if (!hash [i]) {
457                         break; /* fail */
458                 }
459                 /* wrap around */
460                 if (++i == hs->objects_hash_size)
461                         i = 0;
462         } while (i != start_pos);
463         /* should not happen */
464         //printf ("failed heap obj slot\n");
465         return -1;
466 }
467
468 static HeapObjectDesc*
469 heap_shot_obj_add_refs (HeapShot *hs, uintptr_t objaddr, uintptr_t num, uintptr_t *ref_offset)
470 {
471         HeapObjectDesc **hash = hs->objects_hash;
472         uintptr_t i = heap_shot_find_obj_slot (hs, objaddr);
473         if (i >= 0) {
474                 HeapObjectDesc* ho = alloc_heap_obj (objaddr, hash [i]->hklass, hash [i]->num_refs + num);
475                 *ref_offset = hash [i]->num_refs;
476                 memcpy (ho->refs, hash [i]->refs, hash [i]->num_refs * sizeof (uintptr_t));
477                 free (hash [i]);
478                 hash [i] = ho;
479                 return ho;
480         }
481         /* should not happen */
482         printf ("failed heap obj update\n");
483         return NULL;
484         
485 }
486
487 static uintptr_t
488 add_heap_hashed_obj (HeapObjectDesc **hash, uintptr_t hsize, HeapObjectDesc *obj)
489 {
490         uintptr_t i;
491         uintptr_t start_pos;
492         start_pos = ((uintptr_t)obj->objaddr >> 3) % hsize;
493         i = start_pos;
494         do {
495                 if (hash [i] && hash [i]->objaddr == obj->objaddr) {
496                         printf ("duplicate object!\n");
497                         return 0;
498                 } else if (!hash [i]) {
499                         hash [i] = obj;
500                         return 1;
501                 }
502                 /* wrap around */
503                 if (++i == hsize)
504                         i = 0;
505         } while (i != start_pos);
506         /* should not happen */
507         printf ("failed heap obj store\n");
508         return 0;
509 }
510
511 static void
512 add_heap_shot_obj (HeapShot *hs, HeapObjectDesc *obj)
513 {
514         uintptr_t i;
515         if (hs->objects_count * 2 >= hs->objects_hash_size) {
516                 HeapObjectDesc **n;
517                 uintptr_t old_size = hs->objects_hash_size;
518                 hs->objects_hash_size *= 2;
519                 if (hs->objects_hash_size == 0)
520                         hs->objects_hash_size = 4;
521                 n = calloc (sizeof (void*) * hs->objects_hash_size, 1);
522                 for (i = 0; i < old_size; ++i) {
523                         if (hs->objects_hash [i])
524                                 add_heap_hashed_obj (n, hs->objects_hash_size, hs->objects_hash [i]);
525                 }
526                 if (hs->objects_hash)
527                         free (hs->objects_hash);
528                 hs->objects_hash = n;
529         }
530         hs->objects_count += add_heap_hashed_obj (hs->objects_hash, hs->objects_hash_size, obj);
531 }
532
533 static void
534 heap_shot_resolve_reverse_refs (HeapShot *hs)
535 {
536         uintptr_t i;
537         for (i = 0; i < hs->objects_hash_size; ++i) {
538                 uintptr_t r;
539                 HeapObjectDesc *ho = hs->objects_hash [i];
540                 if (!ho)
541                         continue;
542                 for (r = 0; r < ho->num_refs; ++r) {
543                         uintptr_t oi = heap_shot_find_obj_slot (hs, ho->refs [r]);
544                         add_heap_class_rev (ho->hklass, hs->objects_hash [oi]->hklass);
545                 }
546         }
547 }
548
549 #define MARK_GRAY 1
550 #define MARK_BLACK 2
551
552 static void
553 heap_shot_mark_objects (HeapShot *hs)
554 {
555         uintptr_t i, oi, r;
556         unsigned char *marks;
557         HeapObjectDesc *obj, *ref;
558         int marked_some;
559         uintptr_t num_marked = 0, num_unmarked;
560         for (i = 0; i < hs->num_roots; ++i) {
561                 HeapClassDesc *cd;
562                 oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
563                 if (oi == -1) {
564                         continue;
565                 }
566                 obj = hs->objects_hash [oi];
567                 cd = obj->hklass;
568                 if (hs->roots_types [i] & MONO_PROFILE_GC_ROOT_PINNING)
569                         cd->pinned_references++;
570                 cd->root_references++;
571         }
572         if (!debug)
573                 return;
574         /* consistency checks: it seems not all the objects are walked in the heap in some cases */
575         marks = calloc (hs->objects_hash_size, 1);
576         if (!marks)
577                 return;
578         for (i = 0; i < hs->num_roots; ++i) {
579                 oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
580                 if (oi == -1) {
581                         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);
582                         continue;
583                 }
584                 obj = hs->objects_hash [oi];
585                 if (!marks [oi]) {
586                         marks [oi] = obj->num_refs? MARK_GRAY: MARK_BLACK;
587                         num_marked++;
588                 }
589         }
590         marked_some = 1;
591         while (marked_some) {
592                 marked_some = 0;
593                 for (i = 0; i < hs->objects_hash_size; ++i) {
594                         if (marks [i] != MARK_GRAY)
595                                 continue;
596                         marks [i] = MARK_BLACK;
597                         obj = hs->objects_hash [i];
598                         for (r = 0; r < obj->num_refs; ++r) {
599                                 oi = heap_shot_find_obj_slot (hs, obj->refs [r]);
600                                 if (oi == -1) {
601                                         fprintf (outfile, "referenced obj %p not found in heap\n", (void*)obj->refs [r]);
602                                         continue;
603                                 }
604                                 ref = hs->objects_hash [oi];
605                                 if (!marks [oi]) {
606                                         marks [oi] = ref->num_refs? MARK_GRAY: MARK_BLACK;
607                                 }
608                         }
609                         marked_some++;
610                 }
611         }
612
613         num_unmarked = 0;
614         for (i = 0; i < hs->objects_hash_size; ++i) {
615                 if (hs->objects_hash [i] && !marks [i]) {
616                         num_unmarked++;
617                         fprintf (outfile, "object %p (%s) unmarked\n", (void*)hs->objects_hash [i], hs->objects_hash [i]->hklass->klass->name);
618                 }
619         }
620         fprintf (outfile, "Total unmarked: %d/%d\n", num_unmarked, hs->objects_count);
621         free (marks);
622 }
623
624 static void
625 heap_shot_free_objects (HeapShot *hs)
626 {
627         uintptr_t i;
628         for (i = 0; i < hs->objects_hash_size; ++i) {
629                 HeapObjectDesc *ho = hs->objects_hash [i];
630                 if (ho)
631                         free (ho);
632         }
633         if (hs->objects_hash)
634                 free (hs->objects_hash);
635         hs->objects_hash = NULL;
636         hs->objects_hash_size = 0;
637         hs->objects_count = 0;
638 }
639
640 struct _BackTrace {
641         BackTrace *next;
642         unsigned int hash;
643         int count;
644         int id;
645         MethodDesc *methods [1];
646 };
647
648 static BackTrace *backtrace_hash [HASH_SIZE];
649 static BackTrace **backtraces = NULL;
650 static int num_backtraces = 0;
651 static int next_backtrace = 0;
652
653 static int
654 hash_backtrace (int count, MethodDesc **methods)
655 {
656         int hash = count;
657         int i;
658         for (i = 0; i < count; ++i) {
659                 hash = (hash << 5) - hash + methods [i]->method;
660         }
661         return hash;
662 }
663
664 static int
665 compare_backtrace (BackTrace *bt, int count, MethodDesc **methods)
666 {
667         int i;
668         if (bt->count != count)
669                 return 0;
670         for (i = 0; i < count; ++i)
671                 if (methods [i] != bt->methods [i])
672                         return 0;
673         return 1;
674 }
675
676 static BackTrace*
677 add_backtrace (int count, MethodDesc **methods)
678 {
679         int hash = hash_backtrace (count, methods);
680         int slot = (hash & 0xffff) % HASH_SIZE;
681         BackTrace *bt = backtrace_hash [slot];
682         while (bt) {
683                 if (bt->hash == hash && compare_backtrace (bt, count, methods))
684                         return bt;
685                 bt = bt->next;
686         }
687         bt = malloc (sizeof (BackTrace) + ((count - 1) * sizeof (void*)));
688         bt->next = backtrace_hash [slot];
689         backtrace_hash [slot] = bt;
690         if (next_backtrace == num_backtraces) {
691                 num_backtraces *= 2;
692                 if (!num_backtraces)
693                         num_backtraces = 16;
694                 backtraces = realloc (backtraces, sizeof (void*) * num_backtraces);
695         }
696         bt->id = next_backtrace++;
697         backtraces [bt->id] = bt;
698         bt->count = count;
699         bt->hash = hash;
700         for (slot = 0; slot < count; ++slot)
701                 bt->methods [slot] = methods [slot];
702
703         return bt;
704 }
705
706 typedef struct _MonitorDesc MonitorDesc;
707 typedef struct _ThreadContext ThreadContext;
708
709 typedef struct {
710         FILE *file;
711 #if defined (HAVE_SYS_ZLIB)
712         gzFile *gzfile;
713 #endif
714         unsigned char *buf;
715         int size;
716         int data_version;
717         int version_major;
718         int version_minor;
719         int timer_overhead;
720         int pid;
721         int port;
722         uint64_t startup_time;
723         ThreadContext *threads;
724         ThreadContext *current;
725 } ProfContext;
726
727 struct _ThreadContext {
728         ThreadContext *next;
729         intptr_t thread_id;
730         char *name;
731         /* emulated stack */
732         MethodDesc **stack;
733         uint64_t *time_stack;
734         uint64_t *callee_time_stack;
735         uint64_t last_time;
736         uint64_t contention_start;
737         MonitorDesc *monitor;
738         int stack_size;
739         int stack_id;
740         HeapShot *current_heap_shot;
741         uintptr_t num_roots;
742         uintptr_t size_roots;
743         uintptr_t *roots;
744         uintptr_t *roots_extra;
745         int *roots_types;
746         uint64_t gc_start_times [3];
747 };
748
749 static void
750 ensure_buffer (ProfContext *ctx, int size)
751 {
752         if (ctx->size < size) {
753                 ctx->buf = realloc (ctx->buf, size);
754                 ctx->size = size;
755         }
756 }
757
758 static int
759 load_data (ProfContext *ctx, int size)
760 {
761         ensure_buffer (ctx, size);
762 #if defined (HAVE_SYS_ZLIB)
763         if (ctx->gzfile) {
764                 int r = gzread (ctx->gzfile, ctx->buf, size);
765                 if (r == 0)
766                         return size == 0? 1: 0;
767                 return r == size;
768         } else 
769 #endif
770         {
771                 int r = fread (ctx->buf, size, 1, ctx->file);
772                 if (r == 0)
773                         return size == 0? 1: 0;
774                 return r;
775         }
776 }
777
778 static ThreadContext*
779 get_thread (ProfContext *ctx, intptr_t thread_id)
780 {
781         ThreadContext *thread;
782         if (ctx->current && ctx->current->thread_id == thread_id)
783                 return ctx->current;
784         thread = ctx->threads;
785         while (thread) {
786                 if (thread->thread_id == thread_id) {
787                         return thread;
788                 }
789                 thread = thread->next;
790         }
791         thread = calloc (sizeof (ThreadContext), 1);
792         thread->next = ctx->threads;
793         ctx->threads = thread;
794         thread->thread_id = thread_id;
795         thread->last_time = 0;
796         thread->stack_id = 0;
797         thread->stack_size = 32;
798         thread->stack = malloc (thread->stack_size * sizeof (void*));
799         thread->time_stack = malloc (thread->stack_size * sizeof (uint64_t));
800         thread->callee_time_stack = malloc (thread->stack_size * sizeof (uint64_t));
801         return thread;
802 }
803
804 static ThreadContext*
805 load_thread (ProfContext *ctx, intptr_t thread_id)
806 {
807         ThreadContext *thread = get_thread (ctx, thread_id);
808         ctx->current = thread;
809         return thread;
810 }
811
812 static void
813 ensure_thread_stack (ThreadContext *thread)
814 {
815         if (thread->stack_id == thread->stack_size) {
816                 thread->stack_size *= 2;
817                 thread->stack = realloc (thread->stack, thread->stack_size * sizeof (void*));
818                 thread->time_stack = realloc (thread->time_stack, thread->stack_size * sizeof (uint64_t));
819                 thread->callee_time_stack = realloc (thread->callee_time_stack, thread->stack_size * sizeof (uint64_t));
820         }
821 }
822
823 static int
824 add_trace_hashed (CallContext *traces, int size, BackTrace *bt, uint64_t value)
825 {
826         int i;
827         unsigned int start_pos;
828         start_pos = bt->hash % size;
829         i = start_pos;
830         do {
831                 if (traces [i].bt == bt) {
832                         traces [i].count += value;
833                         return 0;
834                 } else if (!traces [i].bt) {
835                         traces [i].bt = bt;
836                         traces [i].count += value;
837                         return 1;
838                 }
839                 /* wrap around */
840                 if (++i == size)
841                         i = 0;
842         } while (i != start_pos);
843         /* should not happen */
844         printf ("failed trace store\n");
845         return 0;
846 }
847
848 static void
849 add_trace_bt (BackTrace *bt, TraceDesc *trace, uint64_t value)
850 {
851         int i;
852         if (!collect_traces)
853                 return;
854         if (trace->count * 2 >= trace->size) {
855                 CallContext *n;
856                 int old_size = trace->size;
857                 trace->size *= 2;
858                 if (trace->size == 0)
859                         trace->size = 4;
860                 n = calloc (sizeof (CallContext) * trace->size, 1);
861                 for (i = 0; i < old_size; ++i) {
862                         if (trace->traces [i].bt)
863                                 add_trace_hashed (n, trace->size, trace->traces [i].bt, trace->traces [i].count);
864                 }
865                 if (trace->traces)
866                         free (trace->traces);
867                 trace->traces = n;
868         }
869         trace->count += add_trace_hashed (trace->traces, trace->size, bt, value);
870 }
871
872 static BackTrace*
873 add_trace_thread (ThreadContext *thread, TraceDesc *trace, uint64_t value)
874 {
875         BackTrace *bt;
876         int count = thread->stack_id;
877         if (!collect_traces)
878                 return NULL;
879         if (count > trace_max)
880                 count = trace_max;
881         bt = add_backtrace (count, thread->stack + thread->stack_id - count);
882         add_trace_bt (bt, trace, value);
883         return bt;
884 }
885
886 static BackTrace*
887 add_trace_methods (MethodDesc **methods, int count, TraceDesc *trace, uint64_t value)
888 {
889         BackTrace *bt;
890         if (!collect_traces)
891                 return NULL;
892         if (count > trace_max)
893                 count = trace_max;
894         bt = add_backtrace (count, methods);
895         add_trace_bt (bt, trace, value);
896         return bt;
897 }
898
899 static void
900 thread_add_root (ThreadContext *ctx, uintptr_t obj, int root_type, uintptr_t extra_info)
901 {
902         if (ctx->num_roots == ctx->size_roots) {
903                 int new_size = ctx->size_roots * 2;
904                 if (!new_size)
905                         new_size = 4;
906                 ctx->roots = realloc (ctx->roots, new_size * sizeof (uintptr_t));
907                 ctx->roots_extra = realloc (ctx->roots_extra, new_size * sizeof (uintptr_t));
908                 ctx->roots_types = realloc (ctx->roots_types, new_size * sizeof (int));
909                 ctx->size_roots = new_size;
910         }
911         ctx->roots_types [ctx->num_roots] = root_type;
912         ctx->roots_extra [ctx->num_roots] = extra_info;
913         ctx->roots [ctx->num_roots++] = obj;
914 }
915
916 static int
917 compare_callc (const void *a, const void *b)
918 {
919         const CallContext *A = a;
920         const CallContext *B = b;
921         if (B->count == A->count)
922                 return 0;
923         if (B->count < A->count)
924                 return -1;
925         return 1;
926 }
927
928 static void
929 sort_context_array (TraceDesc* traces)
930 {
931         int i, j;
932         for (i = 0, j = 0; i < traces->size; ++i) {
933                 if (traces->traces [i].bt) {
934                         traces->traces [j].bt = traces->traces [i].bt;
935                         traces->traces [j].count = traces->traces [i].count;
936                         j++;
937                 }
938         }
939         qsort (traces->traces, traces->count, sizeof (CallContext), compare_callc);
940 }
941
942 static void
943 push_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
944 {
945         ensure_thread_stack (thread);
946         thread->time_stack [thread->stack_id] = timestamp;
947         thread->callee_time_stack [thread->stack_id] = 0;
948         thread->stack [thread->stack_id++] = method;
949         method->recurse_count++;
950 }
951
952 static void
953 pop_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
954 {
955         method->recurse_count--;
956         if (thread->stack_id > 0 && thread->stack [thread->stack_id - 1] == method) {
957                 uint64_t tdiff;
958                 thread->stack_id--;
959                 method->calls++;
960                 if (timestamp < thread->time_stack [thread->stack_id])
961                         fprintf (outfile, "time went backwards for %s\n", method->name);
962                 tdiff = timestamp - thread->time_stack [thread->stack_id];
963                 if (thread->callee_time_stack [thread->stack_id] > tdiff)
964                         fprintf (outfile, "callee time bigger for %s\n", method->name);
965                 method->self_time += tdiff - thread->callee_time_stack [thread->stack_id];
966                 method->callee_time += thread->callee_time_stack [thread->stack_id];
967                 if (thread->stack_id)
968                         thread->callee_time_stack [thread->stack_id - 1] += tdiff;
969                 //fprintf (outfile, "method %s took %d\n", method->name, (int)(tdiff/1000));
970         } else {
971                 fprintf (outfile, "unmatched leave at stack pos: %d for method %s\n", thread->stack_id, method->name);
972         }
973 }
974
975 typedef struct {
976         uint64_t total_time;
977         uint64_t max_time;
978         int count;
979 } GCDesc;
980 static GCDesc gc_info [3];
981 static uint64_t max_heap_size;
982 static uint64_t gc_object_moves;
983 static int gc_resizes;
984 typedef struct {
985         uint64_t created;
986         uint64_t destroyed;
987         uint64_t max_live;
988         TraceDesc traces;
989 } HandleInfo;
990 static HandleInfo handle_info [4];
991
992 static const char*
993 gc_event_name (int ev)
994 {
995         switch (ev) {
996         case MONO_GC_EVENT_START: return "start";
997         case MONO_GC_EVENT_MARK_START: return "mark start";
998         case MONO_GC_EVENT_MARK_END: return "mark end";
999         case MONO_GC_EVENT_RECLAIM_START: return "reclaim start";
1000         case MONO_GC_EVENT_RECLAIM_END: return "reclaim end";
1001         case MONO_GC_EVENT_END: return "end";
1002         case MONO_GC_EVENT_PRE_STOP_WORLD: return "pre stop";
1003         case MONO_GC_EVENT_POST_STOP_WORLD: return "post stop";
1004         case MONO_GC_EVENT_PRE_START_WORLD: return "pre start";
1005         case MONO_GC_EVENT_POST_START_WORLD: return "post start";
1006         default:
1007                 return "unknown";
1008         }
1009 }
1010
1011 static uint64_t clause_summary [MONO_EXCEPTION_CLAUSE_FAULT + 1];
1012 static uint64_t throw_count = 0;
1013 static TraceDesc exc_traces;
1014
1015 static const char*
1016 clause_name (int type)
1017 {
1018         switch (type) {
1019         case MONO_EXCEPTION_CLAUSE_NONE: return "catch";
1020         case MONO_EXCEPTION_CLAUSE_FILTER: return "filter";
1021         case MONO_EXCEPTION_CLAUSE_FINALLY: return "finally";
1022         case MONO_EXCEPTION_CLAUSE_FAULT: return "fault";
1023         default: return "invalid";
1024         }
1025 }
1026
1027 static uint64_t monitor_contention;
1028 static uint64_t monitor_failed;
1029 static uint64_t monitor_acquired;
1030
1031 struct _MonitorDesc {
1032         MonitorDesc *next;
1033         uintptr_t objid;
1034         uintptr_t contentions;
1035         uint64_t wait_time;
1036         uint64_t max_wait_time;
1037         TraceDesc traces;
1038 };
1039
1040 static MonitorDesc* monitor_hash [SMALL_HASH_SIZE] = {0};
1041 static int num_monitors = 0;
1042
1043 static MonitorDesc*
1044 lookup_monitor (uintptr_t objid)
1045 {
1046         int slot = ((objid >> 3) & 0xffff) % SMALL_HASH_SIZE;
1047         MonitorDesc *cd = monitor_hash [slot];
1048         while (cd && cd->objid != objid)
1049                 cd = cd->next;
1050         if (!cd) {
1051                 cd = calloc (sizeof (MonitorDesc), 1);
1052                 cd->objid = objid;
1053                 cd->next = monitor_hash [slot];
1054                 monitor_hash [slot] = cd;
1055                 num_monitors++;
1056         }
1057         return cd;
1058 }
1059
1060 static const char*
1061 monitor_ev_name (int ev)
1062 {
1063         switch (ev) {
1064         case MONO_PROFILER_MONITOR_CONTENTION: return "contended";
1065         case MONO_PROFILER_MONITOR_DONE: return "acquired";
1066         case MONO_PROFILER_MONITOR_FAIL: return "not taken";
1067         default: return "invalid";
1068         }
1069 }
1070
1071 static const char*
1072 get_handle_name (int htype)
1073 {
1074         switch (htype) {
1075         case 0: return "weak";
1076         case 1: return "weaktrack";
1077         case 2: return "normal";
1078         case 3: return "pinned";
1079         default: return "unknown";
1080         }
1081 }
1082
1083 static const char*
1084 get_root_name (int rtype)
1085 {
1086         switch (rtype & MONO_PROFILE_GC_ROOT_TYPEMASK) {
1087         case MONO_PROFILE_GC_ROOT_STACK: return "stack";
1088         case MONO_PROFILE_GC_ROOT_FINALIZER: return "finalizer";
1089         case MONO_PROFILE_GC_ROOT_HANDLE: return "handle";
1090         case MONO_PROFILE_GC_ROOT_OTHER: return "other";
1091         case MONO_PROFILE_GC_ROOT_MISC: return "misc";
1092         default: return "unknown";
1093         }
1094 }
1095
1096 static MethodDesc**
1097 decode_bt (MethodDesc** sframes, int *size, unsigned char *p, unsigned char **endp, intptr_t ptr_base)
1098 {
1099         MethodDesc **frames;
1100         int i;
1101         int flags = decode_uleb128 (p, &p);
1102         int count = decode_uleb128 (p, &p);
1103         if (flags != 0)
1104                 return NULL;
1105         if (count > *size)
1106                 frames = malloc (count * sizeof (void*));
1107         else
1108                 frames = sframes;
1109         for (i = 0; i < count; ++i) {
1110                 intptr_t ptrdiff = decode_sleb128 (p, &p);
1111                 frames [i] = lookup_method (ptr_base + ptrdiff);
1112         }
1113         *size = count;
1114         *endp = p;
1115         return frames;
1116 }
1117
1118 static void
1119 tracked_creation (uintptr_t obj, ClassDesc *cd, uint64_t size, BackTrace *bt, uint64_t timestamp)
1120 {
1121         int i;
1122         for (i = 0; i < num_tracked_objects; ++i) {
1123                 if (tracked_objects [i] != obj)
1124                         continue;
1125                 fprintf (outfile, "Object %p created (%s, %llu bytes) at %.3f secs.\n", (void*)obj, cd->name, size, (timestamp - startup_time)/1000000000.0);
1126                 if (bt && bt->count) {
1127                         int k;
1128                         for (k = 0; k < bt->count; ++k)
1129                                 fprintf (outfile, "\t%s\n", bt->methods [k]->name);
1130                 }
1131         }
1132 }
1133
1134 static void
1135 track_handle (uintptr_t obj, int htype, uint32_t handle)
1136 {
1137         int i;
1138         for (i = 0; i < num_tracked_objects; ++i) {
1139                 if (tracked_objects [i] == obj)
1140                         fprintf (outfile, "Object %p referenced from handle %u\n", (void*)obj, handle);
1141         }
1142 }
1143
1144 static void
1145 track_move (uintptr_t src, uintptr_t dst)
1146 {
1147         int i;
1148         for (i = 0; i < num_tracked_objects; ++i) {
1149                 if (tracked_objects [i] == src)
1150                         fprintf (outfile, "Object %p moved to %p\n", (void*)src, (void*)dst);
1151                 else if (tracked_objects [i] == dst)
1152                         fprintf (outfile, "Object %p moved from %p\n", (void*)dst, (void*)src);
1153         }
1154 }
1155
1156 static void
1157 track_obj_reference (uintptr_t obj, uintptr_t parent, ClassDesc *cd)
1158 {
1159         int i;
1160         for (i = 0; i < num_tracked_objects; ++i) {
1161                 if (tracked_objects [i] == obj) 
1162                         fprintf (outfile, "Object %p referenced from %p (%s).\n", (void*)obj, (void*)parent, cd->name);
1163         }
1164 }
1165
1166 static void
1167 found_object (uintptr_t obj)
1168 {
1169         num_tracked_objects ++;
1170         tracked_objects = realloc (tracked_objects, num_tracked_objects * sizeof (tracked_objects [0]));
1171         tracked_objects [num_tracked_objects - 1] = obj;
1172 }
1173
1174 #define OBJ_ADDR(diff) ((obj_base + diff) << 3)
1175 #define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
1176
1177 static int
1178 decode_buffer (ProfContext *ctx)
1179 {
1180         unsigned char *p;
1181         unsigned char *end;
1182         intptr_t thread_id;
1183         intptr_t ptr_base;
1184         intptr_t obj_base;
1185         intptr_t method_base;
1186         uint64_t time_base;
1187         uint64_t file_offset;
1188         int len, i;
1189         ThreadContext *thread;
1190
1191 #ifdef HAVE_SYS_ZLIB
1192         if (ctx->gzfile)
1193                 file_offset = gztell (ctx->gzfile);
1194         else
1195 #endif
1196                 file_offset = ftell (ctx->file);
1197         if (!load_data (ctx, 48))
1198                 return 0;
1199         p = ctx->buf;
1200         if (read_int32 (p) != BUF_ID) {
1201                 fprintf (outfile, "Incorrect buffer id: 0x%x\n", read_int32 (p));
1202                 for (i = 0; i < 48; ++i) {
1203                         fprintf (outfile, "0x%x%s", p [i], i % 8?" ":"\n");
1204                 }
1205                 return 0;
1206         }
1207         len = read_int32 (p + 4);
1208         time_base = read_int64 (p + 8);
1209         ptr_base = read_int64 (p + 16);
1210         obj_base = read_int64 (p + 24);
1211         thread_id = read_int64 (p + 32);
1212         method_base = read_int64 (p + 40);
1213         if (debug)
1214                 fprintf (outfile, "buf: thread:%x, len: %d, time: %llu, file offset: %llu\n", thread_id, len, time_base, file_offset);
1215         thread = load_thread (ctx, thread_id);
1216         if (!load_data (ctx, len))
1217                 return 0;
1218         if (!startup_time) {
1219                 startup_time = time_base;
1220                 if (time_from) {
1221                         time_from += startup_time;
1222                         time_to += startup_time;
1223                 }
1224                 if (!thread->name)
1225                         thread->name = pstrdup ("Main");
1226         }
1227         for (i = 0; i < thread->stack_id; ++i)
1228                 thread->stack [i]->recurse_count++;
1229         p = ctx->buf;
1230         end = p + len;
1231         while (p < end) {
1232                 switch (*p & 0xf) {
1233                 case TYPE_GC: {
1234                         int subtype = *p & 0xf0;
1235                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
1236                         LOG_TIME (time_base, tdiff);
1237                         time_base += tdiff;
1238                         if (subtype == TYPE_GC_RESIZE) {
1239                                 uint64_t new_size = decode_uleb128 (p, &p);
1240                                 if (debug)
1241                                         fprintf (outfile, "gc heap resized to %llu\n", new_size);
1242                                 gc_resizes++;
1243                                 if (new_size > max_heap_size)
1244                                         max_heap_size = new_size;
1245                         } else if (subtype == TYPE_GC_EVENT) {
1246                                 uint64_t ev = decode_uleb128 (p, &p);
1247                                 int gen = decode_uleb128 (p, &p);
1248                                 if (debug)
1249                                         fprintf (outfile, "gc event for gen%d: %s at %llu (thread: 0x%x)\n", gen, gc_event_name (ev), time_base, thread->thread_id);
1250                                 if (gen > 2) {
1251                                         fprintf (outfile, "incorrect gc gen: %d\n", gen);
1252                                         break;
1253                                 }
1254                                 if (ev == MONO_GC_EVENT_START) {
1255                                         thread->gc_start_times [gen] = time_base;
1256                                         gc_info [gen].count++;
1257                                 } else if (ev == MONO_GC_EVENT_END) {
1258                                         tdiff = time_base - thread->gc_start_times [gen];
1259                                         gc_info [gen].total_time += tdiff;
1260                                         if (tdiff > gc_info [gen].max_time)
1261                                                 gc_info [gen].max_time = tdiff;
1262                                 }
1263                         } else if (subtype == TYPE_GC_MOVE) {
1264                                 int j, num = decode_uleb128 (p, &p);
1265                                 gc_object_moves += num / 2;
1266                                 for (j = 0; j < num; j += 2) {
1267                                         intptr_t obj1diff = decode_sleb128 (p, &p);
1268                                         intptr_t obj2diff = decode_sleb128 (p, &p);
1269                                         if (num_tracked_objects)
1270                                                 track_move (OBJ_ADDR (obj1diff), OBJ_ADDR (obj2diff));
1271                                         if (debug) {
1272                                                 fprintf (outfile, "moved obj %p to %p\n", (void*)OBJ_ADDR (obj1diff), (void*)OBJ_ADDR (obj2diff));
1273                                         }
1274                                 }
1275                         } else if (subtype == TYPE_GC_HANDLE_CREATED) {
1276                                 int htype = decode_uleb128 (p, &p);
1277                                 uint32_t handle = decode_uleb128 (p, &p);
1278                                 intptr_t objdiff = decode_sleb128 (p, &p);
1279                                 if (htype > 3)
1280                                         return 0;
1281                                 handle_info [htype].created++;
1282                                 add_trace_thread (thread, &handle_info [htype].traces, 1);
1283                                 /* FIXME: we don't take into account timing here */
1284                                 if (handle_info [htype].created > handle_info [htype].max_live)
1285                                         handle_info [htype].max_live = handle_info [htype].created;
1286                                 if (num_tracked_objects)
1287                                         track_handle (OBJ_ADDR (objdiff), htype, handle);
1288                                 if (debug)
1289                                         fprintf (outfile, "handle (%s) %u created for object %p\n", get_handle_name (htype), handle, (void*)OBJ_ADDR (objdiff));
1290                         } else if (subtype == TYPE_GC_HANDLE_DESTROYED) {
1291                                 int htype = decode_uleb128 (p, &p);
1292                                 uint32_t handle = decode_uleb128 (p, &p);
1293                                 if (htype > 3)
1294                                         return 0;
1295                                 handle_info [htype].created--;
1296                                 if (debug)
1297                                         fprintf (outfile, "handle (%s) %u destroyed\n", get_handle_name (htype), handle);
1298                         }
1299                         break;
1300                 }
1301                 case TYPE_METADATA: {
1302                         int error = *p & TYPE_LOAD_ERR;
1303                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
1304                         int mtype = *p++;
1305                         intptr_t ptrdiff = decode_sleb128 (p, &p);
1306                         LOG_TIME (time_base, tdiff);
1307                         time_base += tdiff;
1308                         if (mtype == TYPE_CLASS) {
1309                                 intptr_t imptrdiff = decode_sleb128 (p, &p);
1310                                 uint64_t flags = decode_uleb128 (p, &p);
1311                                 if (flags) {
1312                                         fprintf (outfile, "non-zero flags in class\n");
1313                                         return 0;
1314                                 }
1315                                 if (debug)
1316                                         fprintf (outfile, "loaded class %p (%s in %p) at %llu\n", (void*)(ptr_base + ptrdiff), p, (void*)(ptr_base + imptrdiff), time_base);
1317                                 if (!error)
1318                                         add_class (ptr_base + ptrdiff, (char*)p);
1319                                 while (*p) p++;
1320                                 p++;
1321                         } else if (mtype == TYPE_IMAGE) {
1322                                 uint64_t flags = decode_uleb128 (p, &p);
1323                                 if (flags) {
1324                                         fprintf (outfile, "non-zero flags in image\n");
1325                                         return 0;
1326                                 }
1327                                 if (debug)
1328                                         fprintf (outfile, "loaded image %p (%s) at %llu\n", (void*)(ptr_base + ptrdiff), p, time_base);
1329                                 if (!error)
1330                                         add_image (ptr_base + ptrdiff, (char*)p);
1331                                 while (*p) p++;
1332                                 p++;
1333                         } else if (mtype == TYPE_THREAD) {
1334                                 ThreadContext *nt;
1335                                 uint64_t flags = decode_uleb128 (p, &p);
1336                                 if (flags) {
1337                                         fprintf (outfile, "non-zero flags in thread\n");
1338                                         return 0;
1339                                 }
1340                                 nt = get_thread (ctx, ptr_base * ptrdiff);
1341                                 nt->name = pstrdup ((char*)p);
1342                                 if (debug)
1343                                         fprintf (outfile, "thread %p named: %s\n", (void*)(ptr_base + ptrdiff), p);
1344                                 while (*p) p++;
1345                                 p++;
1346                         }
1347                         break;
1348                 }
1349                 case TYPE_ALLOC: {
1350                         int has_bt = *p & TYPE_ALLOC_BT;
1351                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
1352                         intptr_t ptrdiff = decode_sleb128 (p, &p);
1353                         intptr_t objdiff = decode_sleb128 (p, &p);
1354                         uint64_t len;
1355                         int num_bt = 0;
1356                         MethodDesc* sframes [8];
1357                         MethodDesc** frames = sframes;
1358                         ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
1359                         len = decode_uleb128 (p, &p);
1360                         LOG_TIME (time_base, tdiff);
1361                         time_base += tdiff;
1362                         if (debug)
1363                                 fprintf (outfile, "alloced object %p, size %llu (%s) at %llu\n", (void*)OBJ_ADDR (objdiff), len, lookup_class (ptr_base + ptrdiff)->name, time_base);
1364                         if (has_bt) {
1365                                 num_bt = 8;
1366                                 frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
1367                                 if (!frames) {
1368                                         fprintf (outfile, "Cannot load backtrace\n");
1369                                         return 0;
1370                                 }
1371                         }
1372                         if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
1373                                 BackTrace *bt;
1374                                 cd->allocs++;
1375                                 cd->alloc_size += len;
1376                                 if (has_bt)
1377                                         bt = add_trace_methods (frames, num_bt, &cd->traces, len);
1378                                 else
1379                                         bt = add_trace_thread (thread, &cd->traces, len);
1380                                 if (find_size && len >= find_size) {
1381                                         if (!find_name || strstr (cd->name, find_name))
1382                                                 found_object (OBJ_ADDR (objdiff));
1383                                 } else if (!find_size && find_name && strstr (cd->name, find_name)) {
1384                                         found_object (OBJ_ADDR (objdiff));
1385                                 }
1386                                 if (num_tracked_objects)
1387                                         tracked_creation (OBJ_ADDR (objdiff), cd, len, bt, time_base);
1388                         }
1389                         if (frames != sframes)
1390                                 free (frames);
1391                         break;
1392                 }
1393                 case TYPE_METHOD: {
1394                         int subtype = *p & 0xf0;
1395                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
1396                         int64_t ptrdiff = decode_sleb128 (p, &p);
1397                         LOG_TIME (time_base, tdiff);
1398                         time_base += tdiff;
1399                         method_base += ptrdiff;
1400                         if (subtype == TYPE_JIT) {
1401                                 intptr_t codediff = decode_sleb128 (p, &p);
1402                                 int codelen = decode_uleb128 (p, &p);
1403                                 if (debug)
1404                                         fprintf (outfile, "jitted method %p (%s), size: %d\n", (void*)(method_base), p, codelen);
1405                                 add_method (method_base, (char*)p, ptr_base + codediff, codelen);
1406                                 while (*p) p++;
1407                                 p++;
1408                         } else {
1409                                 MethodDesc *method;
1410                                 if ((thread_filter && thread_filter != thread->thread_id))
1411                                         break;
1412                                 method = lookup_method (method_base);
1413                                 if (subtype == TYPE_ENTER) {
1414                                         add_trace_thread (thread, &method->traces, 1);
1415                                         push_method (thread, method, time_base);
1416                                 } else {
1417                                         pop_method (thread, method, time_base);
1418                                 }
1419                                 if (debug)
1420                                         fprintf (outfile, "%s method %s\n", subtype == TYPE_ENTER? "enter": subtype == TYPE_EXC_LEAVE? "exleave": "leave", method->name);
1421                         }
1422                         break;
1423                 }
1424                 case TYPE_HEAP: {
1425                         int subtype = *p & 0xf0;
1426                         if (subtype == TYPE_HEAP_OBJECT) {
1427                                 HeapObjectDesc *ho;
1428                                 int i;
1429                                 intptr_t objdiff = decode_sleb128 (p + 1, &p);
1430                                 intptr_t ptrdiff = decode_sleb128 (p, &p);
1431                                 uint64_t size = decode_uleb128 (p, &p);
1432                                 uintptr_t num = decode_uleb128 (p, &p);
1433                                 uintptr_t ref_offset;
1434                                 uintptr_t last_obj_offset = 0;
1435                                 ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
1436                                 if (size) {
1437                                         HeapClassDesc *hcd = add_heap_shot_class (thread->current_heap_shot, cd, size);
1438                                         if (collect_traces) {
1439                                                 ho = alloc_heap_obj (OBJ_ADDR (objdiff), hcd, num);
1440                                                 add_heap_shot_obj (thread->current_heap_shot, ho);
1441                                                 ref_offset = 0;
1442                                         }
1443                                 } else {
1444                                         if (collect_traces)
1445                                                 ho = heap_shot_obj_add_refs (thread->current_heap_shot, OBJ_ADDR (objdiff), num, &ref_offset);
1446                                 }
1447                                 for (i = 0; i < num; ++i) {
1448                                         /* FIXME: use object distance to measure how good
1449                                          * the GC is at keeping related objects close
1450                                          */
1451                                         uintptr_t offset = ctx->data_version > 1? last_obj_offset + decode_uleb128 (p, &p): -1;
1452                                         intptr_t obj1diff = decode_sleb128 (p, &p);
1453                                         last_obj_offset = offset;
1454                                         if (collect_traces)
1455                                                 ho->refs [ref_offset + i] = OBJ_ADDR (obj1diff);
1456                                         if (num_tracked_objects)
1457                                                 track_obj_reference (OBJ_ADDR (obj1diff), OBJ_ADDR (objdiff), cd);
1458                                 }
1459                                 if (debug && size)
1460                                         fprintf (outfile, "traced object %p, size %llu (%s), refs: %d\n", (void*)OBJ_ADDR (objdiff), size, cd->name, num);
1461                         } else if (subtype == TYPE_HEAP_ROOT) {
1462                                 uintptr_t num = decode_uleb128 (p + 1, &p);
1463                                 uintptr_t gc_num = decode_uleb128 (p, &p);
1464                                 int i;
1465                                 for (i = 0; i < num; ++i) {
1466                                         intptr_t objdiff = decode_sleb128 (p, &p);
1467                                         int root_type = decode_uleb128 (p, &p);
1468                                         /* we just discard the extra info for now */
1469                                         uintptr_t extra_info = decode_uleb128 (p, &p);
1470                                         if (debug)
1471                                                 fprintf (outfile, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff), get_root_name (root_type));
1472                                         if (collect_traces)
1473                                                 thread_add_root (thread, OBJ_ADDR (objdiff), root_type, extra_info);
1474                                 }
1475                         } else if (subtype == TYPE_HEAP_END) {
1476                                 uint64_t tdiff = decode_uleb128 (p + 1, &p);
1477                                 LOG_TIME (time_base, tdiff);
1478                                 time_base += tdiff;
1479                                 if (debug)
1480                                         fprintf (outfile, "heap shot end\n");
1481                                 if (collect_traces) {
1482                                         HeapShot *hs = thread->current_heap_shot;
1483                                         if (hs && thread->num_roots) {
1484                                                 /* transfer the root ownershipt to the heapshot */
1485                                                 hs->num_roots = thread->num_roots;
1486                                                 hs->roots = thread->roots;
1487                                                 hs->roots_extra = thread->roots_extra;
1488                                                 hs->roots_types = thread->roots_types;
1489                                         } else {
1490                                                 free (thread->roots);
1491                                                 free (thread->roots_extra);
1492                                                 free (thread->roots_types);
1493                                         }
1494                                         thread->num_roots = 0;
1495                                         thread->size_roots = 0;
1496                                         thread->roots = NULL;
1497                                         thread->roots_extra = NULL;
1498                                         thread->roots_types = NULL;
1499                                         heap_shot_resolve_reverse_refs (hs);
1500                                         heap_shot_mark_objects (hs);
1501                                         heap_shot_free_objects (hs);
1502                                 }
1503                                 thread->current_heap_shot = NULL;
1504                         } else if (subtype == TYPE_HEAP_START) {
1505                                 uint64_t tdiff = decode_uleb128 (p + 1, &p);
1506                                 LOG_TIME (time_base, tdiff);
1507                                 time_base += tdiff;
1508                                 if (debug)
1509                                         fprintf (outfile, "heap shot start\n");
1510                                 thread->current_heap_shot = new_heap_shot (time_base);
1511                         }
1512                         break;
1513                 }
1514                 case TYPE_MONITOR: {
1515                         int event = (*p >> 4) & 0x3;
1516                         int has_bt = *p & TYPE_MONITOR_BT;
1517                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
1518                         intptr_t objdiff = decode_sleb128 (p, &p);
1519                         MethodDesc* sframes [8];
1520                         MethodDesc** frames = sframes;
1521                         int record;
1522                         int num_bt = 0;
1523                         LOG_TIME (time_base, tdiff);
1524                         time_base += tdiff;
1525                         record = (!thread_filter || thread_filter == thread->thread_id);
1526                         if (event == MONO_PROFILER_MONITOR_CONTENTION) {
1527                                 MonitorDesc *mdesc = lookup_monitor (OBJ_ADDR (objdiff));
1528                                 if (record) {
1529                                         monitor_contention++;
1530                                         mdesc->contentions++;
1531                                         thread->monitor = mdesc;
1532                                         thread->contention_start = time_base;
1533                                 }
1534                                 if (has_bt) {
1535                                         num_bt = 8;
1536                                         frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
1537                                         if (!frames) {
1538                                                 fprintf (outfile, "Cannot load backtrace\n");
1539                                                 return 0;
1540                                         }
1541                                         if (record)
1542                                                 add_trace_methods (frames, num_bt, &mdesc->traces, 1);
1543                                 } else {
1544                                         if (record)
1545                                                 add_trace_thread (thread, &mdesc->traces, 1);
1546                                 }
1547                         } else if (event == MONO_PROFILER_MONITOR_FAIL) {
1548                                 if (record) {
1549                                         monitor_failed++;
1550                                         if (thread->monitor && thread->contention_start) {
1551                                                 uint64_t wait_time = time_base - thread->contention_start;
1552                                                 if (wait_time > thread->monitor->max_wait_time)
1553                                                         thread->monitor->max_wait_time = wait_time;
1554                                                 thread->monitor->wait_time += wait_time;
1555                                                 thread->monitor = NULL;
1556                                                 thread->contention_start = 0;
1557                                         }
1558                                 }
1559                         } else if (event == MONO_PROFILER_MONITOR_DONE) {
1560                                 if (record) {
1561                                         monitor_acquired++;
1562                                         if (thread->monitor && thread->contention_start) {
1563                                                 uint64_t wait_time = time_base - thread->contention_start;
1564                                                 if (wait_time > thread->monitor->max_wait_time)
1565                                                         thread->monitor->max_wait_time = wait_time;
1566                                                 thread->monitor->wait_time += wait_time;
1567                                                 thread->monitor = NULL;
1568                                                 thread->contention_start = 0;
1569                                         }
1570                                 }
1571                         }
1572                         if (debug)
1573                                 fprintf (outfile, "monitor %s for object %p\n", monitor_ev_name (event), (void*)OBJ_ADDR (objdiff));
1574                         if (frames != sframes)
1575                                 free (frames);
1576                         break;
1577                 }
1578                 case TYPE_EXCEPTION: {
1579                         int subtype = *p & 0x70;
1580                         int has_bt = *p & TYPE_EXCEPTION_BT;
1581                         uint64_t tdiff = decode_uleb128 (p + 1, &p);
1582                         MethodDesc* sframes [8];
1583                         MethodDesc** frames = sframes;
1584                         int record;
1585                         LOG_TIME (time_base, tdiff);
1586                         time_base += tdiff;
1587                         record = (!thread_filter || thread_filter == thread->thread_id);
1588                         if (subtype == TYPE_CLAUSE) {
1589                                 int clause_type = decode_uleb128 (p, &p);
1590                                 int clause_num = decode_uleb128 (p, &p);
1591                                 int64_t ptrdiff = decode_sleb128 (p, &p);
1592                                 method_base += ptrdiff;
1593                                 if (record)
1594                                         clause_summary [clause_type]++;
1595                                 if (debug)
1596                                         fprintf (outfile, "clause %s (%d) in method %s\n", clause_name (clause_type), clause_num, lookup_method (method_base)->name);
1597                         } else {
1598                                 intptr_t objdiff = decode_sleb128 (p, &p);
1599                                 if (record)
1600                                         throw_count++;
1601                                 if (has_bt) {
1602                                         has_bt = 8;
1603                                         frames = decode_bt (sframes, &has_bt, p, &p, ptr_base);
1604                                         if (!frames) {
1605                                                 fprintf (outfile, "Cannot load backtrace\n");
1606                                                 return 0;
1607                                         }
1608                                         if (record)
1609                                                 add_trace_methods (frames, has_bt, &exc_traces, 1);
1610                                 } else {
1611                                         if (record)
1612                                                 add_trace_thread (thread, &exc_traces, 1);
1613                                 }
1614                                 if (frames != sframes)
1615                                         free (frames);
1616                                 if (debug)
1617                                         fprintf (outfile, "throw %p\n", (void*)OBJ_ADDR (objdiff));
1618                         }
1619                         break;
1620                 }
1621                 default:
1622                         fprintf (outfile, "unhandled profiler event: 0x%x at file offset: %llu + %d (len: %d\n)\n", *p, file_offset, p - ctx->buf, len);
1623                         exit (1);
1624                 }
1625         }
1626         thread->last_time = time_base;
1627         for (i = 0; i < thread->stack_id; ++i)
1628                 thread->stack [i]->recurse_count = 0;
1629         return 1;
1630 }
1631
1632 static ProfContext*
1633 load_file (char *name)
1634 {
1635         unsigned char *p;
1636         ProfContext *ctx = calloc (sizeof (ProfContext), 1);
1637         if (strcmp (name, "-") == 0)
1638                 ctx->file = stdin;
1639         else
1640                 ctx->file = fopen (name, "rb");
1641         if (!ctx->file) {
1642                 printf ("Cannot open file: %s\n", name);
1643                 exit (1);
1644         }
1645 #if defined (HAVE_SYS_ZLIB)
1646         if (ctx->file != stdin)
1647                 ctx->gzfile = gzdopen (fileno (ctx->file), "rb");
1648 #endif
1649         if (!load_data (ctx, 32))
1650                 return NULL;
1651         p = ctx->buf;
1652         if (read_int32 (p) != LOG_HEADER_ID || p [6] > LOG_DATA_VERSION)
1653                 return NULL;
1654         ctx->version_major = p [4];
1655         ctx->version_minor = p [5];
1656         ctx->data_version = p [6];
1657         /* reading 64 bit files on 32 bit systems not supported yet */
1658         if (p [7] > sizeof (void*))
1659                 return NULL;
1660         if (read_int32 (p + 20)) /* flags must be 0 */
1661                 return NULL;
1662         ctx->startup_time = read_int64 (p + 8);
1663         ctx->timer_overhead = read_int32 (p + 16);
1664         ctx->pid = read_int32 (p + 24);
1665         ctx->port = read_int16 (p + 28);
1666         return ctx;
1667 }
1668
1669 enum {
1670         ALLOC_SORT_BYTES,
1671         ALLOC_SORT_COUNT
1672 };
1673 static int alloc_sort_mode = ALLOC_SORT_BYTES;
1674
1675 static int
1676 compare_class (const void *a, const void *b)
1677 {
1678         ClassDesc *const*A = a;
1679         ClassDesc *const*B = b;
1680         uint64_t vala, valb;
1681         if (alloc_sort_mode == ALLOC_SORT_BYTES) {
1682                 vala = (*A)->alloc_size;
1683                 valb = (*B)->alloc_size;
1684         } else {
1685                 vala = (*A)->allocs;
1686                 valb = (*B)->allocs;
1687         }
1688         if (valb == vala)
1689                 return 0;
1690         if (valb < vala)
1691                 return -1;
1692         return 1;
1693 }
1694
1695 static void
1696 dump_header (ProfContext *ctx)
1697 {
1698         time_t st = ctx->startup_time / 1000;
1699         char *t = ctime (&st);
1700         fprintf (outfile, "\nMono log profiler data\n");
1701         fprintf (outfile, "\tProfiler version: %d.%d\n", ctx->version_major, ctx->version_minor);
1702         fprintf (outfile, "\tData version: %d\n", ctx->data_version);
1703         fprintf (outfile, "\tMean timer overhead: %d nanoseconds\n", ctx->timer_overhead);
1704         fprintf (outfile, "\tProgram startup: %s", t);
1705         if (ctx->pid)
1706                 fprintf (outfile, "\tProgram ID: %d\n", ctx->pid);
1707         if (ctx->port)
1708                 fprintf (outfile, "\tServer listening on: %d\n", ctx->port);
1709 }
1710
1711 static void
1712 dump_traces (TraceDesc *traces, const char *desc)
1713 {
1714         int j;
1715         if (!show_traces)
1716                 return;
1717         if (!traces->count)
1718                 return;
1719         sort_context_array (traces);
1720         for (j = 0; j < traces->count; ++j) {
1721                 int k;
1722                 BackTrace *bt;
1723                 bt = traces->traces [j].bt;
1724                 if (!bt->count)
1725                         continue;
1726                 fprintf (outfile, "\t%llu %s from:\n", traces->traces [j].count, desc);
1727                 for (k = 0; k < bt->count; ++k)
1728                         fprintf (outfile, "\t\t%s\n", bt->methods [k]->name);
1729         }
1730 }
1731
1732 static void
1733 dump_threads (ProfContext *ctx)
1734 {
1735         ThreadContext *thread;
1736         fprintf (outfile, "\nThread summary\n");
1737         for (thread = ctx->threads; thread; thread = thread->next) {
1738                 fprintf (outfile, "\tThread: %p, name: \"%s\"\n", (void*)thread->thread_id, thread->name? thread->name: "");
1739         }
1740 }
1741
1742 static void
1743 dump_exceptions (void)
1744 {
1745         int i;
1746         fprintf (outfile, "\nException summary\n");
1747         fprintf (outfile, "\tThrows: %llu\n", throw_count);
1748         dump_traces (&exc_traces, "throws");
1749         for (i = 0; i <= MONO_EXCEPTION_CLAUSE_FAULT; ++i) {
1750                 if (!clause_summary [i])
1751                         continue;
1752                 fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), clause_summary [i]);
1753         }
1754 }
1755
1756 static int
1757 compare_monitor (const void *a, const void *b)
1758 {
1759         MonitorDesc *const*A = a;
1760         MonitorDesc *const*B = b;
1761         if ((*B)->wait_time == (*A)->wait_time)
1762                 return 0;
1763         if ((*B)->wait_time < (*A)->wait_time)
1764                 return -1;
1765         return 1;
1766 }
1767
1768 static void
1769 dump_monitors (void)
1770 {
1771         MonitorDesc **monitors;
1772         int i, j;
1773         if (!num_monitors)
1774                 return;
1775         monitors = malloc (sizeof (void*) * num_monitors);
1776         for (i = 0, j = 0; i < SMALL_HASH_SIZE; ++i) {
1777                 MonitorDesc *mdesc = monitor_hash [i];
1778                 while (mdesc) {
1779                         monitors [j++] = mdesc;
1780                         mdesc = mdesc->next;
1781                 }
1782         }
1783         qsort (monitors, num_monitors, sizeof (void*), compare_monitor);
1784         fprintf (outfile, "\nMonitor lock summary\n");
1785         for (i = 0; i < num_monitors; ++i) {
1786                 MonitorDesc *mdesc = monitors [i];
1787                 fprintf (outfile, "\tLock object %p: %d contentions\n", (void*)mdesc->objid, (int)mdesc->contentions);
1788                 fprintf (outfile, "\t\t%.6f secs total wait time, %.6f max, %.6f average\n",
1789                         mdesc->wait_time/1000000000.0, mdesc->max_wait_time/1000000000.0, mdesc->wait_time/1000000000.0/mdesc->contentions);
1790                 dump_traces (&mdesc->traces, "contentions");
1791         }
1792         fprintf (outfile, "\tLock contentions: %llu\n", monitor_contention);
1793         fprintf (outfile, "\tLock acquired: %llu\n", monitor_acquired);
1794         fprintf (outfile, "\tLock failures: %llu\n", monitor_failed);
1795 }
1796
1797 static void
1798 dump_gcs (void)
1799 {
1800         int i;
1801         fprintf (outfile, "\nGC summary\n");
1802         fprintf (outfile, "\tGC resizes: %d\n", gc_resizes);
1803         fprintf (outfile, "\tMax heap size: %llu\n", max_heap_size);
1804         fprintf (outfile, "\tObject moves: %llu\n", gc_object_moves);
1805         for (i = 0; i < 3; ++i) {
1806                 if (!gc_info [i].count)
1807                         continue;
1808                 fprintf (outfile, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
1809                         i, gc_info [i].count, gc_info [i].max_time / 1000, gc_info [i].total_time / 1000,
1810                         gc_info [i].total_time / gc_info [i].count / 1000);
1811         }
1812         for (i = 0; i < 3; ++i) {
1813                 if (!handle_info [i].max_live)
1814                         continue;
1815                 fprintf (outfile, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
1816                         get_handle_name (i), handle_info [i].created, handle_info [i].destroyed, handle_info [i].max_live);
1817                 dump_traces (&handle_info [i].traces, "created");
1818         }
1819 }
1820
1821 static void
1822 dump_allocations (void)
1823 {
1824         int i, c;
1825         intptr_t allocs = 0;
1826         uint64_t size = 0;
1827         int header_done = 0;
1828         ClassDesc **classes = malloc (num_classes * sizeof (void*));
1829         ClassDesc *cd;
1830         c = 0;
1831         for (i = 0; i < HASH_SIZE; ++i) {
1832                 cd = class_hash [i];
1833                 while (cd) {
1834                         classes [c++] = cd;
1835                         cd = cd->next;
1836                 }
1837         }
1838         qsort (classes, num_classes, sizeof (void*), compare_class);
1839         for (i = 0; i < num_classes; ++i) {
1840                 cd = classes [i];
1841                 if (!cd->allocs)
1842                         continue;
1843                 allocs += cd->allocs;
1844                 size += cd->alloc_size;
1845                 if (!header_done++) {
1846                         fprintf (outfile, "\nAllocation summary\n");
1847                         fprintf (outfile, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
1848                 }
1849                 fprintf (outfile, "%10llu %10d %8llu %s\n", cd->alloc_size, cd->allocs, cd->alloc_size / cd->allocs, cd->name);
1850                 dump_traces (&cd->traces, "bytes");
1851         }
1852         if (allocs)
1853                 fprintf (outfile, "Total memory allocated: %llu bytes in %d objects\n", size, allocs);
1854 }
1855
1856 enum {
1857         METHOD_SORT_TOTAL,
1858         METHOD_SORT_SELF,
1859         METHOD_SORT_CALLS
1860 };
1861
1862 static int method_sort_mode = METHOD_SORT_TOTAL;
1863
1864 static int
1865 compare_method (const void *a, const void *b)
1866 {
1867         MethodDesc *const*A = a;
1868         MethodDesc *const*B = b;
1869         uint64_t vala, valb;
1870         if (method_sort_mode == METHOD_SORT_SELF) {
1871                 vala = (*A)->self_time;
1872                 valb = (*B)->self_time;
1873         } else if (method_sort_mode == METHOD_SORT_CALLS) {
1874                 vala = (*A)->calls;
1875                 valb = (*B)->calls;
1876         } else {
1877                 vala = (*A)->total_time;
1878                 valb = (*B)->total_time;
1879         }
1880         if (vala == valb)
1881                 return 0;
1882         if (valb < vala)
1883                 return -1;
1884         return 1;
1885 }
1886
1887 static void
1888 dump_metadata (void)
1889 {
1890         fprintf (outfile, "\nMetadata summary\n");
1891         fprintf (outfile, "\tLoaded images: %d\n", num_images);
1892         if (verbose) {
1893                 ImageDesc *image;
1894                 int i;
1895                 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
1896                         image = image_hash [i];
1897                         while (image) {
1898                                 fprintf (outfile, "\t\t%s\n", image->filename);
1899                                 image = image->next;
1900                         }
1901                 }
1902         }
1903
1904 }
1905
1906 static void
1907 dump_methods (void)
1908 {
1909         int i, c;
1910         uint64_t calls = 0;
1911         int header_done = 0;
1912         MethodDesc **methods = malloc (num_methods * sizeof (void*));
1913         MethodDesc *cd;
1914         c = 0;
1915         for (i = 0; i < HASH_SIZE; ++i) {
1916                 cd = method_hash [i];
1917                 while (cd) {
1918                         cd->total_time = cd->self_time + cd->callee_time;
1919                         methods [c++] = cd;
1920                         cd = cd->next;
1921                 }
1922         }
1923         qsort (methods, num_methods, sizeof (void*), compare_method);
1924         for (i = 0; i < num_methods; ++i) {
1925                 uint64_t msecs;
1926                 uint64_t smsecs;
1927                 cd = methods [i];
1928                 if (!cd->calls)
1929                         continue;
1930                 calls += cd->calls;
1931                 msecs = cd->total_time / 1000000;
1932                 smsecs = (cd->total_time - cd->callee_time) / 1000000;
1933                 if (!msecs && !verbose)
1934                         continue;
1935                 if (!header_done++) {
1936                         fprintf (outfile, "\nMethod call summary\n");
1937                         fprintf (outfile, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
1938                 }
1939                 fprintf (outfile, "%8llu %8llu %10llu %s\n", msecs, smsecs, cd->calls, cd->name);
1940                 dump_traces (&cd->traces, "calls");
1941         }
1942         if (calls)
1943                 fprintf (outfile, "Total calls: %llu\n", calls);
1944 }
1945
1946 static int
1947 compare_heap_class (const void *a, const void *b)
1948 {
1949         HeapClassDesc *const*A = a;
1950         HeapClassDesc *const*B = b;
1951         uint64_t vala, valb;
1952         if (alloc_sort_mode == ALLOC_SORT_BYTES) {
1953                 vala = (*A)->total_size;
1954                 valb = (*B)->total_size;
1955         } else {
1956                 vala = (*A)->count;
1957                 valb = (*B)->count;
1958         }
1959         if (valb == vala)
1960                 return 0;
1961         if (valb < vala)
1962                 return -1;
1963         return 1;
1964 }
1965
1966 static int
1967 compare_rev_class (const void *a, const void *b)
1968 {
1969         const HeapClassRevRef *A = a;
1970         const HeapClassRevRef *B = b;
1971         if (B->count == A->count)
1972                 return 0;
1973         if (B->count < A->count)
1974                 return -1;
1975         return 1;
1976 }
1977
1978 static void
1979 dump_rev_claases (HeapClassRevRef *revs, int count)
1980 {
1981         int j;
1982         if (!show_traces)
1983                 return;
1984         if (!count)
1985                 return;
1986         for (j = 0; j < count; ++j) {
1987                 HeapClassDesc *cd = revs [j].klass;
1988                 fprintf (outfile, "\t\t%llu references from: %s\n", revs [j].count, cd->klass->name);
1989         }
1990 }
1991
1992 static void
1993 heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
1994 {
1995         uint64_t size = 0;
1996         uint64_t count = 0;
1997         int ccount = 0;
1998         int i;
1999         HeapClassDesc *cd;
2000         HeapClassDesc **sorted;
2001         sorted = malloc (sizeof (void*) * hs->class_count);
2002         for (i = 0; i < hs->hash_size; ++i) {
2003                 cd = hs->class_hash [i];
2004                 if (!cd)
2005                         continue;
2006                 count += cd->count;
2007                 size += cd->total_size;
2008                 sorted [ccount++] = cd;
2009         }
2010         hs->sorted = sorted;
2011         qsort (sorted, ccount, sizeof (void*), compare_heap_class);
2012         fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %d\n",
2013                 hs_num, (hs->timestamp - startup_time)/1000000000.0, size, count, ccount, hs->num_roots);
2014         if (!verbose && ccount > 30)
2015                 ccount = 30;
2016         fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
2017         for (i = 0; i < ccount; ++i) {
2018                 HeapClassRevRef *rev_sorted;
2019                 int j, k;
2020                 HeapClassDesc *ocd = NULL;
2021                 cd = sorted [i];
2022                 if (last_hs)
2023                         ocd = heap_class_lookup (last_hs, cd->klass);
2024                 fprintf (outfile, "\t%10llu %10llu %8llu %s", cd->total_size, cd->count, cd->total_size / cd->count, cd->klass->name);
2025                 if (ocd) {
2026                         int64_t bdiff = cd->total_size - ocd->total_size;
2027                         int64_t cdiff = cd->count - ocd->count;
2028                         fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", bdiff, cdiff);
2029                 } else {
2030                         fprintf (outfile, "\n");
2031                 }
2032                 if (!collect_traces)
2033                         continue;
2034                 rev_sorted = malloc (cd->rev_count * sizeof (HeapClassRevRef));
2035                 k = 0;
2036                 for (j = 0; j < cd->rev_hash_size; ++j) {
2037                         if (cd->rev_hash [j].klass)
2038                                 rev_sorted [k++] = cd->rev_hash [j];
2039                 }
2040                 assert (cd->rev_count == k);
2041                 qsort (rev_sorted, cd->rev_count, sizeof (HeapClassRevRef), compare_rev_class);
2042                 if (cd->root_references)
2043                         fprintf (outfile, "\t\t%d root references (%d pinning)\n", cd->root_references, cd->pinned_references);
2044                 dump_rev_claases (rev_sorted, cd->rev_count);
2045                 free (rev_sorted);
2046         }
2047         free (sorted);
2048 }
2049
2050 static int
2051 compare_heap_shots (const void *a, const void *b)
2052 {
2053         HeapShot *const*A = a;
2054         HeapShot *const*B = b;
2055         if ((*B)->timestamp == (*A)->timestamp)
2056                 return 0;
2057         if ((*B)->timestamp > (*A)->timestamp)
2058                 return -1;
2059         return 1;
2060 }
2061
2062 static void
2063 dump_heap_shots (void)
2064 {
2065         HeapShot **hs_sorted;
2066         HeapShot *hs;
2067         HeapShot *last_hs = NULL;
2068         int i;
2069         if (!heap_shots)
2070                 return;
2071         hs_sorted = malloc (num_heap_shots * sizeof (void*));
2072         fprintf (outfile, "\nHeap shot summary\n");
2073         i = 0;
2074         for (hs = heap_shots; hs; hs = hs->next)
2075                 hs_sorted [i++] = hs;
2076         qsort (hs_sorted, num_heap_shots, sizeof (void*), compare_heap_shots);
2077         for (i = 0; i < num_heap_shots; ++i) {
2078                 hs = hs_sorted [i];
2079                 heap_shot_summary (hs, i, last_hs);
2080                 last_hs = hs;
2081         }
2082 }
2083
2084 static void
2085 flush_context (ProfContext *ctx)
2086 {
2087         ThreadContext *thread;
2088         /* FIXME: sometimes there are leftovers: indagate */
2089         for (thread = ctx->threads; thread; thread = thread->next) {
2090                 while (thread->stack_id) {
2091                         if (debug)
2092                                 fprintf (outfile, "thread %p has %d items on stack\n", (void*)thread->thread_id, thread->stack_id);
2093                         pop_method (thread, thread->stack [thread->stack_id - 1], thread->last_time);
2094                 }
2095         }
2096 }
2097
2098 static const char *reports = "header,gc,alloc,call,metadata,exception,monitor,thread,heapshot";
2099
2100 static const char*
2101 match_option (const char *p, const char *opt)
2102 {
2103         int len = strlen (opt);
2104         if (strncmp (p, opt, len) == 0) {
2105                 if (p [len] == ',')
2106                         len++;
2107                 return p + len;
2108         }
2109         return p;
2110 }
2111
2112 static int
2113 print_reports (ProfContext *ctx, const char *reps, int parse_only)
2114 {
2115         const char *opt;
2116         const char *p;
2117         for (p = reps; *p; p = opt) {
2118                 if ((opt = match_option (p, "header")) != p) {
2119                         if (!parse_only)
2120                                 dump_header (ctx);
2121                         continue;
2122                 }
2123                 if ((opt = match_option (p, "thread")) != p) {
2124                         if (!parse_only)
2125                                 dump_threads (ctx);
2126                         continue;
2127                 }
2128                 if ((opt = match_option (p, "gc")) != p) {
2129                         if (!parse_only)
2130                                 dump_gcs ();
2131                         continue;
2132                 }
2133                 if ((opt = match_option (p, "alloc")) != p) {
2134                         if (!parse_only)
2135                                 dump_allocations ();
2136                         continue;
2137                 }
2138                 if ((opt = match_option (p, "call")) != p) {
2139                         if (!parse_only)
2140                                 dump_methods ();
2141                         continue;
2142                 }
2143                 if ((opt = match_option (p, "metadata")) != p) {
2144                         if (!parse_only)
2145                                 dump_metadata ();
2146                         continue;
2147                 }
2148                 if ((opt = match_option (p, "exception")) != p) {
2149                         if (!parse_only)
2150                                 dump_exceptions ();
2151                         continue;
2152                 }
2153                 if ((opt = match_option (p, "monitor")) != p) {
2154                         if (!parse_only)
2155                                 dump_monitors ();
2156                         continue;
2157                 }
2158                 if ((opt = match_option (p, "heapshot")) != p) {
2159                         if (!parse_only)
2160                                 dump_heap_shots ();
2161                         continue;
2162                 }
2163                 return 0;
2164         }
2165         return 1;
2166 }
2167
2168 static int
2169 add_find_spec (const char *p)
2170 {
2171         if (p [0] == 'S' && p [1] == ':') {
2172                 char *vale;
2173                 find_size = strtoul (p + 2, &vale, 10);
2174                 return 1;
2175         } else if (p [0] == 'T' && p [1] == ':') {
2176                 find_name = p + 2;
2177                 return 1;
2178         }
2179         return 0;
2180 }
2181
2182 static void
2183 usage (void)
2184 {
2185         printf ("Mono log profiler report version %d.%d\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR);
2186         printf ("Usage: mprof-report [OPTIONS] FILENAME\n");
2187         printf ("FILENAME can be '-' to read from standard input.\n");
2188         printf ("Options:\n");
2189         printf ("\t--help               display this help\n");
2190         printf ("\t--out=FILE           write to FILE instead of stdout\n");
2191         printf ("\t--traces             collect and show backtraces\n"); 
2192         printf ("\t--maxframes=NUM      limit backtraces to NUM entries\n");
2193         printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
2194         printf ("\t                     %s\n", reports);
2195         printf ("\t--method-sort=MODE   sort methods according to MODE: total, self, calls\n");
2196         printf ("\t--alloc-sort=MODE    sort allocations according to MODE: bytes, count\n");
2197         printf ("\t--track=OB1[,OB2...] track what happens to objects OBJ1, O2 etc.\n");
2198         printf ("\t--find=FINDSPEC      find and track objects matching FINFSPEC, where FINDSPEC is:\n");
2199         printf ("\t                     S:minimum_size or T:partial_name\n");
2200         printf ("\t--thread=THREADID    consider just the data for thread THREADID\n");
2201         printf ("\t--time=FROM-TO       consider data FROM seconds from startup up to TO seconds\n");
2202         printf ("\t--verbose            increase verbosity level\n");
2203         printf ("\t--debug              display decoding debug info for mprof-report devs\n");
2204 }
2205
2206 int
2207 main (int argc, char *argv[])
2208 {
2209         ProfContext *ctx;
2210         int i;
2211         outfile = stdout;
2212         for (i = 1; i < argc; ++i) {
2213                 if (strcmp ("--debug", argv [i]) == 0) {
2214                         debug++;
2215                 } else if (strcmp ("--help", argv [i]) == 0) {
2216                         usage ();
2217                         return 0;
2218                 } else if (strncmp ("--alloc-sort=", argv [i], 13) == 0) {
2219                         const char *val = argv [i] + 13;
2220                         if (strcmp (val, "bytes") == 0) {
2221                                 alloc_sort_mode = ALLOC_SORT_BYTES;
2222                         } else if (strcmp (val, "count") == 0) {
2223                                 alloc_sort_mode = ALLOC_SORT_COUNT;
2224                         } else {
2225                                 usage ();
2226                                 return 1;
2227                         }
2228                 } else if (strncmp ("--method-sort=", argv [i], 14) == 0) {
2229                         const char *val = argv [i] + 14;
2230                         if (strcmp (val, "total") == 0) {
2231                                 method_sort_mode = METHOD_SORT_TOTAL;
2232                         } else if (strcmp (val, "self") == 0) {
2233                                 method_sort_mode = METHOD_SORT_SELF;
2234                         } else if (strcmp (val, "calls") == 0) {
2235                                 method_sort_mode = METHOD_SORT_CALLS;
2236                         } else {
2237                                 usage ();
2238                                 return 1;
2239                         }
2240                 } else if (strncmp ("--reports=", argv [i], 10) == 0) {
2241                         const char *val = argv [i] + 10;
2242                         if (!print_reports (NULL, val, 1)) {
2243                                 usage ();
2244                                 return 1;
2245                         }
2246                         reports = val;
2247                 } else if (strncmp ("--out=", argv [i], 6) == 0) {
2248                         const char *val = argv [i] + 6;
2249                         outfile = fopen (val, "w");
2250                         if (!outfile) {
2251                                 printf ("Cannot open output file: %s\n", val);
2252                                 return 1;
2253                         }
2254                 } else if (strncmp ("--maxframes=", argv [i], 12) == 0) {
2255                         const char *val = argv [i] + 12;
2256                         char *vale;
2257                         trace_max = strtoul (val, &vale, 10);
2258                 } else if (strncmp ("--find=", argv [i], 7) == 0) {
2259                         const char *val = argv [i] + 7;
2260                         if (!add_find_spec (val)) {
2261                                 usage ();
2262                                 return 1;
2263                         }
2264                 } else if (strncmp ("--track=", argv [i], 8) == 0) {
2265                         const char *val = argv [i] + 8;
2266                         char *vale;
2267                         while (*val) {
2268                                 uintptr_t tracked_obj;
2269                                 if (*val == ',') {
2270                                         val++;
2271                                         continue;
2272                                 }
2273                                 tracked_obj = strtoul (val, &vale, 0);
2274                                 found_object (tracked_obj);
2275                                 val = vale;
2276                         }
2277                 } else if (strncmp ("--thread=", argv [i], 9) == 0) {
2278                         const char *val = argv [i] + 9;
2279                         char *vale;
2280                         thread_filter = strtoul (val, &vale, 0);
2281                 } else if (strncmp ("--time=", argv [i], 7) == 0) {
2282                         char *val = pstrdup (argv [i] + 7);
2283                         double from_secs, to_secs;
2284                         char *top = strchr (val, '-');
2285                         if (!top) {
2286                                 usage ();
2287                                 return 1;
2288                         }
2289                         *top++ = 0;
2290                         from_secs = atof (val);
2291                         to_secs = atof (top);
2292                         free (val);
2293                         if (from_secs > to_secs) {
2294                                 usage ();
2295                                 return 1;
2296                         }
2297                         time_from = from_secs * 1000000000;
2298                         time_to = to_secs * 1000000000;
2299                 } else if (strcmp ("--verbose", argv [i]) == 0) {
2300                         verbose++;
2301                 } else if (strcmp ("--traces", argv [i]) == 0) {
2302                         show_traces = 1;
2303                         collect_traces = 1;
2304                 } else {
2305                         break;
2306                 }
2307         }
2308         if (i >= argc) {
2309                 usage ();
2310                 return 2;
2311         }
2312         ctx = load_file (argv [i]);
2313         if (!ctx) {
2314                 printf ("Not a log profiler data file (or unsupported version).\n");
2315                 return 1;
2316         }
2317         while (decode_buffer (ctx));
2318         flush_context (ctx);
2319         if (num_tracked_objects)
2320                 return 0;
2321         print_reports (ctx, reports, 0);
2322         return 0;
2323 }
2324