Added the MembershipPasswordAttribute class and associated unit tests
[mono-project.git] / mono / profiler / decode.c
blob2a674e579a2910519757e86ea1ff2d71e2b4b1ea
1 /*
2 * decode.c: mprof-report program source: decode and analyze the log profiler data
4 * Author:
5 * Paolo Molaro (lupus@ximian.com)
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 <glib.h>
24 #include <mono/metadata/profiler.h>
25 #include <mono/metadata/object.h>
26 #include <mono/metadata/debug-helpers.h>
27 #include <mono/utils/mono-counters.h>
29 #define HASH_SIZE 9371
30 #define SMALL_HASH_SIZE 31
32 #if defined(__native_client__) || defined(__native_client_codegen__)
33 volatile int __nacl_thread_suspension_needed = 0;
34 void __nacl_suspend_thread_if_needed() {}
35 #endif
37 static int debug = 0;
38 static int collect_traces = 0;
39 static int show_traces = 0;
40 static int trace_max = 6;
41 static int verbose = 0;
42 static uintptr_t *tracked_objects = 0;
43 static int num_tracked_objects = 0;
44 static uintptr_t thread_filter = 0;
45 static uint64_t find_size = 0;
46 static const char* find_name = NULL;
47 static uint64_t time_from = 0;
48 static uint64_t time_to = 0xffffffffffffffffULL;
49 static int use_time_filter = 0;
50 static uint64_t startup_time = 0;
51 static FILE* outfile = NULL;
53 static int32_t
54 read_int16 (unsigned char *p)
56 int32_t value = *p++;
57 value |= (*p++) << 8;
58 return value;
61 static int32_t
62 read_int32 (unsigned char *p)
64 int32_t value = *p++;
65 value |= (*p++) << 8;
66 value |= (*p++) << 16;
67 value |= (uint32_t)(*p++) << 24;
68 return value;
71 static int64_t
72 read_int64 (unsigned char *p)
74 uint64_t value = *p++;
75 value |= (*p++) << 8;
76 value |= (*p++) << 16;
77 value |= (uint64_t)(*p++) << 24;
78 value |= (uint64_t)(*p++) << 32;
79 value |= (uint64_t)(*p++) << 40;
80 value |= (uint64_t)(*p++) << 48;
81 value |= (uint64_t)(*p++) << 54;
82 return value;
85 static char*
86 pstrdup (const char *s)
88 int len = strlen (s) + 1;
89 char *p = malloc (len);
90 memcpy (p, s, len);
91 return p;
94 typedef struct _CounterValue CounterValue;
95 struct _CounterValue {
96 uint64_t timestamp;
97 unsigned char *buffer;
98 CounterValue *next;
101 typedef struct _Counter Counter;
102 struct _Counter {
103 int index;
104 int section;
105 const char *name;
106 int type;
107 int unit;
108 int variance;
109 CounterValue *values;
110 CounterValue *values_last;
113 typedef struct _CounterList CounterList;
114 struct _CounterList {
115 Counter *counter;
116 CounterList *next;
119 typedef struct _CounterSection CounterSection;
120 struct _CounterSection {
121 int value;
122 CounterList *counters;
123 CounterList *counters_last;
124 CounterSection *next;
127 typedef struct _CounterTimestamp CounterTimestamp;
128 struct _CounterTimestamp {
129 uint64_t value;
130 CounterSection *sections;
131 CounterSection *sections_last;
132 CounterTimestamp *next;
135 static CounterList *counters = NULL;
136 static CounterSection *counters_sections = NULL;
137 static CounterTimestamp *counters_timestamps = NULL;
139 enum {
140 COUNTERS_SORT_TIME,
141 COUNTERS_SORT_CATEGORY
144 static int counters_sort_mode = COUNTERS_SORT_TIME;
146 static void
147 add_counter_to_section (Counter *counter)
149 CounterSection *csection, *s;
150 CounterList *clist;
152 clist = calloc (1, sizeof (CounterList));
153 clist->counter = counter;
155 for (csection = counters_sections; csection; csection = csection->next) {
156 if (csection->value == counter->section) {
157 /* If section exist */
158 if (!csection->counters)
159 csection->counters = clist;
160 else
161 csection->counters_last->next = clist;
162 csection->counters_last = clist;
163 return;
167 /* If section does not exist */
168 csection = calloc (1, sizeof (CounterSection));
169 csection->value = counter->section;
170 csection->counters = clist;
171 csection->counters_last = clist;
173 if (!counters_sections) {
174 counters_sections = csection;
175 } else {
176 s = counters_sections;
177 while (s->next)
178 s = s->next;
179 s->next = csection;
183 static void
184 add_counter (int section, const char *name, int type, int unit, int variance, int index)
186 CounterList *list, *l;
187 Counter *counter;
189 for (list = counters; list; list = list->next)
190 if (list->counter->index == index)
191 return;
193 counter = calloc (1, sizeof (Counter));
194 counter->section = section;
195 counter->name = name;
196 counter->type = type;
197 counter->unit = unit;
198 counter->variance = variance;
199 counter->index = index;
201 list = calloc (1, sizeof (CounterList));
202 list->counter = counter;
204 if (!counters) {
205 counters = list;
206 } else {
207 l = counters;
208 while (l->next)
209 l = l->next;
210 l->next = list;
213 if (counters_sort_mode == COUNTERS_SORT_CATEGORY || !verbose)
214 add_counter_to_section (counter);
217 static void
218 add_counter_to_timestamp (uint64_t timestamp, Counter *counter)
220 CounterTimestamp *ctimestamp, *t;
221 CounterSection *csection;
222 CounterList *clist;
224 clist = calloc (1, sizeof (CounterList));
225 clist->counter = counter;
227 for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
228 if (ctimestamp->value == timestamp) {
229 for (csection = ctimestamp->sections; csection; csection = csection->next) {
230 if (csection->value == counter->section) {
231 /* if timestamp exist and section exist */
232 if (!csection->counters)
233 csection->counters = clist;
234 else
235 csection->counters_last->next = clist;
236 csection->counters_last = clist;
237 return;
241 /* if timestamp exist and section does not exist */
242 csection = calloc (1, sizeof (CounterSection));
243 csection->value = counter->section;
244 csection->counters = clist;
245 csection->counters_last = clist;
247 if (!ctimestamp->sections)
248 ctimestamp->sections = csection;
249 else
250 ctimestamp->sections_last->next = csection;
251 ctimestamp->sections_last = csection;
252 return;
256 /* If timestamp do not exist and section does not exist */
257 csection = calloc (1, sizeof (CounterSection));
258 csection->value = counter->section;
259 csection->counters = clist;
260 csection->counters_last = clist;
262 ctimestamp = calloc (1, sizeof (CounterTimestamp));
263 ctimestamp->value = timestamp;
264 ctimestamp->sections = csection;
265 ctimestamp->sections_last = csection;
267 if (!counters_timestamps) {
268 counters_timestamps = ctimestamp;
269 } else {
270 t = counters_timestamps;
271 while (t->next)
272 t = t->next;
273 t->next = ctimestamp;
277 static void
278 add_counter_value (int index, CounterValue *value)
280 CounterList *list;
282 for (list = counters; list; list = list->next) {
283 if (list->counter->index == index) {
284 if (!list->counter->values)
285 list->counter->values = value;
286 else
287 list->counter->values_last->next = value;
288 list->counter->values_last = value;
290 if (counters_sort_mode == COUNTERS_SORT_TIME)
291 add_counter_to_timestamp (value->timestamp, list->counter);
293 return;
298 static const char*
299 section_name (int section)
301 switch (section) {
302 case MONO_COUNTER_JIT: return "Mono JIT";
303 case MONO_COUNTER_GC: return "Mono GC";
304 case MONO_COUNTER_METADATA: return "Mono Metadata";
305 case MONO_COUNTER_GENERICS: return "Mono Generics";
306 case MONO_COUNTER_SECURITY: return "Mono Security";
307 case MONO_COUNTER_RUNTIME: return "Mono Runtime";
308 case MONO_COUNTER_SYSTEM: return "Mono System";
309 default: return "<unknown>";
313 static const char*
314 type_name (int type)
316 switch (type) {
317 case MONO_COUNTER_INT: return "Int";
318 case MONO_COUNTER_UINT: return "UInt";
319 case MONO_COUNTER_WORD: return "Word";
320 case MONO_COUNTER_LONG: return "Long";
321 case MONO_COUNTER_ULONG: return "ULong";
322 case MONO_COUNTER_DOUBLE: return "Double";
323 case MONO_COUNTER_STRING: return "String";
324 case MONO_COUNTER_TIME_INTERVAL: return "Time Interval";
325 default: return "<unknown>";
329 static const char*
330 unit_name (int unit)
332 switch (unit) {
333 case MONO_COUNTER_RAW: return "Raw";
334 case MONO_COUNTER_BYTES: return "Bytes";
335 case MONO_COUNTER_TIME: return "Time";
336 case MONO_COUNTER_COUNT: return "Count";
337 case MONO_COUNTER_PERCENTAGE: return "Percentage";
338 default: return "<unknown>";
342 static const char*
343 variance_name (int variance)
345 switch (variance) {
346 case MONO_COUNTER_MONOTONIC: return "Monotonic";
347 case MONO_COUNTER_CONSTANT: return "Constant";
348 case MONO_COUNTER_VARIABLE: return "Variable";
349 default: return "<unknown>";
353 static void
354 dump_counters_value (Counter *counter, const char *key_format, const char *key, void *value)
356 char format[32];
358 if (value == NULL) {
359 snprintf (format, sizeof (format), "%s : %%s\n", key_format);
360 fprintf (outfile, format, key, "<null>");
361 } else {
362 switch (counter->type) {
363 case MONO_COUNTER_INT:
364 #if SIZEOF_VOID_P == 4
365 case MONO_COUNTER_WORD:
366 #endif
367 snprintf (format, sizeof (format), "%s : %%d\n", key_format);
368 fprintf (outfile, format, key, *(int32_t*)value);
369 break;
370 case MONO_COUNTER_UINT:
371 snprintf (format, sizeof (format), "%s : %%u\n", key_format);
372 fprintf (outfile, format, key, *(uint32_t*)value);
373 break;
374 case MONO_COUNTER_LONG:
375 #if SIZEOF_VOID_P == 8
376 case MONO_COUNTER_WORD:
377 #endif
378 case MONO_COUNTER_TIME_INTERVAL:
379 if (counter->type == MONO_COUNTER_LONG && counter->unit == MONO_COUNTER_TIME) {
380 snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
381 fprintf (outfile, format, key, (double)*(int64_t*)value / 10000.0);
382 } else if (counter->type == MONO_COUNTER_TIME_INTERVAL) {
383 snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
384 fprintf (outfile, format, key, (double)*(int64_t*)value / 1000.0);
385 } else {
386 snprintf (format, sizeof (format), "%s : %%u\n", key_format);
387 fprintf (outfile, format, key, *(int64_t*)value);
389 break;
390 case MONO_COUNTER_ULONG:
391 snprintf (format, sizeof (format), "%s : %%llu\n", key_format);
392 fprintf (outfile, format, key, *(uint64_t*)value);
393 break;
394 case MONO_COUNTER_DOUBLE:
395 snprintf (format, sizeof (format), "%s : %%f\n", key_format);
396 fprintf (outfile, format, key, *(double*)value);
397 break;
398 case MONO_COUNTER_STRING:
399 snprintf (format, sizeof (format), "%s : %%s\n", key_format);
400 fprintf (outfile, format, key, *(char*)value);
401 break;
406 static void
407 dump_counters (void)
409 Counter *counter;
410 CounterValue *cvalue;
411 CounterTimestamp *ctimestamp;
412 CounterSection *csection;
413 CounterList *clist;
414 char strtimestamp[17];
415 int i, section_printed;
417 fprintf (outfile, "\nCounters:\n");
419 if (!verbose) {
420 char counters_to_print[][64] = {
421 "Methods from AOT",
422 "Methods JITted using mono JIT",
423 "Methods JITted using LLVM",
424 "Total time spent JITting (sec)",
425 "User Time",
426 "System Time",
427 "Total Time",
428 "Working Set",
429 "Private Bytes",
430 "Virtual Bytes",
431 "Page Faults",
432 "CPU Load Average - 1min",
433 "CPU Load Average - 5min",
434 "CPU Load Average - 15min",
438 for (csection = counters_sections; csection; csection = csection->next) {
439 section_printed = 0;
441 for (clist = csection->counters; clist; clist = clist->next) {
442 counter = clist->counter;
443 if (!counter->values_last)
444 continue;
446 for (i = 0; counters_to_print [i][0] != 0; i++) {
447 if (strcmp (counters_to_print [i], counter->name) == 0) {
448 if (!section_printed) {
449 fprintf (outfile, "\t%s:\n", section_name (csection->value));
450 section_printed = 1;
453 dump_counters_value (counter, "\t\t%-30s", counter->name, counter->values_last->buffer);
454 break;
459 } else if (counters_sort_mode == COUNTERS_SORT_TIME) {
460 for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
461 fprintf (outfile, "\t%llu:%02llu:%02llu:%02llu.%03llu:\n",
462 (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 / 24 % 1000),
463 (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 % 24),
464 (unsigned long long) (ctimestamp->value / 1000 / 60 % 60),
465 (unsigned long long) (ctimestamp->value / 1000 % 60),
466 (unsigned long long) (ctimestamp->value % 1000));
468 for (csection = ctimestamp->sections; csection; csection = csection->next) {
469 fprintf (outfile, "\t\t%s:\n", section_name (csection->value));
471 for (clist = csection->counters; clist; clist = clist->next) {
472 counter = clist->counter;
473 for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
474 if (cvalue->timestamp != ctimestamp->value)
475 continue;
477 dump_counters_value (counter, "\t\t\t%-30s", counter->name, cvalue->buffer);
482 } else if (counters_sort_mode == COUNTERS_SORT_CATEGORY) {
483 for (csection = counters_sections; csection; csection = csection->next) {
484 fprintf (outfile, "\t%s:\n", section_name (csection->value));
486 for (clist = csection->counters; clist; clist = clist->next) {
487 counter = clist->counter;
488 fprintf (outfile, "\t\t%s: [type: %s, unit: %s, variance: %s]\n",
489 counter->name, type_name (counter->type), unit_name (counter->unit), variance_name (counter->variance));
491 for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
492 snprintf (strtimestamp, sizeof (strtimestamp), "%llu:%02llu:%02llu:%02llu.%03llu",
493 (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 / 24 % 1000),
494 (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 % 24),
495 (unsigned long long) (cvalue->timestamp / 1000 / 60 % 60),
496 (unsigned long long) (cvalue->timestamp / 1000 % 60),
497 (unsigned long long) (cvalue->timestamp % 1000));
499 dump_counters_value (counter, "\t\t\t%s", strtimestamp, cvalue->buffer);
506 static int num_images;
507 typedef struct _ImageDesc ImageDesc;
508 struct _ImageDesc {
509 ImageDesc *next;
510 intptr_t image;
511 char *filename;
514 static ImageDesc* image_hash [SMALL_HASH_SIZE] = {0};
516 static void
517 add_image (intptr_t image, char *name)
519 int slot = ((image >> 2) & 0xffff) % SMALL_HASH_SIZE;
520 ImageDesc *cd = malloc (sizeof (ImageDesc));
521 cd->image = image;
522 cd->filename = pstrdup (name);
523 cd->next = image_hash [slot];
524 image_hash [slot] = cd;
525 num_images++;
528 typedef struct _BackTrace BackTrace;
529 typedef struct {
530 uint64_t count;
531 BackTrace *bt;
532 } CallContext;
534 typedef struct {
535 int count;
536 int size;
537 CallContext *traces;
538 } TraceDesc;
540 typedef struct _ClassDesc ClassDesc;
541 struct _ClassDesc {
542 ClassDesc *next;
543 intptr_t klass;
544 char *name;
545 intptr_t allocs;
546 uint64_t alloc_size;
547 TraceDesc traces;
550 static ClassDesc* class_hash [HASH_SIZE] = {0};
551 static int num_classes = 0;
553 static ClassDesc*
554 add_class (intptr_t klass, const char *name)
556 int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
557 ClassDesc *cd;
558 cd = class_hash [slot];
559 while (cd && cd->klass != klass)
560 cd = cd->next;
561 /* we resolved an unknown class (unless we had the code unloaded) */
562 if (cd) {
563 /*printf ("resolved unknown: %s\n", name);*/
564 free (cd->name);
565 cd->name = pstrdup (name);
566 return cd;
568 cd = calloc (sizeof (ClassDesc), 1);
569 cd->klass = klass;
570 cd->name = pstrdup (name);
571 cd->next = class_hash [slot];
572 cd->allocs = 0;
573 cd->alloc_size = 0;
574 cd->traces.count = 0;
575 cd->traces.size = 0;
576 cd->traces.traces = NULL;
577 class_hash [slot] = cd;
578 num_classes++;
579 return cd;
582 static ClassDesc *
583 lookup_class (intptr_t klass)
585 int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
586 ClassDesc *cd = class_hash [slot];
587 while (cd && cd->klass != klass)
588 cd = cd->next;
589 if (!cd) {
590 char buf [128];
591 snprintf (buf, sizeof (buf), "unresolved class %p", (void*)klass);
592 return add_class (klass, buf);
594 return cd;
597 typedef struct _MethodDesc MethodDesc;
598 struct _MethodDesc {
599 MethodDesc *next;
600 intptr_t method;
601 char *name;
602 intptr_t code;
603 int len;
604 int recurse_count;
605 int sample_hits;
606 int ignore_jit; /* when this is set, we collect the metadata but don't count this method fot jit time and code size, when filtering events */
607 uint64_t calls;
608 uint64_t total_time;
609 uint64_t callee_time;
610 uint64_t self_time;
611 TraceDesc traces;
614 static MethodDesc* method_hash [HASH_SIZE] = {0};
615 static int num_methods = 0;
617 static MethodDesc*
618 add_method (intptr_t method, const char *name, intptr_t code, int len)
620 int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
621 MethodDesc *cd;
622 cd = method_hash [slot];
623 while (cd && cd->method != method)
624 cd = cd->next;
625 /* we resolved an unknown method (unless we had the code unloaded) */
626 if (cd) {
627 cd->code = code;
628 cd->len = len;
629 /*printf ("resolved unknown: %s\n", name);*/
630 free (cd->name);
631 cd->name = pstrdup (name);
632 return cd;
634 cd = calloc (sizeof (MethodDesc), 1);
635 cd->method = method;
636 cd->name = pstrdup (name);
637 cd->code = code;
638 cd->len = len;
639 cd->calls = 0;
640 cd->total_time = 0;
641 cd->traces.count = 0;
642 cd->traces.size = 0;
643 cd->traces.traces = NULL;
644 cd->next = method_hash [slot];
645 method_hash [slot] = cd;
646 num_methods++;
647 return cd;
650 static MethodDesc *
651 lookup_method (intptr_t method)
653 int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
654 MethodDesc *cd = method_hash [slot];
655 while (cd && cd->method != method)
656 cd = cd->next;
657 if (!cd) {
658 char buf [128];
659 snprintf (buf, sizeof (buf), "unknown method %p", (void*)method);
660 return add_method (method, buf, 0, 0);
662 return cd;
665 static int num_stat_samples = 0;
666 static int size_stat_samples = 0;
667 uintptr_t *stat_samples = NULL;
668 int *stat_sample_desc = NULL;
670 static void
671 add_stat_sample (int type, uintptr_t ip) {
672 if (num_stat_samples == size_stat_samples) {
673 size_stat_samples *= 2;
674 if (!size_stat_samples)
675 size_stat_samples = 32;
676 stat_samples = realloc (stat_samples, size_stat_samples * sizeof (uintptr_t));
677 stat_sample_desc = realloc (stat_sample_desc, size_stat_samples * sizeof (int));
679 stat_samples [num_stat_samples] = ip;
680 stat_sample_desc [num_stat_samples++] = type;
683 static MethodDesc*
684 lookup_method_by_ip (uintptr_t ip)
686 int i;
687 MethodDesc* m;
688 /* dumb */
689 for (i = 0; i < HASH_SIZE; ++i) {
690 m = method_hash [i];
691 while (m) {
692 //printf ("checking %p against %p-%p\n", (void*)ip, (void*)(m->code), (void*)(m->code + m->len));
693 if (ip >= (uintptr_t)m->code && ip < (uintptr_t)m->code + m->len) {
694 return m;
696 m = m->next;
699 return NULL;
702 static int
703 compare_method_samples (const void *a, const void *b)
705 MethodDesc *const*A = a;
706 MethodDesc *const*B = b;
707 if ((*A)->sample_hits == (*B)->sample_hits)
708 return 0;
709 if ((*B)->sample_hits < (*A)->sample_hits)
710 return -1;
711 return 1;
714 typedef struct _UnmanagedSymbol UnmanagedSymbol;
715 struct _UnmanagedSymbol {
716 UnmanagedSymbol *parent;
717 char *name;
718 int is_binary;
719 uintptr_t addr;
720 uintptr_t size;
721 uintptr_t sample_hits;
724 static UnmanagedSymbol **usymbols = NULL;
725 static int usymbols_size = 0;
726 static int usymbols_num = 0;
728 static int
729 compare_usymbol_addr (const void *a, const void *b)
731 UnmanagedSymbol *const*A = a;
732 UnmanagedSymbol *const*B = b;
733 if ((*B)->addr == (*A)->addr)
734 return 0;
735 if ((*B)->addr > (*A)->addr)
736 return -1;
737 return 1;
740 static int
741 compare_usymbol_samples (const void *a, const void *b)
743 UnmanagedSymbol *const*A = a;
744 UnmanagedSymbol *const*B = b;
745 if ((*B)->sample_hits == (*A)->sample_hits)
746 return 0;
747 if ((*B)->sample_hits < (*A)->sample_hits)
748 return -1;
749 return 1;
752 static void
753 add_unmanaged_symbol (uintptr_t addr, char *name, uintptr_t size)
755 UnmanagedSymbol *sym;
756 if (usymbols_num == usymbols_size) {
757 int new_size = usymbols_size * 2;
758 if (!new_size)
759 new_size = 16;
760 usymbols = realloc (usymbols, sizeof (void*) * new_size);
761 usymbols_size = new_size;
763 sym = calloc (sizeof (UnmanagedSymbol), 1);
764 sym->addr = addr;
765 sym->name = name;
766 sym->size = size;
767 usymbols [usymbols_num++] = sym;
770 /* only valid after the symbols are sorted */
771 static UnmanagedSymbol*
772 lookup_unmanaged_symbol (uintptr_t addr)
774 int r = usymbols_num - 1;
775 int l = 0;
776 UnmanagedSymbol *sym;
777 int last_best = -1;
778 while (r >= l) {
779 int m = (l + r) / 2;
780 sym = usymbols [m];
781 if (addr == sym->addr)
782 return sym;
783 if (addr < sym->addr) {
784 r = m - 1;
785 } else if (addr > sym->addr) {
786 l = m + 1;
787 last_best = m;
790 if (last_best >= 0 && (addr - usymbols [last_best]->addr) < 4096)
791 return usymbols [last_best];
792 return NULL;
795 /* we use the same structure for binaries */
796 static UnmanagedSymbol **ubinaries = NULL;
797 static int ubinaries_size = 0;
798 static int ubinaries_num = 0;
800 static void
801 add_unmanaged_binary (uintptr_t addr, char *name, uintptr_t size)
803 UnmanagedSymbol *sym;
804 if (ubinaries_num == ubinaries_size) {
805 int new_size = ubinaries_size * 2;
806 if (!new_size)
807 new_size = 16;
808 ubinaries = realloc (ubinaries, sizeof (void*) * new_size);
809 ubinaries_size = new_size;
811 sym = calloc (sizeof (UnmanagedSymbol), 1);
812 sym->addr = addr;
813 sym->name = name;
814 sym->size = size;
815 sym->is_binary = 1;
816 ubinaries [ubinaries_num++] = sym;
819 static UnmanagedSymbol*
820 lookup_unmanaged_binary (uintptr_t addr)
822 int i;
823 for (i = 0; i < ubinaries_num; ++i) {
824 UnmanagedSymbol *ubin = ubinaries [i];
825 if (addr >= ubin->addr && addr < ubin->addr + ubin->size) {
826 return ubin;
829 return NULL;
832 static const char*
833 sample_type_name (int type)
835 switch (type) {
836 case SAMPLE_CYCLES: return "cycles";
837 case SAMPLE_INSTRUCTIONS: return "instructions retired";
838 case SAMPLE_CACHE_MISSES: return "cache misses";
839 case SAMPLE_CACHE_REFS: return "cache references";
840 case SAMPLE_BRANCHES: return "executed branches";
841 case SAMPLE_BRANCH_MISSES: return "unpredicted branches";
843 return "unknown";
846 static void
847 set_usym_parent (UnmanagedSymbol** cachedus, int count)
849 int i;
850 for (i = 0; i < count; ++i) {
851 UnmanagedSymbol *ubin = lookup_unmanaged_binary (cachedus [i]->addr);
852 if (ubin == cachedus [i])
853 continue;
854 cachedus [i]->parent = ubin;
858 static void
859 print_usym (UnmanagedSymbol* um)
861 if (um->parent)
862 fprintf (outfile, "\t%6zd %6.2f %-36s in %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name, um->parent->name);
863 else
864 fprintf (outfile, "\t%6zd %6.2f %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name);
867 static int
868 sym_percent (uintptr_t sample_hits)
870 double pc;
871 if (verbose)
872 return 1;
873 pc = sample_hits*100.0/num_stat_samples;
874 return pc >= 0.1;
877 static void
878 dump_samples (void)
880 int i, u;
881 int count = 0, msize = 0;
882 int unmanaged_hits = 0;
883 int unresolved_hits = 0;
884 MethodDesc** cachedm = NULL;
885 int ucount = 0, usize = 0;
886 UnmanagedSymbol** cachedus = NULL;
887 if (!num_stat_samples)
888 return;
889 qsort (usymbols, usymbols_num, sizeof (UnmanagedSymbol*), compare_usymbol_addr);
890 for (i = 0; i < num_stat_samples; ++i) {
891 MethodDesc *m = lookup_method_by_ip (stat_samples [i]);
892 if (m) {
893 if (!m->sample_hits) {
894 if (count == msize) {
895 msize *= 2;
896 if (!msize)
897 msize = 4;
898 cachedm = realloc (cachedm, sizeof (void*) * msize);
900 cachedm [count++] = m;
902 m->sample_hits++;
903 } else {
904 UnmanagedSymbol *usym = lookup_unmanaged_symbol (stat_samples [i]);
905 if (!usym) {
906 unresolved_hits++;
907 //printf ("unmanaged hit at %p\n", (void*)stat_samples [i]);
908 usym = lookup_unmanaged_binary (stat_samples [i]);
910 if (usym) {
911 if (!usym->sample_hits) {
912 if (ucount == usize) {
913 usize *= 2;
914 if (!usize)
915 usize = 4;
916 cachedus = realloc (cachedus, sizeof (void*) * usize);
918 cachedus [ucount++] = usym;
920 usym->sample_hits++;
922 unmanaged_hits++;
925 qsort (cachedm, count, sizeof (MethodDesc*), compare_method_samples);
926 qsort (cachedus, ucount, sizeof (UnmanagedSymbol*), compare_usymbol_samples);
927 set_usym_parent (cachedus, ucount);
928 fprintf (outfile, "\nStatistical samples summary\n");
929 fprintf (outfile, "\tSample type: %s\n", sample_type_name (stat_sample_desc [0]));
930 fprintf (outfile, "\tUnmanaged hits: %6d (%4.1f%%)\n", unmanaged_hits, (100.0*unmanaged_hits)/num_stat_samples);
931 fprintf (outfile, "\tManaged hits: %6d (%4.1f%%)\n", num_stat_samples - unmanaged_hits, (100.0*(num_stat_samples-unmanaged_hits))/num_stat_samples);
932 fprintf (outfile, "\tUnresolved hits: %6d (%4.1f%%)\n", unresolved_hits, (100.0*unresolved_hits)/num_stat_samples);
933 fprintf (outfile, "\t%6s %6s %s\n", "Hits", "%", "Method name");
934 i = 0;
935 u = 0;
936 while (i < count || u < ucount) {
937 if (i < count) {
938 MethodDesc *m = cachedm [i];
939 if (u < ucount) {
940 UnmanagedSymbol *um = cachedus [u];
941 if (um->sample_hits > m->sample_hits) {
942 if (!sym_percent (um->sample_hits))
943 break;
944 print_usym (um);
945 u++;
946 continue;
949 if (!sym_percent (m->sample_hits))
950 break;
951 fprintf (outfile, "\t%6d %6.2f %s\n", m->sample_hits, m->sample_hits*100.0/num_stat_samples, m->name);
952 i++;
953 continue;
955 if (u < ucount) {
956 UnmanagedSymbol *um = cachedus [u];
957 if (!sym_percent (um->sample_hits))
958 break;
959 print_usym (um);
960 u++;
961 continue;
966 typedef struct _HeapClassDesc HeapClassDesc;
967 typedef struct {
968 HeapClassDesc *klass;
969 uint64_t count;
970 } HeapClassRevRef;
972 struct _HeapClassDesc {
973 ClassDesc *klass;
974 int64_t count;
975 int64_t total_size;
976 HeapClassRevRef *rev_hash;
977 int rev_hash_size;
978 int rev_count;
979 uintptr_t pinned_references;
980 uintptr_t root_references;
983 static int
984 add_rev_class_hashed (HeapClassRevRef *rev_hash, uintptr_t size, HeapClassDesc *hklass, uint64_t value)
986 uintptr_t i;
987 uintptr_t start_pos;
988 start_pos = (hklass->klass->klass >> 2) % size;
989 assert (start_pos < size);
990 i = start_pos;
991 do {
992 if (rev_hash [i].klass == hklass) {
993 rev_hash [i].count += value;
994 return 0;
995 } else if (!rev_hash [i].klass) {
996 rev_hash [i].klass = hklass;
997 rev_hash [i].count += value;
998 start_pos = 0;
999 for (i = 0; i < size; ++i)
1000 if (rev_hash [i].klass && rev_hash [i].klass->klass == hklass->klass)
1001 start_pos ++;
1002 assert (start_pos == 1);
1003 return 1;
1005 /* wrap around */
1006 if (++i == size)
1007 i = 0;
1008 } while (i != start_pos);
1009 /* should not happen */
1010 printf ("failed revref store\n");
1011 return 0;
1014 static void
1015 add_heap_class_rev (HeapClassDesc *from, HeapClassDesc *to)
1017 uintptr_t i;
1018 if (to->rev_count * 2 >= to->rev_hash_size) {
1019 HeapClassRevRef *n;
1020 uintptr_t old_size = to->rev_hash_size;
1021 to->rev_hash_size *= 2;
1022 if (to->rev_hash_size == 0)
1023 to->rev_hash_size = 4;
1024 n = calloc (sizeof (HeapClassRevRef) * to->rev_hash_size, 1);
1025 for (i = 0; i < old_size; ++i) {
1026 if (to->rev_hash [i].klass)
1027 add_rev_class_hashed (n, to->rev_hash_size, to->rev_hash [i].klass, to->rev_hash [i].count);
1029 if (to->rev_hash)
1030 free (to->rev_hash);
1031 to->rev_hash = n;
1033 to->rev_count += add_rev_class_hashed (to->rev_hash, to->rev_hash_size, from, 1);
1036 typedef struct {
1037 uintptr_t objaddr;
1038 HeapClassDesc *hklass;
1039 uintptr_t num_refs;
1040 uintptr_t refs [0];
1041 } HeapObjectDesc;
1043 typedef struct _HeapShot HeapShot;
1044 struct _HeapShot {
1045 HeapShot *next;
1046 uint64_t timestamp;
1047 int class_count;
1048 int hash_size;
1049 HeapClassDesc **class_hash;
1050 HeapClassDesc **sorted;
1051 HeapObjectDesc **objects_hash;
1052 uintptr_t objects_count;
1053 uintptr_t objects_hash_size;
1054 uintptr_t num_roots;
1055 uintptr_t *roots;
1056 uintptr_t *roots_extra;
1057 int *roots_types;
1060 static HeapShot *heap_shots = NULL;
1061 static int num_heap_shots = 0;
1063 static HeapShot*
1064 new_heap_shot (uint64_t timestamp)
1066 HeapShot *hs = calloc (sizeof (HeapShot), 1);
1067 hs->hash_size = 4;
1068 hs->class_hash = calloc (sizeof (void*), hs->hash_size);
1069 hs->timestamp = timestamp;
1070 num_heap_shots++;
1071 hs->next = heap_shots;
1072 heap_shots = hs;
1073 return hs;
1076 static HeapClassDesc*
1077 heap_class_lookup (HeapShot *hs, ClassDesc *klass)
1079 int i;
1080 unsigned int start_pos;
1081 start_pos = ((uintptr_t)klass->klass >> 2) % hs->hash_size;
1082 i = start_pos;
1083 do {
1084 HeapClassDesc* cd = hs->class_hash [i];
1085 if (!cd)
1086 return NULL;
1087 if (cd->klass == klass)
1088 return cd;
1089 /* wrap around */
1090 if (++i == hs->hash_size)
1091 i = 0;
1092 } while (i != start_pos);
1093 return NULL;
1096 static int
1097 add_heap_hashed (HeapClassDesc **hash, HeapClassDesc **retv, uintptr_t hsize, ClassDesc *klass, uint64_t size, uint64_t count)
1099 uintptr_t i;
1100 uintptr_t start_pos;
1101 start_pos = ((uintptr_t)klass->klass >> 2) % hsize;
1102 i = start_pos;
1103 do {
1104 if (hash [i] && hash [i]->klass == klass) {
1105 hash [i]->total_size += size;
1106 hash [i]->count += count;
1107 *retv = hash [i];
1108 return 0;
1109 } else if (!hash [i]) {
1110 if (*retv) {
1111 hash [i] = *retv;
1112 return 1;
1114 hash [i] = calloc (sizeof (HeapClassDesc), 1);
1115 hash [i]->klass = klass;
1116 hash [i]->total_size += size;
1117 hash [i]->count += count;
1118 *retv = hash [i];
1119 return 1;
1121 /* wrap around */
1122 if (++i == hsize)
1123 i = 0;
1124 } while (i != start_pos);
1125 /* should not happen */
1126 printf ("failed heap class store\n");
1127 return 0;
1130 static HeapClassDesc*
1131 add_heap_shot_class (HeapShot *hs, ClassDesc *klass, uint64_t size)
1133 HeapClassDesc *res;
1134 int i;
1135 if (hs->class_count * 2 >= hs->hash_size) {
1136 HeapClassDesc **n;
1137 int old_size = hs->hash_size;
1138 hs->hash_size *= 2;
1139 if (hs->hash_size == 0)
1140 hs->hash_size = 4;
1141 n = calloc (sizeof (void*) * hs->hash_size, 1);
1142 for (i = 0; i < old_size; ++i) {
1143 res = hs->class_hash [i];
1144 if (hs->class_hash [i])
1145 add_heap_hashed (n, &res, hs->hash_size, hs->class_hash [i]->klass, hs->class_hash [i]->total_size, hs->class_hash [i]->count);
1147 if (hs->class_hash)
1148 free (hs->class_hash);
1149 hs->class_hash = n;
1151 res = NULL;
1152 hs->class_count += add_heap_hashed (hs->class_hash, &res, hs->hash_size, klass, size, 1);
1153 //if (res->count == 1)
1154 // printf ("added heap class: %s\n", res->klass->name);
1155 return res;
1158 static HeapObjectDesc*
1159 alloc_heap_obj (uintptr_t objaddr, HeapClassDesc *hklass, uintptr_t num_refs)
1161 HeapObjectDesc* ho = calloc (sizeof (HeapObjectDesc) + num_refs * sizeof (uintptr_t), 1);
1162 ho->objaddr = objaddr;
1163 ho->hklass = hklass;
1164 ho->num_refs = num_refs;
1165 return ho;
1168 static uintptr_t
1169 heap_shot_find_obj_slot (HeapShot *hs, uintptr_t objaddr)
1171 uintptr_t i;
1172 uintptr_t start_pos;
1173 HeapObjectDesc **hash = hs->objects_hash;
1174 start_pos = ((uintptr_t)objaddr >> 3) % hs->objects_hash_size;
1175 i = start_pos;
1176 do {
1177 if (hash [i] && hash [i]->objaddr == objaddr) {
1178 return i;
1179 } else if (!hash [i]) {
1180 break; /* fail */
1182 /* wrap around */
1183 if (++i == hs->objects_hash_size)
1184 i = 0;
1185 } while (i != start_pos);
1186 /* should not happen */
1187 //printf ("failed heap obj slot\n");
1188 return -1;
1191 static HeapObjectDesc*
1192 heap_shot_obj_add_refs (HeapShot *hs, uintptr_t objaddr, uintptr_t num, uintptr_t *ref_offset)
1194 HeapObjectDesc **hash = hs->objects_hash;
1195 uintptr_t i = heap_shot_find_obj_slot (hs, objaddr);
1196 if (i >= 0) {
1197 HeapObjectDesc* ho = alloc_heap_obj (objaddr, hash [i]->hklass, hash [i]->num_refs + num);
1198 *ref_offset = hash [i]->num_refs;
1199 memcpy (ho->refs, hash [i]->refs, hash [i]->num_refs * sizeof (uintptr_t));
1200 free (hash [i]);
1201 hash [i] = ho;
1202 return ho;
1204 /* should not happen */
1205 printf ("failed heap obj update\n");
1206 return NULL;
1210 static uintptr_t
1211 add_heap_hashed_obj (HeapObjectDesc **hash, uintptr_t hsize, HeapObjectDesc *obj)
1213 uintptr_t i;
1214 uintptr_t start_pos;
1215 start_pos = ((uintptr_t)obj->objaddr >> 3) % hsize;
1216 i = start_pos;
1217 do {
1218 if (hash [i] && hash [i]->objaddr == obj->objaddr) {
1219 printf ("duplicate object!\n");
1220 return 0;
1221 } else if (!hash [i]) {
1222 hash [i] = obj;
1223 return 1;
1225 /* wrap around */
1226 if (++i == hsize)
1227 i = 0;
1228 } while (i != start_pos);
1229 /* should not happen */
1230 printf ("failed heap obj store\n");
1231 return 0;
1234 static void
1235 add_heap_shot_obj (HeapShot *hs, HeapObjectDesc *obj)
1237 uintptr_t i;
1238 if (hs->objects_count * 2 >= hs->objects_hash_size) {
1239 HeapObjectDesc **n;
1240 uintptr_t old_size = hs->objects_hash_size;
1241 hs->objects_hash_size *= 2;
1242 if (hs->objects_hash_size == 0)
1243 hs->objects_hash_size = 4;
1244 n = calloc (sizeof (void*) * hs->objects_hash_size, 1);
1245 for (i = 0; i < old_size; ++i) {
1246 if (hs->objects_hash [i])
1247 add_heap_hashed_obj (n, hs->objects_hash_size, hs->objects_hash [i]);
1249 if (hs->objects_hash)
1250 free (hs->objects_hash);
1251 hs->objects_hash = n;
1253 hs->objects_count += add_heap_hashed_obj (hs->objects_hash, hs->objects_hash_size, obj);
1256 static void
1257 heap_shot_resolve_reverse_refs (HeapShot *hs)
1259 uintptr_t i;
1260 for (i = 0; i < hs->objects_hash_size; ++i) {
1261 uintptr_t r;
1262 HeapObjectDesc *ho = hs->objects_hash [i];
1263 if (!ho)
1264 continue;
1265 for (r = 0; r < ho->num_refs; ++r) {
1266 uintptr_t oi = heap_shot_find_obj_slot (hs, ho->refs [r]);
1267 add_heap_class_rev (ho->hklass, hs->objects_hash [oi]->hklass);
1272 #define MARK_GRAY 1
1273 #define MARK_BLACK 2
1275 static void
1276 heap_shot_mark_objects (HeapShot *hs)
1278 uintptr_t i, oi, r;
1279 unsigned char *marks;
1280 HeapObjectDesc *obj, *ref;
1281 int marked_some;
1282 uintptr_t num_marked = 0, num_unmarked;
1283 for (i = 0; i < hs->num_roots; ++i) {
1284 HeapClassDesc *cd;
1285 oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
1286 if (oi == -1) {
1287 continue;
1289 obj = hs->objects_hash [oi];
1290 cd = obj->hklass;
1291 if (hs->roots_types [i] & MONO_PROFILE_GC_ROOT_PINNING)
1292 cd->pinned_references++;
1293 cd->root_references++;
1295 if (!debug)
1296 return;
1297 /* consistency checks: it seems not all the objects are walked in the heap in some cases */
1298 marks = calloc (hs->objects_hash_size, 1);
1299 if (!marks)
1300 return;
1301 for (i = 0; i < hs->num_roots; ++i) {
1302 oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
1303 if (oi == -1) {
1304 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);
1305 continue;
1307 obj = hs->objects_hash [oi];
1308 if (!marks [oi]) {
1309 marks [oi] = obj->num_refs? MARK_GRAY: MARK_BLACK;
1310 num_marked++;
1313 marked_some = 1;
1314 while (marked_some) {
1315 marked_some = 0;
1316 for (i = 0; i < hs->objects_hash_size; ++i) {
1317 if (marks [i] != MARK_GRAY)
1318 continue;
1319 marks [i] = MARK_BLACK;
1320 obj = hs->objects_hash [i];
1321 for (r = 0; r < obj->num_refs; ++r) {
1322 oi = heap_shot_find_obj_slot (hs, obj->refs [r]);
1323 if (oi == -1) {
1324 fprintf (outfile, "referenced obj %p not found in heap\n", (void*)obj->refs [r]);
1325 continue;
1327 ref = hs->objects_hash [oi];
1328 if (!marks [oi]) {
1329 marks [oi] = ref->num_refs? MARK_GRAY: MARK_BLACK;
1332 marked_some++;
1336 num_unmarked = 0;
1337 for (i = 0; i < hs->objects_hash_size; ++i) {
1338 if (hs->objects_hash [i] && !marks [i]) {
1339 num_unmarked++;
1340 fprintf (outfile, "object %p (%s) unmarked\n", (void*)hs->objects_hash [i], hs->objects_hash [i]->hklass->klass->name);
1343 fprintf (outfile, "Total unmarked: %zd/%zd\n", num_unmarked, hs->objects_count);
1344 free (marks);
1347 static void
1348 heap_shot_free_objects (HeapShot *hs)
1350 uintptr_t i;
1351 for (i = 0; i < hs->objects_hash_size; ++i) {
1352 HeapObjectDesc *ho = hs->objects_hash [i];
1353 if (ho)
1354 free (ho);
1356 if (hs->objects_hash)
1357 free (hs->objects_hash);
1358 hs->objects_hash = NULL;
1359 hs->objects_hash_size = 0;
1360 hs->objects_count = 0;
1364 struct _BackTrace {
1365 BackTrace *next;
1366 unsigned int hash;
1367 int count;
1368 int id;
1369 MethodDesc *methods [1];
1372 static BackTrace *backtrace_hash [HASH_SIZE];
1373 static BackTrace **backtraces = NULL;
1374 static int num_backtraces = 0;
1375 static int next_backtrace = 0;
1377 static int
1378 hash_backtrace (int count, MethodDesc **methods)
1380 int hash = count;
1381 int i;
1382 for (i = 0; i < count; ++i) {
1383 hash = (hash << 5) - hash + methods [i]->method;
1385 return hash;
1388 static int
1389 compare_backtrace (BackTrace *bt, int count, MethodDesc **methods)
1391 int i;
1392 if (bt->count != count)
1393 return 0;
1394 for (i = 0; i < count; ++i)
1395 if (methods [i] != bt->methods [i])
1396 return 0;
1397 return 1;
1400 static BackTrace*
1401 add_backtrace (int count, MethodDesc **methods)
1403 int hash = hash_backtrace (count, methods);
1404 int slot = (hash & 0xffff) % HASH_SIZE;
1405 BackTrace *bt = backtrace_hash [slot];
1406 while (bt) {
1407 if (bt->hash == hash && compare_backtrace (bt, count, methods))
1408 return bt;
1409 bt = bt->next;
1411 bt = malloc (sizeof (BackTrace) + ((count - 1) * sizeof (void*)));
1412 bt->next = backtrace_hash [slot];
1413 backtrace_hash [slot] = bt;
1414 if (next_backtrace == num_backtraces) {
1415 num_backtraces *= 2;
1416 if (!num_backtraces)
1417 num_backtraces = 16;
1418 backtraces = realloc (backtraces, sizeof (void*) * num_backtraces);
1420 bt->id = next_backtrace++;
1421 backtraces [bt->id] = bt;
1422 bt->count = count;
1423 bt->hash = hash;
1424 for (slot = 0; slot < count; ++slot)
1425 bt->methods [slot] = methods [slot];
1427 return bt;
1430 typedef struct _MonitorDesc MonitorDesc;
1431 typedef struct _ThreadContext ThreadContext;
1433 typedef struct {
1434 FILE *file;
1435 #if defined (HAVE_SYS_ZLIB)
1436 gzFile gzfile;
1437 #endif
1438 unsigned char *buf;
1439 int size;
1440 int data_version;
1441 int version_major;
1442 int version_minor;
1443 int timer_overhead;
1444 int pid;
1445 int port;
1446 uint64_t startup_time;
1447 ThreadContext *threads;
1448 ThreadContext *current;
1449 } ProfContext;
1451 struct _ThreadContext {
1452 ThreadContext *next;
1453 intptr_t thread_id;
1454 char *name;
1455 /* emulated stack */
1456 MethodDesc **stack;
1457 uint64_t *time_stack;
1458 uint64_t *callee_time_stack;
1459 uint64_t last_time;
1460 uint64_t contention_start;
1461 MonitorDesc *monitor;
1462 int stack_size;
1463 int stack_id;
1464 HeapShot *current_heap_shot;
1465 uintptr_t num_roots;
1466 uintptr_t size_roots;
1467 uintptr_t *roots;
1468 uintptr_t *roots_extra;
1469 int *roots_types;
1470 uint64_t gc_start_times [3];
1473 static void
1474 ensure_buffer (ProfContext *ctx, int size)
1476 if (ctx->size < size) {
1477 ctx->buf = realloc (ctx->buf, size);
1478 ctx->size = size;
1482 static int
1483 load_data (ProfContext *ctx, int size)
1485 ensure_buffer (ctx, size);
1486 #if defined (HAVE_SYS_ZLIB)
1487 if (ctx->gzfile) {
1488 int r = gzread (ctx->gzfile, ctx->buf, size);
1489 if (r == 0)
1490 return size == 0? 1: 0;
1491 return r == size;
1492 } else
1493 #endif
1495 int r = fread (ctx->buf, size, 1, ctx->file);
1496 if (r == 0)
1497 return size == 0? 1: 0;
1498 return r;
1502 static ThreadContext*
1503 get_thread (ProfContext *ctx, intptr_t thread_id)
1505 ThreadContext *thread;
1506 if (ctx->current && ctx->current->thread_id == thread_id)
1507 return ctx->current;
1508 thread = ctx->threads;
1509 while (thread) {
1510 if (thread->thread_id == thread_id) {
1511 return thread;
1513 thread = thread->next;
1515 thread = calloc (sizeof (ThreadContext), 1);
1516 thread->next = ctx->threads;
1517 ctx->threads = thread;
1518 thread->thread_id = thread_id;
1519 thread->last_time = 0;
1520 thread->stack_id = 0;
1521 thread->stack_size = 32;
1522 thread->stack = malloc (thread->stack_size * sizeof (void*));
1523 thread->time_stack = malloc (thread->stack_size * sizeof (uint64_t));
1524 thread->callee_time_stack = malloc (thread->stack_size * sizeof (uint64_t));
1525 return thread;
1528 static ThreadContext*
1529 load_thread (ProfContext *ctx, intptr_t thread_id)
1531 ThreadContext *thread = get_thread (ctx, thread_id);
1532 ctx->current = thread;
1533 return thread;
1536 static void
1537 ensure_thread_stack (ThreadContext *thread)
1539 if (thread->stack_id == thread->stack_size) {
1540 thread->stack_size *= 2;
1541 thread->stack = realloc (thread->stack, thread->stack_size * sizeof (void*));
1542 thread->time_stack = realloc (thread->time_stack, thread->stack_size * sizeof (uint64_t));
1543 thread->callee_time_stack = realloc (thread->callee_time_stack, thread->stack_size * sizeof (uint64_t));
1547 static int
1548 add_trace_hashed (CallContext *traces, int size, BackTrace *bt, uint64_t value)
1550 int i;
1551 unsigned int start_pos;
1552 start_pos = bt->hash % size;
1553 i = start_pos;
1554 do {
1555 if (traces [i].bt == bt) {
1556 traces [i].count += value;
1557 return 0;
1558 } else if (!traces [i].bt) {
1559 traces [i].bt = bt;
1560 traces [i].count += value;
1561 return 1;
1563 /* wrap around */
1564 if (++i == size)
1565 i = 0;
1566 } while (i != start_pos);
1567 /* should not happen */
1568 printf ("failed trace store\n");
1569 return 0;
1572 static void
1573 add_trace_bt (BackTrace *bt, TraceDesc *trace, uint64_t value)
1575 int i;
1576 if (!collect_traces)
1577 return;
1578 if (trace->count * 2 >= trace->size) {
1579 CallContext *n;
1580 int old_size = trace->size;
1581 trace->size *= 2;
1582 if (trace->size == 0)
1583 trace->size = 4;
1584 n = calloc (sizeof (CallContext) * trace->size, 1);
1585 for (i = 0; i < old_size; ++i) {
1586 if (trace->traces [i].bt)
1587 add_trace_hashed (n, trace->size, trace->traces [i].bt, trace->traces [i].count);
1589 if (trace->traces)
1590 free (trace->traces);
1591 trace->traces = n;
1593 trace->count += add_trace_hashed (trace->traces, trace->size, bt, value);
1596 static BackTrace*
1597 add_trace_thread (ThreadContext *thread, TraceDesc *trace, uint64_t value)
1599 BackTrace *bt;
1600 int count = thread->stack_id;
1601 if (!collect_traces)
1602 return NULL;
1603 if (count > trace_max)
1604 count = trace_max;
1605 bt = add_backtrace (count, thread->stack + thread->stack_id - count);
1606 add_trace_bt (bt, trace, value);
1607 return bt;
1610 static BackTrace*
1611 add_trace_methods (MethodDesc **methods, int count, TraceDesc *trace, uint64_t value)
1613 BackTrace *bt;
1614 if (!collect_traces)
1615 return NULL;
1616 if (count > trace_max)
1617 count = trace_max;
1618 bt = add_backtrace (count, methods);
1619 add_trace_bt (bt, trace, value);
1620 return bt;
1623 static void
1624 thread_add_root (ThreadContext *ctx, uintptr_t obj, int root_type, uintptr_t extra_info)
1626 if (ctx->num_roots == ctx->size_roots) {
1627 int new_size = ctx->size_roots * 2;
1628 if (!new_size)
1629 new_size = 4;
1630 ctx->roots = realloc (ctx->roots, new_size * sizeof (uintptr_t));
1631 ctx->roots_extra = realloc (ctx->roots_extra, new_size * sizeof (uintptr_t));
1632 ctx->roots_types = realloc (ctx->roots_types, new_size * sizeof (int));
1633 ctx->size_roots = new_size;
1635 ctx->roots_types [ctx->num_roots] = root_type;
1636 ctx->roots_extra [ctx->num_roots] = extra_info;
1637 ctx->roots [ctx->num_roots++] = obj;
1640 static int
1641 compare_callc (const void *a, const void *b)
1643 const CallContext *A = a;
1644 const CallContext *B = b;
1645 if (B->count == A->count)
1646 return 0;
1647 if (B->count < A->count)
1648 return -1;
1649 return 1;
1652 static void
1653 sort_context_array (TraceDesc* traces)
1655 int i, j;
1656 for (i = 0, j = 0; i < traces->size; ++i) {
1657 if (traces->traces [i].bt) {
1658 traces->traces [j].bt = traces->traces [i].bt;
1659 traces->traces [j].count = traces->traces [i].count;
1660 j++;
1663 qsort (traces->traces, traces->count, sizeof (CallContext), compare_callc);
1666 static void
1667 push_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
1669 ensure_thread_stack (thread);
1670 thread->time_stack [thread->stack_id] = timestamp;
1671 thread->callee_time_stack [thread->stack_id] = 0;
1672 thread->stack [thread->stack_id++] = method;
1673 method->recurse_count++;
1676 static void
1677 pop_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
1679 method->recurse_count--;
1680 if (thread->stack_id > 0 && thread->stack [thread->stack_id - 1] == method) {
1681 uint64_t tdiff;
1682 thread->stack_id--;
1683 method->calls++;
1684 if (timestamp < thread->time_stack [thread->stack_id])
1685 fprintf (outfile, "time went backwards for %s\n", method->name);
1686 tdiff = timestamp - thread->time_stack [thread->stack_id];
1687 if (thread->callee_time_stack [thread->stack_id] > tdiff)
1688 fprintf (outfile, "callee time bigger for %s\n", method->name);
1689 method->self_time += tdiff - thread->callee_time_stack [thread->stack_id];
1690 method->callee_time += thread->callee_time_stack [thread->stack_id];
1691 if (thread->stack_id)
1692 thread->callee_time_stack [thread->stack_id - 1] += tdiff;
1693 //fprintf (outfile, "method %s took %d\n", method->name, (int)(tdiff/1000));
1694 } else {
1695 fprintf (outfile, "unmatched leave at stack pos: %d for method %s\n", thread->stack_id, method->name);
1699 typedef struct {
1700 uint64_t total_time;
1701 uint64_t max_time;
1702 int count;
1703 } GCDesc;
1704 static GCDesc gc_info [3];
1705 static uint64_t max_heap_size;
1706 static uint64_t gc_object_moves;
1707 static int gc_resizes;
1708 typedef struct {
1709 uint64_t created;
1710 uint64_t destroyed;
1711 uint64_t live;
1712 uint64_t max_live;
1713 TraceDesc traces;
1714 } HandleInfo;
1715 static HandleInfo handle_info [4];
1717 static const char*
1718 gc_event_name (int ev)
1720 switch (ev) {
1721 case MONO_GC_EVENT_START: return "start";
1722 case MONO_GC_EVENT_MARK_START: return "mark start";
1723 case MONO_GC_EVENT_MARK_END: return "mark end";
1724 case MONO_GC_EVENT_RECLAIM_START: return "reclaim start";
1725 case MONO_GC_EVENT_RECLAIM_END: return "reclaim end";
1726 case MONO_GC_EVENT_END: return "end";
1727 case MONO_GC_EVENT_PRE_STOP_WORLD: return "pre stop";
1728 case MONO_GC_EVENT_POST_STOP_WORLD: return "post stop";
1729 case MONO_GC_EVENT_PRE_START_WORLD: return "pre start";
1730 case MONO_GC_EVENT_POST_START_WORLD: return "post start";
1731 default:
1732 return "unknown";
1736 static uint64_t clause_summary [MONO_EXCEPTION_CLAUSE_FAULT + 1];
1737 static uint64_t throw_count = 0;
1738 static TraceDesc exc_traces;
1740 static const char*
1741 clause_name (int type)
1743 switch (type) {
1744 case MONO_EXCEPTION_CLAUSE_NONE: return "catch";
1745 case MONO_EXCEPTION_CLAUSE_FILTER: return "filter";
1746 case MONO_EXCEPTION_CLAUSE_FINALLY: return "finally";
1747 case MONO_EXCEPTION_CLAUSE_FAULT: return "fault";
1748 default: return "invalid";
1752 static uint64_t monitor_contention;
1753 static uint64_t monitor_failed;
1754 static uint64_t monitor_acquired;
1756 struct _MonitorDesc {
1757 MonitorDesc *next;
1758 uintptr_t objid;
1759 uintptr_t contentions;
1760 uint64_t wait_time;
1761 uint64_t max_wait_time;
1762 TraceDesc traces;
1765 static MonitorDesc* monitor_hash [SMALL_HASH_SIZE] = {0};
1766 static int num_monitors = 0;
1768 static MonitorDesc*
1769 lookup_monitor (uintptr_t objid)
1771 int slot = ((objid >> 3) & 0xffff) % SMALL_HASH_SIZE;
1772 MonitorDesc *cd = monitor_hash [slot];
1773 while (cd && cd->objid != objid)
1774 cd = cd->next;
1775 if (!cd) {
1776 cd = calloc (sizeof (MonitorDesc), 1);
1777 cd->objid = objid;
1778 cd->next = monitor_hash [slot];
1779 monitor_hash [slot] = cd;
1780 num_monitors++;
1782 return cd;
1785 static const char*
1786 monitor_ev_name (int ev)
1788 switch (ev) {
1789 case MONO_PROFILER_MONITOR_CONTENTION: return "contended";
1790 case MONO_PROFILER_MONITOR_DONE: return "acquired";
1791 case MONO_PROFILER_MONITOR_FAIL: return "not taken";
1792 default: return "invalid";
1796 static const char*
1797 get_handle_name (int htype)
1799 switch (htype) {
1800 case 0: return "weak";
1801 case 1: return "weaktrack";
1802 case 2: return "normal";
1803 case 3: return "pinned";
1804 default: return "unknown";
1808 static const char*
1809 get_root_name (int rtype)
1811 switch (rtype & MONO_PROFILE_GC_ROOT_TYPEMASK) {
1812 case MONO_PROFILE_GC_ROOT_STACK: return "stack";
1813 case MONO_PROFILE_GC_ROOT_FINALIZER: return "finalizer";
1814 case MONO_PROFILE_GC_ROOT_HANDLE: return "handle";
1815 case MONO_PROFILE_GC_ROOT_OTHER: return "other";
1816 case MONO_PROFILE_GC_ROOT_MISC: return "misc";
1817 default: return "unknown";
1821 static MethodDesc**
1822 decode_bt (MethodDesc** sframes, int *size, unsigned char *p, unsigned char **endp, intptr_t ptr_base)
1824 MethodDesc **frames;
1825 int i;
1826 int flags = decode_uleb128 (p, &p);
1827 int count = decode_uleb128 (p, &p);
1828 if (flags != 0)
1829 return NULL;
1830 if (count > *size)
1831 frames = malloc (count * sizeof (void*));
1832 else
1833 frames = sframes;
1834 for (i = 0; i < count; ++i) {
1835 intptr_t ptrdiff = decode_sleb128 (p, &p);
1836 frames [i] = lookup_method (ptr_base + ptrdiff);
1838 *size = count;
1839 *endp = p;
1840 return frames;
1843 static void
1844 tracked_creation (uintptr_t obj, ClassDesc *cd, uint64_t size, BackTrace *bt, uint64_t timestamp)
1846 int i;
1847 for (i = 0; i < num_tracked_objects; ++i) {
1848 if (tracked_objects [i] != obj)
1849 continue;
1850 fprintf (outfile, "Object %p created (%s, %llu bytes) at %.3f secs.\n", (void*)obj, cd->name, (unsigned long long) size, (timestamp - startup_time)/1000000000.0);
1851 if (bt && bt->count) {
1852 int k;
1853 for (k = 0; k < bt->count; ++k)
1854 fprintf (outfile, "\t%s\n", bt->methods [k]->name);
1859 static void
1860 track_handle (uintptr_t obj, int htype, uint32_t handle)
1862 int i;
1863 for (i = 0; i < num_tracked_objects; ++i) {
1864 if (tracked_objects [i] == obj)
1865 fprintf (outfile, "Object %p referenced from handle %u\n", (void*)obj, handle);
1869 static void
1870 track_move (uintptr_t src, uintptr_t dst)
1872 int i;
1873 for (i = 0; i < num_tracked_objects; ++i) {
1874 if (tracked_objects [i] == src)
1875 fprintf (outfile, "Object %p moved to %p\n", (void*)src, (void*)dst);
1876 else if (tracked_objects [i] == dst)
1877 fprintf (outfile, "Object %p moved from %p\n", (void*)dst, (void*)src);
1881 static void
1882 track_obj_reference (uintptr_t obj, uintptr_t parent, ClassDesc *cd)
1884 int i;
1885 for (i = 0; i < num_tracked_objects; ++i) {
1886 if (tracked_objects [i] == obj)
1887 fprintf (outfile, "Object %p referenced from %p (%s).\n", (void*)obj, (void*)parent, cd->name);
1891 static void
1892 found_object (uintptr_t obj)
1894 num_tracked_objects ++;
1895 tracked_objects = realloc (tracked_objects, num_tracked_objects * sizeof (tracked_objects [0]));
1896 tracked_objects [num_tracked_objects - 1] = obj;
1899 #define OBJ_ADDR(diff) ((obj_base + diff) << 3)
1900 #define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
1902 static int
1903 decode_buffer (ProfContext *ctx)
1905 unsigned char *p;
1906 unsigned char *end;
1907 intptr_t thread_id;
1908 intptr_t ptr_base;
1909 intptr_t obj_base;
1910 intptr_t method_base;
1911 uint64_t time_base;
1912 uint64_t file_offset;
1913 int len, i;
1914 ThreadContext *thread;
1916 #ifdef HAVE_SYS_ZLIB
1917 if (ctx->gzfile)
1918 file_offset = gztell (ctx->gzfile);
1919 else
1920 #endif
1921 file_offset = ftell (ctx->file);
1922 if (!load_data (ctx, 48))
1923 return 0;
1924 p = ctx->buf;
1925 if (read_int32 (p) != BUF_ID) {
1926 fprintf (outfile, "Incorrect buffer id: 0x%x\n", read_int32 (p));
1927 for (i = 0; i < 48; ++i) {
1928 fprintf (outfile, "0x%x%s", p [i], i % 8?" ":"\n");
1930 return 0;
1932 len = read_int32 (p + 4);
1933 time_base = read_int64 (p + 8);
1934 ptr_base = read_int64 (p + 16);
1935 obj_base = read_int64 (p + 24);
1936 thread_id = read_int64 (p + 32);
1937 method_base = read_int64 (p + 40);
1938 if (debug)
1939 fprintf (outfile, "buf: thread:%zx, len: %d, time: %llu, file offset: %llu\n", thread_id, len, (unsigned long long) time_base, (unsigned long long) file_offset);
1940 thread = load_thread (ctx, thread_id);
1941 if (!load_data (ctx, len))
1942 return 0;
1943 if (!startup_time) {
1944 startup_time = time_base;
1945 if (use_time_filter) {
1946 time_from += startup_time;
1947 time_to += startup_time;
1949 if (!thread->name)
1950 thread->name = pstrdup ("Main");
1952 for (i = 0; i < thread->stack_id; ++i)
1953 thread->stack [i]->recurse_count++;
1954 p = ctx->buf;
1955 end = p + len;
1956 while (p < end) {
1957 switch (*p & 0xf) {
1958 case TYPE_GC: {
1959 int subtype = *p & 0xf0;
1960 uint64_t tdiff = decode_uleb128 (p + 1, &p);
1961 LOG_TIME (time_base, tdiff);
1962 time_base += tdiff;
1963 if (subtype == TYPE_GC_RESIZE) {
1964 uint64_t new_size = decode_uleb128 (p, &p);
1965 if (debug)
1966 fprintf (outfile, "gc heap resized to %llu\n", (unsigned long long) new_size);
1967 gc_resizes++;
1968 if (new_size > max_heap_size)
1969 max_heap_size = new_size;
1970 } else if (subtype == TYPE_GC_EVENT) {
1971 uint64_t ev = decode_uleb128 (p, &p);
1972 int gen = decode_uleb128 (p, &p);
1973 if (debug)
1974 fprintf (outfile, "gc event for gen%d: %s at %llu (thread: 0x%zx)\n", gen, gc_event_name (ev), (unsigned long long) time_base, thread->thread_id);
1975 if (gen > 2) {
1976 fprintf (outfile, "incorrect gc gen: %d\n", gen);
1977 break;
1979 if (ev == MONO_GC_EVENT_START) {
1980 thread->gc_start_times [gen] = time_base;
1981 gc_info [gen].count++;
1982 } else if (ev == MONO_GC_EVENT_END) {
1983 tdiff = time_base - thread->gc_start_times [gen];
1984 gc_info [gen].total_time += tdiff;
1985 if (tdiff > gc_info [gen].max_time)
1986 gc_info [gen].max_time = tdiff;
1988 } else if (subtype == TYPE_GC_MOVE) {
1989 int j, num = decode_uleb128 (p, &p);
1990 gc_object_moves += num / 2;
1991 for (j = 0; j < num; j += 2) {
1992 intptr_t obj1diff = decode_sleb128 (p, &p);
1993 intptr_t obj2diff = decode_sleb128 (p, &p);
1994 if (num_tracked_objects)
1995 track_move (OBJ_ADDR (obj1diff), OBJ_ADDR (obj2diff));
1996 if (debug) {
1997 fprintf (outfile, "moved obj %p to %p\n", (void*)OBJ_ADDR (obj1diff), (void*)OBJ_ADDR (obj2diff));
2000 } else if (subtype == TYPE_GC_HANDLE_CREATED) {
2001 int htype = decode_uleb128 (p, &p);
2002 uint32_t handle = decode_uleb128 (p, &p);
2003 intptr_t objdiff = decode_sleb128 (p, &p);
2004 if (htype > 3)
2005 return 0;
2006 handle_info [htype].created++;
2007 handle_info [htype].live++;
2008 add_trace_thread (thread, &handle_info [htype].traces, 1);
2009 /* FIXME: we don't take into account timing here */
2010 if (handle_info [htype].live > handle_info [htype].max_live)
2011 handle_info [htype].max_live = handle_info [htype].live;
2012 if (num_tracked_objects)
2013 track_handle (OBJ_ADDR (objdiff), htype, handle);
2014 if (debug)
2015 fprintf (outfile, "handle (%s) %u created for object %p\n", get_handle_name (htype), handle, (void*)OBJ_ADDR (objdiff));
2016 } else if (subtype == TYPE_GC_HANDLE_DESTROYED) {
2017 int htype = decode_uleb128 (p, &p);
2018 uint32_t handle = decode_uleb128 (p, &p);
2019 if (htype > 3)
2020 return 0;
2021 handle_info [htype].destroyed ++;
2022 handle_info [htype].live--;
2023 if (debug)
2024 fprintf (outfile, "handle (%s) %u destroyed\n", get_handle_name (htype), handle);
2026 break;
2028 case TYPE_METADATA: {
2029 int error = *p & TYPE_LOAD_ERR;
2030 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2031 int mtype = *p++;
2032 intptr_t ptrdiff = decode_sleb128 (p, &p);
2033 LOG_TIME (time_base, tdiff);
2034 time_base += tdiff;
2035 if (mtype == TYPE_CLASS) {
2036 intptr_t imptrdiff = decode_sleb128 (p, &p);
2037 uint64_t flags = decode_uleb128 (p, &p);
2038 if (flags) {
2039 fprintf (outfile, "non-zero flags in class\n");
2040 return 0;
2042 if (debug)
2043 fprintf (outfile, "loaded class %p (%s in %p) at %llu\n", (void*)(ptr_base + ptrdiff), p, (void*)(ptr_base + imptrdiff), (unsigned long long) time_base);
2044 if (!error)
2045 add_class (ptr_base + ptrdiff, (char*)p);
2046 while (*p) p++;
2047 p++;
2048 } else if (mtype == TYPE_IMAGE) {
2049 uint64_t flags = decode_uleb128 (p, &p);
2050 if (flags) {
2051 fprintf (outfile, "non-zero flags in image\n");
2052 return 0;
2054 if (debug)
2055 fprintf (outfile, "loaded image %p (%s) at %llu\n", (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
2056 if (!error)
2057 add_image (ptr_base + ptrdiff, (char*)p);
2058 while (*p) p++;
2059 p++;
2060 } else if (mtype == TYPE_THREAD) {
2061 ThreadContext *nt;
2062 uint64_t flags = decode_uleb128 (p, &p);
2063 if (flags) {
2064 fprintf (outfile, "non-zero flags in thread\n");
2065 return 0;
2067 nt = get_thread (ctx, ptr_base + ptrdiff);
2068 nt->name = pstrdup ((char*)p);
2069 if (debug)
2070 fprintf (outfile, "thread %p named: %s\n", (void*)(ptr_base + ptrdiff), p);
2071 while (*p) p++;
2072 p++;
2074 break;
2076 case TYPE_ALLOC: {
2077 int has_bt = *p & TYPE_ALLOC_BT;
2078 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2079 intptr_t ptrdiff = decode_sleb128 (p, &p);
2080 intptr_t objdiff = decode_sleb128 (p, &p);
2081 uint64_t len;
2082 int num_bt = 0;
2083 MethodDesc* sframes [8];
2084 MethodDesc** frames = sframes;
2085 ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
2086 len = decode_uleb128 (p, &p);
2087 LOG_TIME (time_base, tdiff);
2088 time_base += tdiff;
2089 if (debug)
2090 fprintf (outfile, "alloced object %p, size %llu (%s) at %llu\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) len, lookup_class (ptr_base + ptrdiff)->name, (unsigned long long) time_base);
2091 if (has_bt) {
2092 num_bt = 8;
2093 frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
2094 if (!frames) {
2095 fprintf (outfile, "Cannot load backtrace\n");
2096 return 0;
2099 if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2100 BackTrace *bt;
2101 cd->allocs++;
2102 cd->alloc_size += len;
2103 if (has_bt)
2104 bt = add_trace_methods (frames, num_bt, &cd->traces, len);
2105 else
2106 bt = add_trace_thread (thread, &cd->traces, len);
2107 if (find_size && len >= find_size) {
2108 if (!find_name || strstr (cd->name, find_name))
2109 found_object (OBJ_ADDR (objdiff));
2110 } else if (!find_size && find_name && strstr (cd->name, find_name)) {
2111 found_object (OBJ_ADDR (objdiff));
2113 if (num_tracked_objects)
2114 tracked_creation (OBJ_ADDR (objdiff), cd, len, bt, time_base);
2116 if (frames != sframes)
2117 free (frames);
2118 break;
2120 case TYPE_METHOD: {
2121 int subtype = *p & 0xf0;
2122 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2123 int64_t ptrdiff = decode_sleb128 (p, &p);
2124 LOG_TIME (time_base, tdiff);
2125 time_base += tdiff;
2126 method_base += ptrdiff;
2127 if (subtype == TYPE_JIT) {
2128 intptr_t codediff = decode_sleb128 (p, &p);
2129 int codelen = decode_uleb128 (p, &p);
2130 MethodDesc *jitted_method;
2131 if (debug)
2132 fprintf (outfile, "jitted method %p (%s), size: %d, code: %p\n", (void*)(method_base), p, codelen, (void*)(ptr_base + codediff));
2133 jitted_method = add_method (method_base, (char*)p, ptr_base + codediff, codelen);
2134 if (!(time_base >= time_from && time_base < time_to))
2135 jitted_method->ignore_jit = 1;
2136 while (*p) p++;
2137 p++;
2138 } else {
2139 MethodDesc *method;
2140 if ((thread_filter && thread_filter != thread->thread_id))
2141 break;
2142 if (!(time_base >= time_from && time_base < time_to))
2143 break;
2144 method = lookup_method (method_base);
2145 if (subtype == TYPE_ENTER) {
2146 add_trace_thread (thread, &method->traces, 1);
2147 push_method (thread, method, time_base);
2148 } else {
2149 pop_method (thread, method, time_base);
2151 if (debug)
2152 fprintf (outfile, "%s method %s\n", subtype == TYPE_ENTER? "enter": subtype == TYPE_EXC_LEAVE? "exleave": "leave", method->name);
2154 break;
2156 case TYPE_HEAP: {
2157 int subtype = *p & 0xf0;
2158 if (subtype == TYPE_HEAP_OBJECT) {
2159 HeapObjectDesc *ho = NULL;
2160 int i;
2161 intptr_t objdiff = decode_sleb128 (p + 1, &p);
2162 intptr_t ptrdiff = decode_sleb128 (p, &p);
2163 uint64_t size = decode_uleb128 (p, &p);
2164 uintptr_t num = decode_uleb128 (p, &p);
2165 uintptr_t ref_offset = 0;
2166 uintptr_t last_obj_offset = 0;
2167 ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
2168 if (size) {
2169 HeapClassDesc *hcd = add_heap_shot_class (thread->current_heap_shot, cd, size);
2170 if (collect_traces) {
2171 ho = alloc_heap_obj (OBJ_ADDR (objdiff), hcd, num);
2172 add_heap_shot_obj (thread->current_heap_shot, ho);
2173 ref_offset = 0;
2175 } else {
2176 if (collect_traces)
2177 ho = heap_shot_obj_add_refs (thread->current_heap_shot, OBJ_ADDR (objdiff), num, &ref_offset);
2179 for (i = 0; i < num; ++i) {
2180 /* FIXME: use object distance to measure how good
2181 * the GC is at keeping related objects close
2183 uintptr_t offset = ctx->data_version > 1? last_obj_offset + decode_uleb128 (p, &p): -1;
2184 intptr_t obj1diff = decode_sleb128 (p, &p);
2185 last_obj_offset = offset;
2186 if (collect_traces)
2187 ho->refs [ref_offset + i] = OBJ_ADDR (obj1diff);
2188 if (num_tracked_objects)
2189 track_obj_reference (OBJ_ADDR (obj1diff), OBJ_ADDR (objdiff), cd);
2191 if (debug && size)
2192 fprintf (outfile, "traced object %p, size %llu (%s), refs: %zd\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) size, cd->name, num);
2193 } else if (subtype == TYPE_HEAP_ROOT) {
2194 uintptr_t num = decode_uleb128 (p + 1, &p);
2195 uintptr_t gc_num G_GNUC_UNUSED = decode_uleb128 (p, &p);
2196 int i;
2197 for (i = 0; i < num; ++i) {
2198 intptr_t objdiff = decode_sleb128 (p, &p);
2199 int root_type = decode_uleb128 (p, &p);
2200 /* we just discard the extra info for now */
2201 uintptr_t extra_info = decode_uleb128 (p, &p);
2202 if (debug)
2203 fprintf (outfile, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff), get_root_name (root_type));
2204 if (collect_traces)
2205 thread_add_root (thread, OBJ_ADDR (objdiff), root_type, extra_info);
2207 } else if (subtype == TYPE_HEAP_END) {
2208 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2209 LOG_TIME (time_base, tdiff);
2210 time_base += tdiff;
2211 if (debug)
2212 fprintf (outfile, "heap shot end\n");
2213 if (collect_traces) {
2214 HeapShot *hs = thread->current_heap_shot;
2215 if (hs && thread->num_roots) {
2216 /* transfer the root ownershipt to the heapshot */
2217 hs->num_roots = thread->num_roots;
2218 hs->roots = thread->roots;
2219 hs->roots_extra = thread->roots_extra;
2220 hs->roots_types = thread->roots_types;
2221 } else {
2222 free (thread->roots);
2223 free (thread->roots_extra);
2224 free (thread->roots_types);
2226 thread->num_roots = 0;
2227 thread->size_roots = 0;
2228 thread->roots = NULL;
2229 thread->roots_extra = NULL;
2230 thread->roots_types = NULL;
2231 heap_shot_resolve_reverse_refs (hs);
2232 heap_shot_mark_objects (hs);
2233 heap_shot_free_objects (hs);
2235 thread->current_heap_shot = NULL;
2236 } else if (subtype == TYPE_HEAP_START) {
2237 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2238 LOG_TIME (time_base, tdiff);
2239 time_base += tdiff;
2240 if (debug)
2241 fprintf (outfile, "heap shot start\n");
2242 thread->current_heap_shot = new_heap_shot (time_base);
2244 break;
2246 case TYPE_MONITOR: {
2247 int event = (*p >> 4) & 0x3;
2248 int has_bt = *p & TYPE_MONITOR_BT;
2249 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2250 intptr_t objdiff = decode_sleb128 (p, &p);
2251 MethodDesc* sframes [8];
2252 MethodDesc** frames = sframes;
2253 int record;
2254 int num_bt = 0;
2255 LOG_TIME (time_base, tdiff);
2256 time_base += tdiff;
2257 record = (!thread_filter || thread_filter == thread->thread_id);
2258 if (!(time_base >= time_from && time_base < time_to))
2259 record = 0;
2260 if (event == MONO_PROFILER_MONITOR_CONTENTION) {
2261 MonitorDesc *mdesc = lookup_monitor (OBJ_ADDR (objdiff));
2262 if (record) {
2263 monitor_contention++;
2264 mdesc->contentions++;
2265 thread->monitor = mdesc;
2266 thread->contention_start = time_base;
2268 if (has_bt) {
2269 num_bt = 8;
2270 frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
2271 if (!frames) {
2272 fprintf (outfile, "Cannot load backtrace\n");
2273 return 0;
2275 if (record)
2276 add_trace_methods (frames, num_bt, &mdesc->traces, 1);
2277 } else {
2278 if (record)
2279 add_trace_thread (thread, &mdesc->traces, 1);
2281 } else if (event == MONO_PROFILER_MONITOR_FAIL) {
2282 if (record) {
2283 monitor_failed++;
2284 if (thread->monitor && thread->contention_start) {
2285 uint64_t wait_time = time_base - thread->contention_start;
2286 if (wait_time > thread->monitor->max_wait_time)
2287 thread->monitor->max_wait_time = wait_time;
2288 thread->monitor->wait_time += wait_time;
2289 thread->monitor = NULL;
2290 thread->contention_start = 0;
2293 } else if (event == MONO_PROFILER_MONITOR_DONE) {
2294 if (record) {
2295 monitor_acquired++;
2296 if (thread->monitor && thread->contention_start) {
2297 uint64_t wait_time = time_base - thread->contention_start;
2298 if (wait_time > thread->monitor->max_wait_time)
2299 thread->monitor->max_wait_time = wait_time;
2300 thread->monitor->wait_time += wait_time;
2301 thread->monitor = NULL;
2302 thread->contention_start = 0;
2306 if (debug)
2307 fprintf (outfile, "monitor %s for object %p\n", monitor_ev_name (event), (void*)OBJ_ADDR (objdiff));
2308 if (frames != sframes)
2309 free (frames);
2310 break;
2312 case TYPE_EXCEPTION: {
2313 int subtype = *p & 0x70;
2314 int has_bt = *p & TYPE_EXCEPTION_BT;
2315 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2316 MethodDesc* sframes [8];
2317 MethodDesc** frames = sframes;
2318 int record;
2319 LOG_TIME (time_base, tdiff);
2320 time_base += tdiff;
2321 record = (!thread_filter || thread_filter == thread->thread_id);
2322 if (!(time_base >= time_from && time_base < time_to))
2323 record = 0;
2324 if (subtype == TYPE_CLAUSE) {
2325 int clause_type = decode_uleb128 (p, &p);
2326 int clause_num = decode_uleb128 (p, &p);
2327 int64_t ptrdiff = decode_sleb128 (p, &p);
2328 method_base += ptrdiff;
2329 if (record)
2330 clause_summary [clause_type]++;
2331 if (debug)
2332 fprintf (outfile, "clause %s (%d) in method %s\n", clause_name (clause_type), clause_num, lookup_method (method_base)->name);
2333 } else {
2334 intptr_t objdiff = decode_sleb128 (p, &p);
2335 if (record)
2336 throw_count++;
2337 if (has_bt) {
2338 has_bt = 8;
2339 frames = decode_bt (sframes, &has_bt, p, &p, ptr_base);
2340 if (!frames) {
2341 fprintf (outfile, "Cannot load backtrace\n");
2342 return 0;
2344 if (record)
2345 add_trace_methods (frames, has_bt, &exc_traces, 1);
2346 } else {
2347 if (record)
2348 add_trace_thread (thread, &exc_traces, 1);
2350 if (frames != sframes)
2351 free (frames);
2352 if (debug)
2353 fprintf (outfile, "throw %p\n", (void*)OBJ_ADDR (objdiff));
2355 break;
2357 case TYPE_SAMPLE: {
2358 int subtype = *p & 0xf0;
2359 if (subtype == TYPE_SAMPLE_HIT) {
2360 int i;
2361 int sample_type = decode_uleb128 (p + 1, &p);
2362 uint64_t tstamp = decode_uleb128 (p, &p);
2363 int count = decode_uleb128 (p, &p);
2364 for (i = 0; i < count; ++i) {
2365 uintptr_t ip = ptr_base + decode_sleb128 (p, &p);
2366 if ((tstamp >= time_from && tstamp < time_to))
2367 add_stat_sample (sample_type, ip);
2368 if (debug)
2369 fprintf (outfile, "sample hit, type: %d at %p\n", sample_type, (void*)ip);
2371 if (ctx->data_version > 5) {
2372 count = decode_uleb128 (p, &p);
2373 for (i = 0; i < count; ++i) {
2374 MethodDesc *method;
2375 int64_t ptrdiff = decode_sleb128 (p, &p);
2376 int il_offset = decode_sleb128 (p, &p);
2377 int native_offset = decode_sleb128 (p, &p);
2378 method_base += ptrdiff;
2379 method = lookup_method (method_base);
2380 if (debug)
2381 fprintf (outfile, "sample hit bt %d: %s at IL offset %d (native: %d)\n", i, method->name, il_offset, native_offset);
2384 } else if (subtype == TYPE_SAMPLE_USYM) {
2385 /* un unmanaged symbol description */
2386 uintptr_t addr = ptr_base + decode_sleb128 (p + 1, &p);
2387 uintptr_t size = decode_uleb128 (p, &p);
2388 char *name;
2389 name = pstrdup ((char*)p);
2390 add_unmanaged_symbol (addr, name, size);
2391 if (debug)
2392 fprintf (outfile, "unmanaged symbol %s at %p\n", name, (void*)addr);
2393 while (*p) p++;
2394 p++;
2395 } else if (subtype == TYPE_SAMPLE_UBIN) {
2396 /* un unmanaged binary loaded in memory */
2397 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2398 uintptr_t addr = decode_sleb128 (p, &p);
2399 uint64_t offset G_GNUC_UNUSED = decode_uleb128 (p, &p);
2400 uintptr_t size = decode_uleb128 (p, &p);
2401 char *name;
2402 LOG_TIME (time_base, tdiff);
2403 time_base += tdiff;
2404 name = pstrdup ((char*)p);
2405 add_unmanaged_binary (addr, name, size);
2406 if (debug)
2407 fprintf (outfile, "unmanaged binary %s at %p\n", name, (void*)addr);
2408 while (*p) p++;
2409 p++;
2410 } else if (subtype == TYPE_SAMPLE_COUNTERS_DESC) {
2411 uint64_t i, len = decode_uleb128 (p + 1, &p);
2412 for (i = 0; i < len; i++) {
2413 uint64_t type, unit, variance, index;
2414 uint64_t section = decode_uleb128 (p, &p);
2415 char *name = pstrdup ((char*)p);
2416 while (*p++);
2417 type = decode_uleb128 (p, &p);
2418 unit = decode_uleb128 (p, &p);
2419 variance = decode_uleb128 (p, &p);
2420 index = decode_uleb128 (p, &p);
2421 add_counter ((int)section, name, (int)type, (int)unit, (int)variance, (int)index);
2423 } else if (subtype == TYPE_SAMPLE_COUNTERS) {
2424 int i;
2425 CounterValue *value, *previous = NULL;
2426 CounterList *list;
2427 uint64_t timestamp = decode_uleb128 (p + 1, &p);
2428 uint64_t time_between = timestamp / 1000 * 1000 * 1000 * 1000 + startup_time;
2429 while (1) {
2430 uint64_t type, index = decode_uleb128 (p, &p);
2431 if (index == 0)
2432 break;
2434 for (list = counters; list; list = list->next) {
2435 if (list->counter->index == (int)index) {
2436 previous = list->counter->values_last;
2437 break;
2441 type = decode_uleb128 (p, &p);
2443 value = calloc (1, sizeof (CounterValue));
2444 value->timestamp = timestamp;
2446 switch (type) {
2447 case MONO_COUNTER_INT:
2448 #if SIZEOF_VOID_P == 4
2449 case MONO_COUNTER_WORD:
2450 #endif
2451 value->buffer = malloc (sizeof (int32_t));
2452 *(int32_t*)value->buffer = (int32_t)decode_sleb128 (p, &p) + (previous ? (*(int32_t*)previous->buffer) : 0);
2453 break;
2454 case MONO_COUNTER_UINT:
2455 value->buffer = malloc (sizeof (uint32_t));
2456 *(uint32_t*)value->buffer = (uint32_t)decode_uleb128 (p, &p) + (previous ? (*(uint32_t*)previous->buffer) : 0);
2457 break;
2458 case MONO_COUNTER_LONG:
2459 #if SIZEOF_VOID_P == 8
2460 case MONO_COUNTER_WORD:
2461 #endif
2462 case MONO_COUNTER_TIME_INTERVAL:
2463 value->buffer = malloc (sizeof (int64_t));
2464 *(int64_t*)value->buffer = (int64_t)decode_sleb128 (p, &p) + (previous ? (*(int64_t*)previous->buffer) : 0);
2465 break;
2466 case MONO_COUNTER_ULONG:
2467 value->buffer = malloc (sizeof (uint64_t));
2468 *(uint64_t*)value->buffer = (uint64_t)decode_uleb128 (p, &p) + (previous ? (*(uint64_t*)previous->buffer) : 0);
2469 break;
2470 case MONO_COUNTER_DOUBLE:
2471 value->buffer = malloc (sizeof (double));
2472 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
2473 for (i = 0; i < sizeof (double); i++)
2474 #else
2475 for (i = sizeof (double) - 1; i >= 0; i--)
2476 #endif
2477 value->buffer[i] = *p++;
2478 break;
2479 case MONO_COUNTER_STRING:
2480 if (*p++ == 0) {
2481 value->buffer = NULL;
2482 } else {
2483 value->buffer = (unsigned char*) pstrdup ((char*)p);
2484 while (*p++);
2486 break;
2488 if (time_between >= time_from && time_between <= time_to)
2489 add_counter_value (index, value);
2491 } else {
2492 return 0;
2494 break;
2496 default:
2497 fprintf (outfile, "unhandled profiler event: 0x%x at file offset: %llu + %lld (len: %d\n)\n", *p, (unsigned long long) file_offset, (long long) (p - ctx->buf), len);
2498 exit (1);
2501 thread->last_time = time_base;
2502 for (i = 0; i < thread->stack_id; ++i)
2503 thread->stack [i]->recurse_count = 0;
2504 return 1;
2507 static ProfContext*
2508 load_file (char *name)
2510 unsigned char *p;
2511 ProfContext *ctx = calloc (sizeof (ProfContext), 1);
2512 if (strcmp (name, "-") == 0)
2513 ctx->file = stdin;
2514 else
2515 ctx->file = fopen (name, "rb");
2516 if (!ctx->file) {
2517 printf ("Cannot open file: %s\n", name);
2518 exit (1);
2520 #if defined (HAVE_SYS_ZLIB)
2521 if (ctx->file != stdin)
2522 ctx->gzfile = gzdopen (fileno (ctx->file), "rb");
2523 #endif
2524 if (!load_data (ctx, 32))
2525 return NULL;
2526 p = ctx->buf;
2527 if (read_int32 (p) != LOG_HEADER_ID || p [6] > LOG_DATA_VERSION)
2528 return NULL;
2529 ctx->version_major = p [4];
2530 ctx->version_minor = p [5];
2531 ctx->data_version = p [6];
2532 /* reading 64 bit files on 32 bit systems not supported yet */
2533 if (p [7] > sizeof (void*))
2534 return NULL;
2535 if (read_int32 (p + 20)) /* flags must be 0 */
2536 return NULL;
2537 ctx->startup_time = read_int64 (p + 8);
2538 ctx->timer_overhead = read_int32 (p + 16);
2539 ctx->pid = read_int32 (p + 24);
2540 ctx->port = read_int16 (p + 28);
2541 return ctx;
2544 enum {
2545 ALLOC_SORT_BYTES,
2546 ALLOC_SORT_COUNT
2548 static int alloc_sort_mode = ALLOC_SORT_BYTES;
2550 static int
2551 compare_class (const void *a, const void *b)
2553 ClassDesc *const*A = a;
2554 ClassDesc *const*B = b;
2555 uint64_t vala, valb;
2556 if (alloc_sort_mode == ALLOC_SORT_BYTES) {
2557 vala = (*A)->alloc_size;
2558 valb = (*B)->alloc_size;
2559 } else {
2560 vala = (*A)->allocs;
2561 valb = (*B)->allocs;
2563 if (valb == vala)
2564 return 0;
2565 if (valb < vala)
2566 return -1;
2567 return 1;
2570 static void
2571 dump_header (ProfContext *ctx)
2573 time_t st = ctx->startup_time / 1000;
2574 char *t = ctime (&st);
2575 fprintf (outfile, "\nMono log profiler data\n");
2576 fprintf (outfile, "\tProfiler version: %d.%d\n", ctx->version_major, ctx->version_minor);
2577 fprintf (outfile, "\tData version: %d\n", ctx->data_version);
2578 fprintf (outfile, "\tMean timer overhead: %d nanoseconds\n", ctx->timer_overhead);
2579 fprintf (outfile, "\tProgram startup: %s", t);
2580 if (ctx->pid)
2581 fprintf (outfile, "\tProgram ID: %d\n", ctx->pid);
2582 if (ctx->port)
2583 fprintf (outfile, "\tServer listening on: %d\n", ctx->port);
2586 static void
2587 dump_traces (TraceDesc *traces, const char *desc)
2589 int j;
2590 if (!show_traces)
2591 return;
2592 if (!traces->count)
2593 return;
2594 sort_context_array (traces);
2595 for (j = 0; j < traces->count; ++j) {
2596 int k;
2597 BackTrace *bt;
2598 bt = traces->traces [j].bt;
2599 if (!bt->count)
2600 continue;
2601 fprintf (outfile, "\t%llu %s from:\n", (unsigned long long) traces->traces [j].count, desc);
2602 for (k = 0; k < bt->count; ++k)
2603 fprintf (outfile, "\t\t%s\n", bt->methods [k]->name);
2607 static void
2608 dump_threads (ProfContext *ctx)
2610 ThreadContext *thread;
2611 fprintf (outfile, "\nThread summary\n");
2612 for (thread = ctx->threads; thread; thread = thread->next) {
2613 fprintf (outfile, "\tThread: %p, name: \"%s\"\n", (void*)thread->thread_id, thread->name? thread->name: "");
2617 static void
2618 dump_exceptions (void)
2620 int i;
2621 fprintf (outfile, "\nException summary\n");
2622 fprintf (outfile, "\tThrows: %llu\n", (unsigned long long) throw_count);
2623 dump_traces (&exc_traces, "throws");
2624 for (i = 0; i <= MONO_EXCEPTION_CLAUSE_FAULT; ++i) {
2625 if (!clause_summary [i])
2626 continue;
2627 fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), (unsigned long long) clause_summary [i]);
2631 static int
2632 compare_monitor (const void *a, const void *b)
2634 MonitorDesc *const*A = a;
2635 MonitorDesc *const*B = b;
2636 if ((*B)->wait_time == (*A)->wait_time)
2637 return 0;
2638 if ((*B)->wait_time < (*A)->wait_time)
2639 return -1;
2640 return 1;
2643 static void
2644 dump_monitors (void)
2646 MonitorDesc **monitors;
2647 int i, j;
2648 if (!num_monitors)
2649 return;
2650 monitors = malloc (sizeof (void*) * num_monitors);
2651 for (i = 0, j = 0; i < SMALL_HASH_SIZE; ++i) {
2652 MonitorDesc *mdesc = monitor_hash [i];
2653 while (mdesc) {
2654 monitors [j++] = mdesc;
2655 mdesc = mdesc->next;
2658 qsort (monitors, num_monitors, sizeof (void*), compare_monitor);
2659 fprintf (outfile, "\nMonitor lock summary\n");
2660 for (i = 0; i < num_monitors; ++i) {
2661 MonitorDesc *mdesc = monitors [i];
2662 fprintf (outfile, "\tLock object %p: %d contentions\n", (void*)mdesc->objid, (int)mdesc->contentions);
2663 fprintf (outfile, "\t\t%.6f secs total wait time, %.6f max, %.6f average\n",
2664 mdesc->wait_time/1000000000.0, mdesc->max_wait_time/1000000000.0, mdesc->wait_time/1000000000.0/mdesc->contentions);
2665 dump_traces (&mdesc->traces, "contentions");
2667 fprintf (outfile, "\tLock contentions: %llu\n", (unsigned long long) monitor_contention);
2668 fprintf (outfile, "\tLock acquired: %llu\n", (unsigned long long) monitor_acquired);
2669 fprintf (outfile, "\tLock failures: %llu\n", (unsigned long long) monitor_failed);
2672 static void
2673 dump_gcs (void)
2675 int i;
2676 fprintf (outfile, "\nGC summary\n");
2677 fprintf (outfile, "\tGC resizes: %d\n", gc_resizes);
2678 fprintf (outfile, "\tMax heap size: %llu\n", (unsigned long long) max_heap_size);
2679 fprintf (outfile, "\tObject moves: %llu\n", (unsigned long long) gc_object_moves);
2680 for (i = 0; i < 3; ++i) {
2681 if (!gc_info [i].count)
2682 continue;
2683 fprintf (outfile, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
2684 i, gc_info [i].count,
2685 (unsigned long long) (gc_info [i].max_time / 1000),
2686 (unsigned long long) (gc_info [i].total_time / 1000),
2687 (unsigned long long) (gc_info [i].total_time / gc_info [i].count / 1000));
2689 for (i = 0; i < 3; ++i) {
2690 if (!handle_info [i].max_live)
2691 continue;
2692 fprintf (outfile, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
2693 get_handle_name (i),
2694 (unsigned long long) (handle_info [i].created),
2695 (unsigned long long) (handle_info [i].destroyed),
2696 (unsigned long long) (handle_info [i].max_live));
2697 dump_traces (&handle_info [i].traces, "created");
2701 static void
2702 dump_jit (void)
2704 int i;
2705 int code_size = 0;
2706 int compiled_methods = 0;
2707 MethodDesc* m;
2708 fprintf (outfile, "\nJIT summary\n");
2709 for (i = 0; i < HASH_SIZE; ++i) {
2710 m = method_hash [i];
2711 for (m = method_hash [i]; m; m = m->next) {
2712 if (!m->code || m->ignore_jit)
2713 continue;
2714 compiled_methods++;
2715 code_size += m->len;
2718 fprintf (outfile, "\tCompiled methods: %d\n", compiled_methods);
2719 fprintf (outfile, "\tGenerated code size: %d\n", code_size);
2722 static void
2723 dump_allocations (void)
2725 int i, c;
2726 intptr_t allocs = 0;
2727 uint64_t size = 0;
2728 int header_done = 0;
2729 ClassDesc **classes = malloc (num_classes * sizeof (void*));
2730 ClassDesc *cd;
2731 c = 0;
2732 for (i = 0; i < HASH_SIZE; ++i) {
2733 cd = class_hash [i];
2734 while (cd) {
2735 classes [c++] = cd;
2736 cd = cd->next;
2739 qsort (classes, num_classes, sizeof (void*), compare_class);
2740 for (i = 0; i < num_classes; ++i) {
2741 cd = classes [i];
2742 if (!cd->allocs)
2743 continue;
2744 allocs += cd->allocs;
2745 size += cd->alloc_size;
2746 if (!header_done++) {
2747 fprintf (outfile, "\nAllocation summary\n");
2748 fprintf (outfile, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
2750 fprintf (outfile, "%10llu %10zd %8llu %s\n",
2751 (unsigned long long) (cd->alloc_size),
2752 cd->allocs,
2753 (unsigned long long) (cd->alloc_size / cd->allocs),
2754 cd->name);
2755 dump_traces (&cd->traces, "bytes");
2757 if (allocs)
2758 fprintf (outfile, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size, allocs);
2761 enum {
2762 METHOD_SORT_TOTAL,
2763 METHOD_SORT_SELF,
2764 METHOD_SORT_CALLS
2767 static int method_sort_mode = METHOD_SORT_TOTAL;
2769 static int
2770 compare_method (const void *a, const void *b)
2772 MethodDesc *const*A = a;
2773 MethodDesc *const*B = b;
2774 uint64_t vala, valb;
2775 if (method_sort_mode == METHOD_SORT_SELF) {
2776 vala = (*A)->self_time;
2777 valb = (*B)->self_time;
2778 } else if (method_sort_mode == METHOD_SORT_CALLS) {
2779 vala = (*A)->calls;
2780 valb = (*B)->calls;
2781 } else {
2782 vala = (*A)->total_time;
2783 valb = (*B)->total_time;
2785 if (vala == valb)
2786 return 0;
2787 if (valb < vala)
2788 return -1;
2789 return 1;
2792 static void
2793 dump_metadata (void)
2795 fprintf (outfile, "\nMetadata summary\n");
2796 fprintf (outfile, "\tLoaded images: %d\n", num_images);
2797 if (verbose) {
2798 ImageDesc *image;
2799 int i;
2800 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
2801 image = image_hash [i];
2802 while (image) {
2803 fprintf (outfile, "\t\t%s\n", image->filename);
2804 image = image->next;
2811 static void
2812 dump_methods (void)
2814 int i, c;
2815 uint64_t calls = 0;
2816 int header_done = 0;
2817 MethodDesc **methods = malloc (num_methods * sizeof (void*));
2818 MethodDesc *cd;
2819 c = 0;
2820 for (i = 0; i < HASH_SIZE; ++i) {
2821 cd = method_hash [i];
2822 while (cd) {
2823 cd->total_time = cd->self_time + cd->callee_time;
2824 methods [c++] = cd;
2825 cd = cd->next;
2828 qsort (methods, num_methods, sizeof (void*), compare_method);
2829 for (i = 0; i < num_methods; ++i) {
2830 uint64_t msecs;
2831 uint64_t smsecs;
2832 cd = methods [i];
2833 if (!cd->calls)
2834 continue;
2835 calls += cd->calls;
2836 msecs = cd->total_time / 1000000;
2837 smsecs = (cd->total_time - cd->callee_time) / 1000000;
2838 if (!msecs && !verbose)
2839 continue;
2840 if (!header_done++) {
2841 fprintf (outfile, "\nMethod call summary\n");
2842 fprintf (outfile, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
2844 fprintf (outfile, "%8llu %8llu %10llu %s\n",
2845 (unsigned long long) (msecs),
2846 (unsigned long long) (smsecs),
2847 (unsigned long long) (cd->calls),
2848 cd->name);
2849 dump_traces (&cd->traces, "calls");
2851 if (calls)
2852 fprintf (outfile, "Total calls: %llu\n", (unsigned long long) calls);
2855 static int
2856 compare_heap_class (const void *a, const void *b)
2858 HeapClassDesc *const*A = a;
2859 HeapClassDesc *const*B = b;
2860 uint64_t vala, valb;
2861 if (alloc_sort_mode == ALLOC_SORT_BYTES) {
2862 vala = (*A)->total_size;
2863 valb = (*B)->total_size;
2864 } else {
2865 vala = (*A)->count;
2866 valb = (*B)->count;
2868 if (valb == vala)
2869 return 0;
2870 if (valb < vala)
2871 return -1;
2872 return 1;
2875 static int
2876 compare_rev_class (const void *a, const void *b)
2878 const HeapClassRevRef *A = a;
2879 const HeapClassRevRef *B = b;
2880 if (B->count == A->count)
2881 return 0;
2882 if (B->count < A->count)
2883 return -1;
2884 return 1;
2887 static void
2888 dump_rev_claases (HeapClassRevRef *revs, int count)
2890 int j;
2891 if (!show_traces)
2892 return;
2893 if (!count)
2894 return;
2895 for (j = 0; j < count; ++j) {
2896 HeapClassDesc *cd = revs [j].klass;
2897 fprintf (outfile, "\t\t%llu references from: %s\n",
2898 (unsigned long long) (revs [j].count),
2899 cd->klass->name);
2903 static void
2904 heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
2906 uint64_t size = 0;
2907 uint64_t count = 0;
2908 int ccount = 0;
2909 int i;
2910 HeapClassDesc *cd;
2911 HeapClassDesc **sorted;
2912 sorted = malloc (sizeof (void*) * hs->class_count);
2913 for (i = 0; i < hs->hash_size; ++i) {
2914 cd = hs->class_hash [i];
2915 if (!cd)
2916 continue;
2917 count += cd->count;
2918 size += cd->total_size;
2919 sorted [ccount++] = cd;
2921 hs->sorted = sorted;
2922 qsort (sorted, ccount, sizeof (void*), compare_heap_class);
2923 fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %zd\n",
2924 hs_num,
2925 (hs->timestamp - startup_time)/1000000000.0,
2926 (unsigned long long) (size),
2927 (unsigned long long) (count),
2928 ccount, hs->num_roots);
2929 if (!verbose && ccount > 30)
2930 ccount = 30;
2931 fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
2932 for (i = 0; i < ccount; ++i) {
2933 HeapClassRevRef *rev_sorted;
2934 int j, k;
2935 HeapClassDesc *ocd = NULL;
2936 cd = sorted [i];
2937 if (last_hs)
2938 ocd = heap_class_lookup (last_hs, cd->klass);
2939 fprintf (outfile, "\t%10llu %10llu %8llu %s",
2940 (unsigned long long) (cd->total_size),
2941 (unsigned long long) (cd->count),
2942 (unsigned long long) (cd->total_size / cd->count),
2943 cd->klass->name);
2944 if (ocd) {
2945 int64_t bdiff = cd->total_size - ocd->total_size;
2946 int64_t cdiff = cd->count - ocd->count;
2947 fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", (long long) bdiff, (long long) cdiff);
2948 } else {
2949 fprintf (outfile, "\n");
2951 if (!collect_traces)
2952 continue;
2953 rev_sorted = malloc (cd->rev_count * sizeof (HeapClassRevRef));
2954 k = 0;
2955 for (j = 0; j < cd->rev_hash_size; ++j) {
2956 if (cd->rev_hash [j].klass)
2957 rev_sorted [k++] = cd->rev_hash [j];
2959 assert (cd->rev_count == k);
2960 qsort (rev_sorted, cd->rev_count, sizeof (HeapClassRevRef), compare_rev_class);
2961 if (cd->root_references)
2962 fprintf (outfile, "\t\t%zd root references (%zd pinning)\n", cd->root_references, cd->pinned_references);
2963 dump_rev_claases (rev_sorted, cd->rev_count);
2964 free (rev_sorted);
2966 free (sorted);
2969 static int
2970 compare_heap_shots (const void *a, const void *b)
2972 HeapShot *const*A = a;
2973 HeapShot *const*B = b;
2974 if ((*B)->timestamp == (*A)->timestamp)
2975 return 0;
2976 if ((*B)->timestamp > (*A)->timestamp)
2977 return -1;
2978 return 1;
2981 static void
2982 dump_heap_shots (void)
2984 HeapShot **hs_sorted;
2985 HeapShot *hs;
2986 HeapShot *last_hs = NULL;
2987 int i;
2988 if (!heap_shots)
2989 return;
2990 hs_sorted = malloc (num_heap_shots * sizeof (void*));
2991 fprintf (outfile, "\nHeap shot summary\n");
2992 i = 0;
2993 for (hs = heap_shots; hs; hs = hs->next)
2994 hs_sorted [i++] = hs;
2995 qsort (hs_sorted, num_heap_shots, sizeof (void*), compare_heap_shots);
2996 for (i = 0; i < num_heap_shots; ++i) {
2997 hs = hs_sorted [i];
2998 heap_shot_summary (hs, i, last_hs);
2999 last_hs = hs;
3003 static void
3004 flush_context (ProfContext *ctx)
3006 ThreadContext *thread;
3007 /* FIXME: sometimes there are leftovers: indagate */
3008 for (thread = ctx->threads; thread; thread = thread->next) {
3009 while (thread->stack_id) {
3010 if (debug)
3011 fprintf (outfile, "thread %p has %d items on stack\n", (void*)thread->thread_id, thread->stack_id);
3012 pop_method (thread, thread->stack [thread->stack_id - 1], thread->last_time);
3017 static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,heapshot,counters";
3019 static const char*
3020 match_option (const char *p, const char *opt)
3022 int len = strlen (opt);
3023 if (strncmp (p, opt, len) == 0) {
3024 if (p [len] == ',')
3025 len++;
3026 return p + len;
3028 return p;
3031 static int
3032 print_reports (ProfContext *ctx, const char *reps, int parse_only)
3034 const char *opt;
3035 const char *p;
3036 for (p = reps; *p; p = opt) {
3037 if ((opt = match_option (p, "header")) != p) {
3038 if (!parse_only)
3039 dump_header (ctx);
3040 continue;
3042 if ((opt = match_option (p, "thread")) != p) {
3043 if (!parse_only)
3044 dump_threads (ctx);
3045 continue;
3047 if ((opt = match_option (p, "gc")) != p) {
3048 if (!parse_only)
3049 dump_gcs ();
3050 continue;
3052 if ((opt = match_option (p, "jit")) != p) {
3053 if (!parse_only)
3054 dump_jit ();
3055 continue;
3057 if ((opt = match_option (p, "alloc")) != p) {
3058 if (!parse_only)
3059 dump_allocations ();
3060 continue;
3062 if ((opt = match_option (p, "call")) != p) {
3063 if (!parse_only)
3064 dump_methods ();
3065 continue;
3067 if ((opt = match_option (p, "metadata")) != p) {
3068 if (!parse_only)
3069 dump_metadata ();
3070 continue;
3072 if ((opt = match_option (p, "exception")) != p) {
3073 if (!parse_only)
3074 dump_exceptions ();
3075 continue;
3077 if ((opt = match_option (p, "monitor")) != p) {
3078 if (!parse_only)
3079 dump_monitors ();
3080 continue;
3082 if ((opt = match_option (p, "heapshot")) != p) {
3083 if (!parse_only)
3084 dump_heap_shots ();
3085 continue;
3087 if ((opt = match_option (p, "sample")) != p) {
3088 if (!parse_only)
3089 dump_samples ();
3090 continue;
3092 if ((opt = match_option (p, "counters")) != p) {
3093 if (!parse_only)
3094 dump_counters ();
3095 continue;
3097 return 0;
3099 return 1;
3102 static int
3103 add_find_spec (const char *p)
3105 if (p [0] == 'S' && p [1] == ':') {
3106 char *vale;
3107 find_size = strtoul (p + 2, &vale, 10);
3108 return 1;
3109 } else if (p [0] == 'T' && p [1] == ':') {
3110 find_name = p + 2;
3111 return 1;
3113 return 0;
3116 static void
3117 usage (void)
3119 printf ("Mono log profiler report version %d.%d\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR);
3120 printf ("Usage: mprof-report [OPTIONS] FILENAME\n");
3121 printf ("FILENAME can be '-' to read from standard input.\n");
3122 printf ("Options:\n");
3123 printf ("\t--help display this help\n");
3124 printf ("\t--out=FILE write to FILE instead of stdout\n");
3125 printf ("\t--traces collect and show backtraces\n");
3126 printf ("\t--maxframes=NUM limit backtraces to NUM entries\n");
3127 printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
3128 printf ("\t %s\n", reports);
3129 printf ("\t--method-sort=MODE sort methods according to MODE: total, self, calls\n");
3130 printf ("\t--alloc-sort=MODE sort allocations according to MODE: bytes, count\n");
3131 printf ("\t--counters-sort=MODE sort counters according to MODE: time, category\n");
3132 printf ("\t only accessible in verbose mode\n");
3133 printf ("\t--track=OB1[,OB2...] track what happens to objects OBJ1, O2 etc.\n");
3134 printf ("\t--find=FINDSPEC find and track objects matching FINFSPEC, where FINDSPEC is:\n");
3135 printf ("\t S:minimum_size or T:partial_name\n");
3136 printf ("\t--thread=THREADID consider just the data for thread THREADID\n");
3137 printf ("\t--time=FROM-TO consider data FROM seconds from startup up to TO seconds\n");
3138 printf ("\t--verbose increase verbosity level\n");
3139 printf ("\t--debug display decoding debug info for mprof-report devs\n");
3143 main (int argc, char *argv[])
3145 ProfContext *ctx;
3146 int i;
3147 outfile = stdout;
3148 for (i = 1; i < argc; ++i) {
3149 if (strcmp ("--debug", argv [i]) == 0) {
3150 debug++;
3151 } else if (strcmp ("--help", argv [i]) == 0) {
3152 usage ();
3153 return 0;
3154 } else if (strncmp ("--alloc-sort=", argv [i], 13) == 0) {
3155 const char *val = argv [i] + 13;
3156 if (strcmp (val, "bytes") == 0) {
3157 alloc_sort_mode = ALLOC_SORT_BYTES;
3158 } else if (strcmp (val, "count") == 0) {
3159 alloc_sort_mode = ALLOC_SORT_COUNT;
3160 } else {
3161 usage ();
3162 return 1;
3164 } else if (strncmp ("--method-sort=", argv [i], 14) == 0) {
3165 const char *val = argv [i] + 14;
3166 if (strcmp (val, "total") == 0) {
3167 method_sort_mode = METHOD_SORT_TOTAL;
3168 } else if (strcmp (val, "self") == 0) {
3169 method_sort_mode = METHOD_SORT_SELF;
3170 } else if (strcmp (val, "calls") == 0) {
3171 method_sort_mode = METHOD_SORT_CALLS;
3172 } else {
3173 usage ();
3174 return 1;
3176 } else if (strncmp ("--counters-sort=", argv [i], 16) == 0) {
3177 const char *val = argv [i] + 16;
3178 if (strcmp (val, "time") == 0) {
3179 counters_sort_mode = COUNTERS_SORT_TIME;
3180 } else if (strcmp (val, "category") == 0) {
3181 counters_sort_mode = COUNTERS_SORT_CATEGORY;
3182 } else {
3183 usage ();
3184 return 1;
3186 } else if (strncmp ("--reports=", argv [i], 10) == 0) {
3187 const char *val = argv [i] + 10;
3188 if (!print_reports (NULL, val, 1)) {
3189 usage ();
3190 return 1;
3192 reports = val;
3193 } else if (strncmp ("--out=", argv [i], 6) == 0) {
3194 const char *val = argv [i] + 6;
3195 outfile = fopen (val, "w");
3196 if (!outfile) {
3197 printf ("Cannot open output file: %s\n", val);
3198 return 1;
3200 } else if (strncmp ("--maxframes=", argv [i], 12) == 0) {
3201 const char *val = argv [i] + 12;
3202 char *vale;
3203 trace_max = strtoul (val, &vale, 10);
3204 } else if (strncmp ("--find=", argv [i], 7) == 0) {
3205 const char *val = argv [i] + 7;
3206 if (!add_find_spec (val)) {
3207 usage ();
3208 return 1;
3210 } else if (strncmp ("--track=", argv [i], 8) == 0) {
3211 const char *val = argv [i] + 8;
3212 char *vale;
3213 while (*val) {
3214 uintptr_t tracked_obj;
3215 if (*val == ',') {
3216 val++;
3217 continue;
3219 tracked_obj = strtoul (val, &vale, 0);
3220 found_object (tracked_obj);
3221 val = vale;
3223 } else if (strncmp ("--thread=", argv [i], 9) == 0) {
3224 const char *val = argv [i] + 9;
3225 char *vale;
3226 thread_filter = strtoul (val, &vale, 0);
3227 } else if (strncmp ("--time=", argv [i], 7) == 0) {
3228 char *val = pstrdup (argv [i] + 7);
3229 double from_secs, to_secs;
3230 char *top = strchr (val, '-');
3231 if (!top) {
3232 usage ();
3233 return 1;
3235 *top++ = 0;
3236 from_secs = atof (val);
3237 to_secs = atof (top);
3238 free (val);
3239 if (from_secs > to_secs) {
3240 usage ();
3241 return 1;
3243 time_from = from_secs * 1000000000;
3244 time_to = to_secs * 1000000000;
3245 use_time_filter = 1;
3246 } else if (strcmp ("--verbose", argv [i]) == 0) {
3247 verbose++;
3248 } else if (strcmp ("--traces", argv [i]) == 0) {
3249 show_traces = 1;
3250 collect_traces = 1;
3251 } else {
3252 break;
3255 if (i >= argc) {
3256 usage ();
3257 return 2;
3259 ctx = load_file (argv [i]);
3260 if (!ctx) {
3261 printf ("Not a log profiler data file (or unsupported version).\n");
3262 return 1;
3264 while (decode_buffer (ctx));
3265 flush_context (ctx);
3266 if (num_tracked_objects)
3267 return 0;
3268 print_reports (ctx, reports, 0);
3269 return 0;