[WinForms] Matching Mono PropertyGrid behaviour with .Net one (#11618)
[mono-project.git] / mono / profiler / mprof-report.c
blobc597ca305b05835db5799881eb43764c4a6d0ef2
1 /*
2 * mprof-report.c: mprof-report program source: decode and analyze the log profiler data
4 * Authors:
5 * Paolo Molaro (lupus@ximian.com)
6 * Alex Rønne Petersen (alexrp@xamarin.com)
8 * Copyright 2010 Novell, Inc (http://www.novell.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 #include <config.h>
13 #include "log.h"
14 #include <string.h>
15 #include <assert.h>
16 #include <stdio.h>
17 #include <time.h>
18 #if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
19 #include <malloc.h>
20 #endif
21 #include <unistd.h>
22 #include <stdlib.h>
23 #if defined (HAVE_SYS_ZLIB)
24 #include <zlib.h>
25 #endif
26 #include <glib.h>
27 #include <mono/metadata/profiler.h>
28 #include <mono/metadata/object.h>
29 #include <mono/metadata/debug-helpers.h>
30 #include <mono/utils/mono-counters.h>
32 #define HASH_SIZE 9371
33 #define SMALL_HASH_SIZE 31
35 /* Version < 14 root type enum */
36 typedef enum {
37 /* Upper 2 bytes. */
38 MONO_PROFILER_GC_ROOT_PINNING = 1 << 8,
39 MONO_PROFILER_GC_ROOT_WEAKREF = 2 << 8,
40 MONO_PROFILER_GC_ROOT_INTERIOR = 4 << 8,
42 /* Lower 2 bytes (flags). */
43 MONO_PROFILER_GC_ROOT_STACK = 1 << 0,
44 MONO_PROFILER_GC_ROOT_FINALIZER = 1 << 1,
45 MONO_PROFILER_GC_ROOT_HANDLE = 1 << 2,
46 MONO_PROFILER_GC_ROOT_OTHER = 1 << 3,
47 MONO_PROFILER_GC_ROOT_MISC = 1 << 4,
49 MONO_PROFILER_GC_ROOT_TYPEMASK = 0xff,
50 } MonoProfilerGCRootType;
52 static int debug = 0;
53 static int collect_traces = 0;
54 static int show_traces = 0;
55 static int trace_max = 6;
56 static int verbose = 0;
57 static uintptr_t *tracked_objects = 0;
58 static int num_tracked_objects = 0;
59 static uintptr_t thread_filter = 0;
60 static uint64_t find_size = 0;
61 static const char* find_name = NULL;
62 static uint64_t time_from = 0;
63 static uint64_t time_to = 0xffffffffffffffffULL;
64 static int use_time_filter = 0;
65 static uint64_t startup_time = 0;
66 static FILE* outfile = NULL;
68 static int32_t
69 read_int16 (unsigned char *p)
71 int32_t value = *p++;
72 value |= (*p++) << 8;
73 return value;
76 static int32_t
77 read_int32 (unsigned char *p)
79 int32_t value = *p++;
80 value |= (*p++) << 8;
81 value |= (*p++) << 16;
82 value |= (uint32_t)(*p++) << 24;
83 return value;
86 static int64_t
87 read_int64 (unsigned char *p)
89 uint64_t value = *p++;
90 value |= (*p++) << 8;
91 value |= (*p++) << 16;
92 value |= (uint64_t)(*p++) << 24;
93 value |= (uint64_t)(*p++) << 32;
94 value |= (uint64_t)(*p++) << 40;
95 value |= (uint64_t)(*p++) << 48;
96 value |= (uint64_t)(*p++) << 54;
97 return value;
100 static char*
101 pstrdup (const char *s)
103 int len = strlen (s) + 1;
104 char *p = (char *) g_malloc (len);
105 memcpy (p, s, len);
106 return p;
109 typedef struct _CounterValue CounterValue;
110 struct _CounterValue {
111 uint64_t timestamp;
112 unsigned char *buffer;
113 CounterValue *next;
116 typedef struct _Counter Counter;
117 struct _Counter {
118 int index;
119 const char *section;
120 const char *name;
121 int type;
122 int unit;
123 int variance;
124 CounterValue *values;
125 CounterValue *values_last;
128 typedef struct _CounterList CounterList;
129 struct _CounterList {
130 Counter *counter;
131 CounterList *next;
134 typedef struct _CounterSection CounterSection;
135 struct _CounterSection {
136 const char *value;
137 CounterList *counters;
138 CounterList *counters_last;
139 CounterSection *next;
142 typedef struct _CounterTimestamp CounterTimestamp;
143 struct _CounterTimestamp {
144 uint64_t value;
145 CounterSection *sections;
146 CounterSection *sections_last;
147 CounterTimestamp *next;
150 static CounterList *counters = NULL;
151 static CounterSection *counters_sections = NULL;
152 static CounterTimestamp *counters_timestamps = NULL;
154 enum {
155 COUNTERS_SORT_TIME,
156 COUNTERS_SORT_CATEGORY
159 static int counters_sort_mode = COUNTERS_SORT_TIME;
161 static void
162 add_counter_to_section (Counter *counter)
164 CounterSection *csection, *s;
165 CounterList *clist;
167 clist = (CounterList *) g_calloc (1, sizeof (CounterList));
168 clist->counter = counter;
170 for (csection = counters_sections; csection; csection = csection->next) {
171 if (strcmp (csection->value, counter->section) == 0) {
172 /* If section exist */
173 if (!csection->counters)
174 csection->counters = clist;
175 else
176 csection->counters_last->next = clist;
177 csection->counters_last = clist;
178 return;
182 /* If section does not exist */
183 csection = (CounterSection *) g_calloc (1, sizeof (CounterSection));
184 csection->value = counter->section;
185 csection->counters = clist;
186 csection->counters_last = clist;
188 if (!counters_sections) {
189 counters_sections = csection;
190 } else {
191 s = counters_sections;
192 while (s->next)
193 s = s->next;
194 s->next = csection;
198 static void
199 add_counter (const char *section, const char *name, int type, int unit, int variance, int index)
201 CounterList *list, *l;
202 Counter *counter;
204 for (list = counters; list; list = list->next)
205 if (list->counter->index == index)
206 return;
208 counter = (Counter *) g_calloc (1, sizeof (Counter));
209 counter->section = section;
210 counter->name = name;
211 counter->type = type;
212 counter->unit = unit;
213 counter->variance = variance;
214 counter->index = index;
216 list = (CounterList *) g_calloc (1, sizeof (CounterList));
217 list->counter = counter;
219 if (!counters) {
220 counters = list;
221 } else {
222 l = counters;
223 while (l->next)
224 l = l->next;
225 l->next = list;
228 if (counters_sort_mode == COUNTERS_SORT_CATEGORY || !verbose)
229 add_counter_to_section (counter);
232 static void
233 add_counter_to_timestamp (uint64_t timestamp, Counter *counter)
235 CounterTimestamp *ctimestamp, *t;
236 CounterSection *csection;
237 CounterList *clist;
239 clist = (CounterList *) g_calloc (1, sizeof (CounterList));
240 clist->counter = counter;
242 for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
243 if (ctimestamp->value == timestamp) {
244 for (csection = ctimestamp->sections; csection; csection = csection->next) {
245 if (strcmp (csection->value, counter->section) == 0) {
246 /* if timestamp exist and section exist */
247 if (!csection->counters)
248 csection->counters = clist;
249 else
250 csection->counters_last->next = clist;
251 csection->counters_last = clist;
252 return;
256 /* if timestamp exist and section does not exist */
257 csection = (CounterSection *) g_calloc (1, sizeof (CounterSection));
258 csection->value = counter->section;
259 csection->counters = clist;
260 csection->counters_last = clist;
262 if (!ctimestamp->sections)
263 ctimestamp->sections = csection;
264 else
265 ctimestamp->sections_last->next = csection;
266 ctimestamp->sections_last = csection;
267 return;
271 /* If timestamp do not exist and section does not exist */
272 csection = (CounterSection *) g_calloc (1, sizeof (CounterSection));
273 csection->value = counter->section;
274 csection->counters = clist;
275 csection->counters_last = clist;
277 ctimestamp = (CounterTimestamp *) g_calloc (1, sizeof (CounterTimestamp));
278 ctimestamp->value = timestamp;
279 ctimestamp->sections = csection;
280 ctimestamp->sections_last = csection;
282 if (!counters_timestamps) {
283 counters_timestamps = ctimestamp;
284 } else {
285 t = counters_timestamps;
286 while (t->next)
287 t = t->next;
288 t->next = ctimestamp;
292 static void
293 add_counter_value (int index, CounterValue *value)
295 CounterList *list;
297 for (list = counters; list; list = list->next) {
298 if (list->counter->index == index) {
299 if (!list->counter->values)
300 list->counter->values = value;
301 else
302 list->counter->values_last->next = value;
303 list->counter->values_last = value;
305 if (counters_sort_mode == COUNTERS_SORT_TIME)
306 add_counter_to_timestamp (value->timestamp, list->counter);
308 return;
313 static const char*
314 section_name (int section)
316 switch (section) {
317 case MONO_COUNTER_JIT: return "Mono JIT";
318 case MONO_COUNTER_GC: return "Mono GC";
319 case MONO_COUNTER_METADATA: return "Mono Metadata";
320 case MONO_COUNTER_GENERICS: return "Mono Generics";
321 case MONO_COUNTER_SECURITY: return "Mono Security";
322 case MONO_COUNTER_RUNTIME: return "Mono Runtime";
323 case MONO_COUNTER_SYSTEM: return "Mono System";
324 case MONO_COUNTER_PROFILER: return "Mono Profiler";
325 default: return "<unknown>";
329 static const char*
330 type_name (int type)
332 switch (type) {
333 case MONO_COUNTER_INT: return "Int";
334 case MONO_COUNTER_UINT: return "UInt";
335 case MONO_COUNTER_WORD: return "Word";
336 case MONO_COUNTER_LONG: return "Long";
337 case MONO_COUNTER_ULONG: return "ULong";
338 case MONO_COUNTER_DOUBLE: return "Double";
339 case MONO_COUNTER_STRING: return "String";
340 case MONO_COUNTER_TIME_INTERVAL: return "Time Interval";
341 default: return "<unknown>";
345 static const char*
346 unit_name (int unit)
348 switch (unit) {
349 case MONO_COUNTER_RAW: return "Raw";
350 case MONO_COUNTER_BYTES: return "Bytes";
351 case MONO_COUNTER_TIME: return "Time";
352 case MONO_COUNTER_COUNT: return "Count";
353 case MONO_COUNTER_PERCENTAGE: return "Percentage";
354 default: return "<unknown>";
358 static const char*
359 variance_name (int variance)
361 switch (variance) {
362 case MONO_COUNTER_MONOTONIC: return "Monotonic";
363 case MONO_COUNTER_CONSTANT: return "Constant";
364 case MONO_COUNTER_VARIABLE: return "Variable";
365 default: return "<unknown>";
369 static void
370 dump_counters_value (Counter *counter, const char *key_format, const char *key, void *value)
372 char format[32];
374 if (value == NULL) {
375 snprintf (format, sizeof (format), "%s : %%s\n", key_format);
376 fprintf (outfile, format, key, "<null>");
377 } else {
378 switch (counter->type) {
379 case MONO_COUNTER_INT:
380 #if SIZEOF_VOID_P == 4
381 case MONO_COUNTER_WORD:
382 #endif
383 snprintf (format, sizeof (format), "%s : %%d\n", key_format);
384 fprintf (outfile, format, key, *(int32_t*)value);
385 break;
386 case MONO_COUNTER_UINT:
387 snprintf (format, sizeof (format), "%s : %%u\n", key_format);
388 fprintf (outfile, format, key, *(uint32_t*)value);
389 break;
390 case MONO_COUNTER_LONG:
391 #if SIZEOF_VOID_P == 8
392 case MONO_COUNTER_WORD:
393 #endif
394 case MONO_COUNTER_TIME_INTERVAL:
395 if (counter->type == MONO_COUNTER_LONG && counter->unit == MONO_COUNTER_TIME) {
396 snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
397 fprintf (outfile, format, key, (double)*(int64_t*)value / 10000.0);
398 } else if (counter->type == MONO_COUNTER_TIME_INTERVAL) {
399 snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
400 fprintf (outfile, format, key, (double)*(int64_t*)value / 1000.0);
401 } else {
402 snprintf (format, sizeof (format), "%s : %%u\n", key_format);
403 fprintf (outfile, format, key, *(int64_t*)value);
405 break;
406 case MONO_COUNTER_ULONG:
407 snprintf (format, sizeof (format), "%s : %%llu\n", key_format);
408 fprintf (outfile, format, key, *(uint64_t*)value);
409 break;
410 case MONO_COUNTER_DOUBLE:
411 snprintf (format, sizeof (format), "%s : %%f\n", key_format);
412 fprintf (outfile, format, key, *(double*)value);
413 break;
414 case MONO_COUNTER_STRING:
415 snprintf (format, sizeof (format), "%s : %%s\n", key_format);
416 fprintf (outfile, format, key, *(char*)value);
417 break;
422 static void
423 dump_counters (void)
425 Counter *counter;
426 CounterValue *cvalue;
427 CounterTimestamp *ctimestamp;
428 CounterSection *csection;
429 CounterList *clist;
430 char strtimestamp[17];
431 int i, section_printed;
433 fprintf (outfile, "\nCounters:\n");
435 if (!verbose) {
436 char counters_to_print[][64] = {
437 "Methods from AOT",
438 "Methods JITted using mono JIT",
439 "Methods JITted using LLVM",
440 "Total time spent JITting (sec)",
441 "User Time",
442 "System Time",
443 "Total Time",
444 "Working Set",
445 "Private Bytes",
446 "Virtual Bytes",
447 "Page Faults",
448 "CPU Load Average - 1min",
449 "CPU Load Average - 5min",
450 "CPU Load Average - 15min",
454 for (csection = counters_sections; csection; csection = csection->next) {
455 section_printed = 0;
457 for (clist = csection->counters; clist; clist = clist->next) {
458 counter = clist->counter;
459 if (!counter->values_last)
460 continue;
462 for (i = 0; counters_to_print [i][0] != 0; i++) {
463 if (strcmp (counters_to_print [i], counter->name) == 0) {
464 if (!section_printed) {
465 fprintf (outfile, "\t%s:\n", csection->value);
466 section_printed = 1;
469 dump_counters_value (counter, "\t\t%-30s", counter->name, counter->values_last->buffer);
470 break;
475 } else if (counters_sort_mode == COUNTERS_SORT_TIME) {
476 for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
477 fprintf (outfile, "\t%llu:%02llu:%02llu:%02llu.%03llu:\n",
478 (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 / 24 % 1000),
479 (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 % 24),
480 (unsigned long long) (ctimestamp->value / 1000 / 60 % 60),
481 (unsigned long long) (ctimestamp->value / 1000 % 60),
482 (unsigned long long) (ctimestamp->value % 1000));
484 for (csection = ctimestamp->sections; csection; csection = csection->next) {
485 fprintf (outfile, "\t\t%s:\n", csection->value);
487 for (clist = csection->counters; clist; clist = clist->next) {
488 counter = clist->counter;
489 for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
490 if (cvalue->timestamp != ctimestamp->value)
491 continue;
493 dump_counters_value (counter, "\t\t\t%-30s", counter->name, cvalue->buffer);
498 } else if (counters_sort_mode == COUNTERS_SORT_CATEGORY) {
499 for (csection = counters_sections; csection; csection = csection->next) {
500 fprintf (outfile, "\t%s:\n", csection->value);
502 for (clist = csection->counters; clist; clist = clist->next) {
503 counter = clist->counter;
504 fprintf (outfile, "\t\t%s: [type: %s, unit: %s, variance: %s]\n",
505 counter->name, type_name (counter->type), unit_name (counter->unit), variance_name (counter->variance));
507 for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
508 snprintf (strtimestamp, sizeof (strtimestamp), "%llu:%02llu:%02llu:%02llu.%03llu",
509 (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 / 24 % 1000),
510 (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 % 24),
511 (unsigned long long) (cvalue->timestamp / 1000 / 60 % 60),
512 (unsigned long long) (cvalue->timestamp / 1000 % 60),
513 (unsigned long long) (cvalue->timestamp % 1000));
515 dump_counters_value (counter, "\t\t\t%s", strtimestamp, cvalue->buffer);
522 static int num_images;
523 typedef struct _ImageDesc ImageDesc;
524 struct _ImageDesc {
525 ImageDesc *next;
526 intptr_t image;
527 char *filename;
530 static ImageDesc* image_hash [SMALL_HASH_SIZE] = {0};
532 static void
533 add_image (intptr_t image, char *name)
535 int slot = ((image >> 2) & 0xffff) % SMALL_HASH_SIZE;
536 ImageDesc *cd = (ImageDesc *) g_malloc (sizeof (ImageDesc));
537 cd->image = image;
538 cd->filename = pstrdup (name);
539 cd->next = image_hash [slot];
540 image_hash [slot] = cd;
541 num_images++;
544 static int num_assemblies;
546 typedef struct _AssemblyDesc AssemblyDesc;
547 struct _AssemblyDesc {
548 AssemblyDesc *next;
549 intptr_t assembly;
550 char *asmname;
553 static AssemblyDesc* assembly_hash [SMALL_HASH_SIZE] = {0};
555 static void
556 add_assembly (intptr_t assembly, char *name)
558 int slot = ((assembly >> 2) & 0xffff) % SMALL_HASH_SIZE;
559 AssemblyDesc *cd = (AssemblyDesc *) g_malloc (sizeof (AssemblyDesc));
560 cd->assembly = assembly;
561 cd->asmname = pstrdup (name);
562 cd->next = assembly_hash [slot];
563 assembly_hash [slot] = cd;
564 num_assemblies++;
567 typedef struct _BackTrace BackTrace;
568 typedef struct {
569 uint64_t count;
570 BackTrace *bt;
571 } CallContext;
573 typedef struct {
574 int count;
575 int size;
576 CallContext *traces;
577 } TraceDesc;
579 typedef struct _ClassDesc ClassDesc;
580 struct _ClassDesc {
581 ClassDesc *next;
582 intptr_t klass;
583 char *name;
584 intptr_t allocs;
585 uint64_t alloc_size;
586 TraceDesc traces;
589 static ClassDesc* class_hash [HASH_SIZE] = {0};
590 static int num_classes = 0;
592 static ClassDesc*
593 add_class (intptr_t klass, const char *name)
595 int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
596 ClassDesc *cd;
597 cd = class_hash [slot];
598 while (cd && cd->klass != klass)
599 cd = cd->next;
600 /* we resolved an unknown class (unless we had the code unloaded) */
601 if (cd) {
602 /*printf ("resolved unknown: %s\n", name);*/
603 g_free (cd->name);
604 cd->name = pstrdup (name);
605 return cd;
607 cd = (ClassDesc *) g_calloc (sizeof (ClassDesc), 1);
608 cd->klass = klass;
609 cd->name = pstrdup (name);
610 cd->next = class_hash [slot];
611 cd->allocs = 0;
612 cd->alloc_size = 0;
613 cd->traces.count = 0;
614 cd->traces.size = 0;
615 cd->traces.traces = NULL;
616 class_hash [slot] = cd;
617 num_classes++;
618 return cd;
621 static ClassDesc *
622 lookup_class (intptr_t klass)
624 int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
625 ClassDesc *cd = class_hash [slot];
626 while (cd && cd->klass != klass)
627 cd = cd->next;
628 if (!cd) {
629 char buf [128];
630 snprintf (buf, sizeof (buf), "unresolved class %p", (void*)klass);
631 return add_class (klass, buf);
633 return cd;
636 typedef struct _VTableDesc VTableDesc;
637 struct _VTableDesc {
638 VTableDesc *next;
639 intptr_t vtable;
640 ClassDesc *klass;
643 static VTableDesc* vtable_hash [HASH_SIZE] = {0};
645 static VTableDesc*
646 add_vtable (intptr_t vtable, intptr_t klass)
648 int slot = ((vtable >> 2) & 0xffff) % HASH_SIZE;
650 VTableDesc *vt = vtable_hash [slot];
652 while (vt && vt->vtable != vtable)
653 vt = vt->next;
655 if (vt)
656 return vt;
658 vt = (VTableDesc *) g_calloc (sizeof (VTableDesc), 1);
660 vt->vtable = vtable;
661 vt->klass = lookup_class (klass);
662 vt->next = vtable_hash [slot];
664 vtable_hash [slot] = vt;
666 return vt;
669 static VTableDesc *
670 lookup_vtable (intptr_t vtable)
672 int slot = ((vtable >> 2) & 0xffff) % HASH_SIZE;
673 VTableDesc *vt = vtable_hash [slot];
675 while (vt && vt->vtable != vtable)
676 vt = vt->next;
678 if (!vt)
679 return add_vtable (vtable, 0);
681 return vt;
684 typedef struct _MethodDesc MethodDesc;
685 struct _MethodDesc {
686 MethodDesc *next;
687 intptr_t method;
688 char *name;
689 intptr_t code;
690 int len;
691 int recurse_count;
692 int sample_hits;
693 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 */
694 uint64_t calls;
695 uint64_t total_time;
696 uint64_t callee_time;
697 uint64_t self_time;
698 TraceDesc traces;
701 static MethodDesc* method_hash [HASH_SIZE] = {0};
702 static int num_methods = 0;
704 static MethodDesc*
705 add_method (intptr_t method, const char *name, intptr_t code, int len)
707 int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
708 MethodDesc *cd;
709 cd = method_hash [slot];
710 while (cd && cd->method != method)
711 cd = cd->next;
712 /* we resolved an unknown method (unless we had the code unloaded) */
713 if (cd) {
714 cd->code = code;
715 cd->len = len;
716 /*printf ("resolved unknown: %s\n", name);*/
717 g_free (cd->name);
718 cd->name = pstrdup (name);
719 return cd;
721 cd = (MethodDesc *) g_calloc (sizeof (MethodDesc), 1);
722 cd->method = method;
723 cd->name = pstrdup (name);
724 cd->code = code;
725 cd->len = len;
726 cd->calls = 0;
727 cd->total_time = 0;
728 cd->traces.count = 0;
729 cd->traces.size = 0;
730 cd->traces.traces = NULL;
731 cd->next = method_hash [slot];
732 method_hash [slot] = cd;
733 num_methods++;
734 return cd;
737 static MethodDesc *
738 lookup_method (intptr_t method)
740 int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
741 MethodDesc *cd = method_hash [slot];
742 while (cd && cd->method != method)
743 cd = cd->next;
744 if (!cd) {
745 char buf [128];
746 snprintf (buf, sizeof (buf), "unknown method %p", (void*)method);
747 return add_method (method, buf, 0, 0);
749 return cd;
752 static int num_stat_samples = 0;
753 static int size_stat_samples = 0;
754 uintptr_t *stat_samples = NULL;
755 int *stat_sample_desc = NULL;
757 static void
758 add_stat_sample (int type, uintptr_t ip) {
759 if (num_stat_samples == size_stat_samples) {
760 size_stat_samples *= 2;
761 if (!size_stat_samples)
762 size_stat_samples = 32;
763 stat_samples = (uintptr_t *) g_realloc (stat_samples, size_stat_samples * sizeof (uintptr_t));
764 stat_sample_desc = (int *) g_realloc (stat_sample_desc, size_stat_samples * sizeof (int));
766 stat_samples [num_stat_samples] = ip;
767 stat_sample_desc [num_stat_samples++] = type;
770 static MethodDesc*
771 lookup_method_by_ip (uintptr_t ip)
773 int i;
774 MethodDesc* m;
775 /* dumb */
776 for (i = 0; i < HASH_SIZE; ++i) {
777 m = method_hash [i];
778 while (m) {
779 //printf ("checking %p against %p-%p\n", (void*)ip, (void*)(m->code), (void*)(m->code + m->len));
780 if (ip >= (uintptr_t)m->code && ip < (uintptr_t)m->code + m->len) {
781 return m;
783 m = m->next;
786 return NULL;
789 static int
790 compare_method_samples (const void *a, const void *b)
792 MethodDesc *const *A = (MethodDesc *const *)a;
793 MethodDesc *const *B = (MethodDesc *const *)b;
794 if ((*A)->sample_hits == (*B)->sample_hits)
795 return 0;
796 if ((*B)->sample_hits < (*A)->sample_hits)
797 return -1;
798 return 1;
801 typedef struct _UnmanagedSymbol UnmanagedSymbol;
802 struct _UnmanagedSymbol {
803 UnmanagedSymbol *parent;
804 char *name;
805 int is_binary;
806 uintptr_t addr;
807 uintptr_t size;
808 uintptr_t sample_hits;
811 static UnmanagedSymbol **usymbols = NULL;
812 static int usymbols_size = 0;
813 static int usymbols_num = 0;
815 static int
816 compare_usymbol_addr (const void *a, const void *b)
818 UnmanagedSymbol *const *A = (UnmanagedSymbol *const *)a;
819 UnmanagedSymbol *const *B = (UnmanagedSymbol *const *)b;
820 if ((*B)->addr == (*A)->addr)
821 return 0;
822 if ((*B)->addr > (*A)->addr)
823 return -1;
824 return 1;
827 static int
828 compare_usymbol_samples (const void *a, const void *b)
830 UnmanagedSymbol *const *A = (UnmanagedSymbol *const *)a;
831 UnmanagedSymbol *const *B = (UnmanagedSymbol *const *)b;
832 if ((*B)->sample_hits == (*A)->sample_hits)
833 return 0;
834 if ((*B)->sample_hits < (*A)->sample_hits)
835 return -1;
836 return 1;
839 static void
840 add_unmanaged_symbol (uintptr_t addr, char *name, uintptr_t size)
842 UnmanagedSymbol *sym;
843 if (usymbols_num == usymbols_size) {
844 int new_size = usymbols_size * 2;
845 if (!new_size)
846 new_size = 16;
847 usymbols = (UnmanagedSymbol **) g_realloc (usymbols, sizeof (void*) * new_size);
848 usymbols_size = new_size;
850 sym = (UnmanagedSymbol *) g_calloc (sizeof (UnmanagedSymbol), 1);
851 sym->addr = addr;
852 sym->name = name;
853 sym->size = size;
854 usymbols [usymbols_num++] = sym;
857 /* only valid after the symbols are sorted */
858 static UnmanagedSymbol*
859 lookup_unmanaged_symbol (uintptr_t addr)
861 int r = usymbols_num - 1;
862 int l = 0;
863 UnmanagedSymbol *sym;
864 int last_best = -1;
865 while (r >= l) {
866 int m = (l + r) / 2;
867 sym = usymbols [m];
868 if (addr == sym->addr)
869 return sym;
870 if (addr < sym->addr) {
871 r = m - 1;
872 } else if (addr > sym->addr) {
873 l = m + 1;
874 last_best = m;
877 if (last_best >= 0 && (addr - usymbols [last_best]->addr) < 4096)
878 return usymbols [last_best];
879 return NULL;
882 /* we use the same structure for binaries */
883 static UnmanagedSymbol **ubinaries = NULL;
884 static int ubinaries_size = 0;
885 static int ubinaries_num = 0;
887 static void
888 add_unmanaged_binary (uintptr_t addr, char *name, uintptr_t size)
890 UnmanagedSymbol *sym;
891 if (ubinaries_num == ubinaries_size) {
892 int new_size = ubinaries_size * 2;
893 if (!new_size)
894 new_size = 16;
895 ubinaries = (UnmanagedSymbol **) g_realloc (ubinaries, sizeof (void*) * new_size);
896 ubinaries_size = new_size;
898 sym = (UnmanagedSymbol *) g_calloc (sizeof (UnmanagedSymbol), 1);
899 sym->addr = addr;
900 sym->name = name;
901 sym->size = size;
902 sym->is_binary = 1;
903 ubinaries [ubinaries_num++] = sym;
906 static UnmanagedSymbol*
907 lookup_unmanaged_binary (uintptr_t addr)
909 int i;
910 for (i = 0; i < ubinaries_num; ++i) {
911 UnmanagedSymbol *ubin = ubinaries [i];
912 if (addr >= ubin->addr && addr < ubin->addr + ubin->size) {
913 return ubin;
916 return NULL;
919 // For backwards compatibility.
920 enum {
921 TYPE_SAMPLE_UBIN = 2 << 4,
923 TYPE_COVERAGE = 9,
925 TYPE_COVERAGE_ASSEMBLY = 0 << 4,
926 TYPE_COVERAGE_METHOD = 1 << 4,
927 TYPE_COVERAGE_STATEMENT = 2 << 4,
928 TYPE_COVERAGE_CLASS = 3 << 4,
931 enum {
932 SAMPLE_CYCLES = 1,
933 SAMPLE_INSTRUCTIONS,
934 SAMPLE_CACHE_MISSES,
935 SAMPLE_CACHE_REFS,
936 SAMPLE_BRANCHES,
937 SAMPLE_BRANCH_MISSES,
940 enum {
941 MONO_GC_EVENT_MARK_START = 1,
942 MONO_GC_EVENT_MARK_END = 2,
943 MONO_GC_EVENT_RECLAIM_START = 3,
944 MONO_GC_EVENT_RECLAIM_END = 4,
947 static const char*
948 sample_type_name (int type)
950 switch (type) {
951 case SAMPLE_CYCLES: return "cycles";
952 case SAMPLE_INSTRUCTIONS: return "instructions retired";
953 case SAMPLE_CACHE_MISSES: return "cache misses";
954 case SAMPLE_CACHE_REFS: return "cache references";
955 case SAMPLE_BRANCHES: return "executed branches";
956 case SAMPLE_BRANCH_MISSES: return "unpredicted branches";
958 return "unknown";
961 static void
962 set_usym_parent (UnmanagedSymbol** cachedus, int count)
964 int i;
965 for (i = 0; i < count; ++i) {
966 UnmanagedSymbol *ubin = lookup_unmanaged_binary (cachedus [i]->addr);
967 if (ubin == cachedus [i])
968 continue;
969 cachedus [i]->parent = ubin;
973 static void
974 print_usym (UnmanagedSymbol* um)
976 if (um->parent)
977 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);
978 else
979 fprintf (outfile, "\t%6zd %6.2f %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name);
982 static int
983 sym_percent (uintptr_t sample_hits)
985 double pc;
986 if (verbose)
987 return 1;
988 pc = sample_hits*100.0/num_stat_samples;
989 return pc >= 0.1;
992 static void
993 dump_samples (void)
995 int i, u;
996 int count = 0, msize = 0;
997 int unmanaged_hits = 0;
998 int unresolved_hits = 0;
999 MethodDesc** cachedm = NULL;
1000 int ucount = 0, usize = 0;
1001 UnmanagedSymbol** cachedus = NULL;
1002 if (!num_stat_samples)
1003 return;
1004 qsort (usymbols, usymbols_num, sizeof (UnmanagedSymbol*), compare_usymbol_addr);
1005 for (i = 0; i < num_stat_samples; ++i) {
1006 MethodDesc *m = lookup_method_by_ip (stat_samples [i]);
1007 if (m) {
1008 if (!m->sample_hits) {
1009 if (count == msize) {
1010 msize *= 2;
1011 if (!msize)
1012 msize = 4;
1013 cachedm = (MethodDesc **) g_realloc (cachedm, sizeof (void*) * msize);
1015 cachedm [count++] = m;
1017 m->sample_hits++;
1018 } else {
1019 UnmanagedSymbol *usym = lookup_unmanaged_symbol (stat_samples [i]);
1020 if (!usym) {
1021 unresolved_hits++;
1022 //printf ("unmanaged hit at %p\n", (void*)stat_samples [i]);
1023 usym = lookup_unmanaged_binary (stat_samples [i]);
1025 if (usym) {
1026 if (!usym->sample_hits) {
1027 if (ucount == usize) {
1028 usize *= 2;
1029 if (!usize)
1030 usize = 4;
1031 cachedus = (UnmanagedSymbol **) g_realloc (cachedus, sizeof (void*) * usize);
1033 cachedus [ucount++] = usym;
1035 usym->sample_hits++;
1037 unmanaged_hits++;
1040 qsort (cachedm, count, sizeof (MethodDesc*), compare_method_samples);
1041 qsort (cachedus, ucount, sizeof (UnmanagedSymbol*), compare_usymbol_samples);
1042 set_usym_parent (cachedus, ucount);
1043 fprintf (outfile, "\nStatistical samples summary\n");
1044 fprintf (outfile, "\tSample type: %s\n", sample_type_name (stat_sample_desc [0]));
1045 fprintf (outfile, "\tUnmanaged hits: %6d (%4.1f%%)\n", unmanaged_hits, (100.0*unmanaged_hits)/num_stat_samples);
1046 fprintf (outfile, "\tManaged hits: %6d (%4.1f%%)\n", num_stat_samples - unmanaged_hits, (100.0*(num_stat_samples-unmanaged_hits))/num_stat_samples);
1047 fprintf (outfile, "\tUnresolved hits: %6d (%4.1f%%)\n", unresolved_hits, (100.0*unresolved_hits)/num_stat_samples);
1048 fprintf (outfile, "\t%6s %6s %s\n", "Hits", "%", "Method name");
1049 i = 0;
1050 u = 0;
1051 while (i < count || u < ucount) {
1052 if (i < count) {
1053 MethodDesc *m = cachedm [i];
1054 if (u < ucount) {
1055 UnmanagedSymbol *um = cachedus [u];
1056 if (um->sample_hits > m->sample_hits) {
1057 if (!sym_percent (um->sample_hits))
1058 break;
1059 print_usym (um);
1060 u++;
1061 continue;
1064 if (!sym_percent (m->sample_hits))
1065 break;
1066 fprintf (outfile, "\t%6d %6.2f %s\n", m->sample_hits, m->sample_hits*100.0/num_stat_samples, m->name);
1067 i++;
1068 continue;
1070 if (u < ucount) {
1071 UnmanagedSymbol *um = cachedus [u];
1072 if (!sym_percent (um->sample_hits))
1073 break;
1074 print_usym (um);
1075 u++;
1076 continue;
1081 typedef struct _HeapClassDesc HeapClassDesc;
1082 typedef struct {
1083 HeapClassDesc *klass;
1084 uint64_t count;
1085 } HeapClassRevRef;
1087 struct _HeapClassDesc {
1088 ClassDesc *klass;
1089 int64_t count;
1090 int64_t total_size;
1091 HeapClassRevRef *rev_hash;
1092 int rev_hash_size;
1093 int rev_count;
1094 uintptr_t pinned_references;
1095 uintptr_t root_references;
1098 static int
1099 add_rev_class_hashed (HeapClassRevRef *rev_hash, uintptr_t size, HeapClassDesc *hklass, uint64_t value)
1101 uintptr_t i;
1102 uintptr_t start_pos;
1103 start_pos = (hklass->klass->klass >> 2) % size;
1104 assert (start_pos < size);
1105 i = start_pos;
1106 do {
1107 if (rev_hash [i].klass == hklass) {
1108 rev_hash [i].count += value;
1109 return 0;
1110 } else if (!rev_hash [i].klass) {
1111 rev_hash [i].klass = hklass;
1112 rev_hash [i].count += value;
1113 start_pos = 0;
1114 for (i = 0; i < size; ++i)
1115 if (rev_hash [i].klass && rev_hash [i].klass->klass == hklass->klass)
1116 start_pos ++;
1117 assert (start_pos == 1);
1118 return 1;
1120 /* wrap around */
1121 if (++i == size)
1122 i = 0;
1123 } while (i != start_pos);
1124 /* should not happen */
1125 printf ("failed revref store\n");
1126 return 0;
1129 static void
1130 add_heap_class_rev (HeapClassDesc *from, HeapClassDesc *to)
1132 uintptr_t i;
1133 if (to->rev_count * 2 >= to->rev_hash_size) {
1134 HeapClassRevRef *n;
1135 uintptr_t old_size = to->rev_hash_size;
1136 to->rev_hash_size *= 2;
1137 if (to->rev_hash_size == 0)
1138 to->rev_hash_size = 4;
1139 n = (HeapClassRevRef *) g_calloc (sizeof (HeapClassRevRef) * to->rev_hash_size, 1);
1140 for (i = 0; i < old_size; ++i) {
1141 if (to->rev_hash [i].klass)
1142 add_rev_class_hashed (n, to->rev_hash_size, to->rev_hash [i].klass, to->rev_hash [i].count);
1144 if (to->rev_hash)
1145 g_free (to->rev_hash);
1146 to->rev_hash = n;
1148 to->rev_count += add_rev_class_hashed (to->rev_hash, to->rev_hash_size, from, 1);
1151 typedef struct {
1152 uintptr_t objaddr;
1153 HeapClassDesc *hklass;
1154 uintptr_t num_refs;
1155 uintptr_t refs [0];
1156 } HeapObjectDesc;
1158 typedef struct _HeapShot HeapShot;
1159 struct _HeapShot {
1160 HeapShot *next;
1161 uint64_t timestamp;
1162 int class_count;
1163 int hash_size;
1164 HeapClassDesc **class_hash;
1165 HeapClassDesc **sorted;
1166 HeapObjectDesc **objects_hash;
1167 uintptr_t objects_count;
1168 uintptr_t objects_hash_size;
1169 uintptr_t num_roots;
1170 uintptr_t *roots;
1171 uintptr_t *roots_extra;
1172 int *roots_types;
1175 static HeapShot *heap_shots = NULL;
1176 static int num_heap_shots = 0;
1178 static HeapShot*
1179 new_heap_shot (uint64_t timestamp)
1181 HeapShot *hs = (HeapShot *) g_calloc (sizeof (HeapShot), 1);
1182 hs->hash_size = 4;
1183 hs->class_hash = (HeapClassDesc **) g_calloc (sizeof (void*), hs->hash_size);
1184 hs->timestamp = timestamp;
1185 num_heap_shots++;
1186 hs->next = heap_shots;
1187 heap_shots = hs;
1188 return hs;
1191 static HeapClassDesc*
1192 heap_class_lookup (HeapShot *hs, ClassDesc *klass)
1194 int i;
1195 unsigned int start_pos;
1196 start_pos = ((uintptr_t)klass->klass >> 2) % hs->hash_size;
1197 i = start_pos;
1198 do {
1199 HeapClassDesc* cd = hs->class_hash [i];
1200 if (!cd)
1201 return NULL;
1202 if (cd->klass == klass)
1203 return cd;
1204 /* wrap around */
1205 if (++i == hs->hash_size)
1206 i = 0;
1207 } while (i != start_pos);
1208 return NULL;
1211 static int
1212 add_heap_hashed (HeapClassDesc **hash, HeapClassDesc **retv, uintptr_t hsize, ClassDesc *klass, uint64_t size, uint64_t count)
1214 uintptr_t i;
1215 uintptr_t start_pos;
1216 start_pos = ((uintptr_t)klass->klass >> 2) % hsize;
1217 i = start_pos;
1218 do {
1219 if (hash [i] && hash [i]->klass == klass) {
1220 hash [i]->total_size += size;
1221 hash [i]->count += count;
1222 *retv = hash [i];
1223 return 0;
1224 } else if (!hash [i]) {
1225 if (*retv) {
1226 hash [i] = *retv;
1227 return 1;
1229 hash [i] = (HeapClassDesc *) g_calloc (sizeof (HeapClassDesc), 1);
1230 hash [i]->klass = klass;
1231 hash [i]->total_size += size;
1232 hash [i]->count += count;
1233 *retv = hash [i];
1234 return 1;
1236 /* wrap around */
1237 if (++i == hsize)
1238 i = 0;
1239 } while (i != start_pos);
1240 /* should not happen */
1241 printf ("failed heap class store\n");
1242 return 0;
1245 static HeapClassDesc*
1246 add_heap_shot_class (HeapShot *hs, ClassDesc *klass, uint64_t size)
1248 HeapClassDesc *res;
1249 int i;
1250 if (hs->class_count * 2 >= hs->hash_size) {
1251 HeapClassDesc **n;
1252 int old_size = hs->hash_size;
1253 hs->hash_size *= 2;
1254 if (hs->hash_size == 0)
1255 hs->hash_size = 4;
1256 n = (HeapClassDesc **) g_calloc (sizeof (void*) * hs->hash_size, 1);
1257 for (i = 0; i < old_size; ++i) {
1258 res = hs->class_hash [i];
1259 if (hs->class_hash [i])
1260 add_heap_hashed (n, &res, hs->hash_size, hs->class_hash [i]->klass, hs->class_hash [i]->total_size, hs->class_hash [i]->count);
1262 if (hs->class_hash)
1263 g_free (hs->class_hash);
1264 hs->class_hash = n;
1266 res = NULL;
1267 hs->class_count += add_heap_hashed (hs->class_hash, &res, hs->hash_size, klass, size, 1);
1268 //if (res->count == 1)
1269 // printf ("added heap class: %s\n", res->klass->name);
1270 return res;
1273 static HeapObjectDesc*
1274 alloc_heap_obj (uintptr_t objaddr, HeapClassDesc *hklass, uintptr_t num_refs)
1276 HeapObjectDesc* ho = (HeapObjectDesc *) g_calloc (sizeof (HeapObjectDesc) + num_refs * sizeof (uintptr_t), 1);
1277 ho->objaddr = objaddr;
1278 ho->hklass = hklass;
1279 ho->num_refs = num_refs;
1280 return ho;
1283 static uintptr_t
1284 heap_shot_find_obj_slot (HeapShot *hs, uintptr_t objaddr)
1286 uintptr_t i;
1287 uintptr_t start_pos;
1288 HeapObjectDesc **hash = hs->objects_hash;
1289 if (hs->objects_hash_size == 0)
1290 return -1;
1291 start_pos = ((uintptr_t)objaddr >> 3) % hs->objects_hash_size;
1292 i = start_pos;
1293 do {
1294 if (hash [i] && hash [i]->objaddr == objaddr) {
1295 return i;
1296 } else if (!hash [i]) {
1297 break; /* fail */
1299 /* wrap around */
1300 if (++i == hs->objects_hash_size)
1301 i = 0;
1302 } while (i != start_pos);
1303 /* should not happen */
1304 //printf ("failed heap obj slot\n");
1305 return -1;
1308 static HeapObjectDesc*
1309 heap_shot_obj_add_refs (HeapShot *hs, uintptr_t objaddr, uintptr_t num, uintptr_t *ref_offset)
1311 HeapObjectDesc **hash = hs->objects_hash;
1312 uintptr_t i = heap_shot_find_obj_slot (hs, objaddr);
1313 if (i >= 0) {
1314 HeapObjectDesc* ho = alloc_heap_obj (objaddr, hash [i]->hklass, hash [i]->num_refs + num);
1315 *ref_offset = hash [i]->num_refs;
1316 memcpy (ho->refs, hash [i]->refs, hash [i]->num_refs * sizeof (uintptr_t));
1317 g_free (hash [i]);
1318 hash [i] = ho;
1319 return ho;
1321 /* should not happen */
1322 printf ("failed heap obj update\n");
1323 return NULL;
1327 static uintptr_t
1328 add_heap_hashed_obj (HeapObjectDesc **hash, uintptr_t hsize, HeapObjectDesc *obj)
1330 uintptr_t i;
1331 uintptr_t start_pos;
1332 start_pos = ((uintptr_t)obj->objaddr >> 3) % hsize;
1333 i = start_pos;
1334 do {
1335 if (hash [i] && hash [i]->objaddr == obj->objaddr) {
1336 printf ("duplicate object!\n");
1337 return 0;
1338 } else if (!hash [i]) {
1339 hash [i] = obj;
1340 return 1;
1342 /* wrap around */
1343 if (++i == hsize)
1344 i = 0;
1345 } while (i != start_pos);
1346 /* should not happen */
1347 printf ("failed heap obj store\n");
1348 return 0;
1351 static void
1352 add_heap_shot_obj (HeapShot *hs, HeapObjectDesc *obj)
1354 uintptr_t i;
1355 if (hs->objects_count * 2 >= hs->objects_hash_size) {
1356 HeapObjectDesc **n;
1357 uintptr_t old_size = hs->objects_hash_size;
1358 hs->objects_hash_size *= 2;
1359 if (hs->objects_hash_size == 0)
1360 hs->objects_hash_size = 4;
1361 n = (HeapObjectDesc **) g_calloc (sizeof (void*) * hs->objects_hash_size, 1);
1362 for (i = 0; i < old_size; ++i) {
1363 if (hs->objects_hash [i])
1364 add_heap_hashed_obj (n, hs->objects_hash_size, hs->objects_hash [i]);
1366 if (hs->objects_hash)
1367 g_free (hs->objects_hash);
1368 hs->objects_hash = n;
1370 hs->objects_count += add_heap_hashed_obj (hs->objects_hash, hs->objects_hash_size, obj);
1373 static void
1374 heap_shot_resolve_reverse_refs (HeapShot *hs)
1376 uintptr_t i;
1377 for (i = 0; i < hs->objects_hash_size; ++i) {
1378 uintptr_t r;
1379 HeapObjectDesc *ho = hs->objects_hash [i];
1380 if (!ho)
1381 continue;
1382 for (r = 0; r < ho->num_refs; ++r) {
1383 uintptr_t oi = heap_shot_find_obj_slot (hs, ho->refs [r]);
1384 add_heap_class_rev (ho->hklass, hs->objects_hash [oi]->hklass);
1389 #define MARK_GRAY 1
1390 #define MARK_BLACK 2
1392 static void
1393 heap_shot_mark_objects (HeapShot *hs)
1395 uintptr_t i, oi, r;
1396 unsigned char *marks;
1397 HeapObjectDesc *obj, *ref;
1398 int marked_some;
1399 uintptr_t num_marked = 0, num_unmarked;
1400 for (i = 0; i < hs->num_roots; ++i) {
1401 HeapClassDesc *cd;
1402 oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
1403 if (oi == -1) {
1404 continue;
1406 obj = hs->objects_hash [oi];
1407 cd = obj->hklass;
1408 if (hs->roots_types [i] & MONO_PROFILER_GC_ROOT_PINNING)
1409 cd->pinned_references++;
1410 cd->root_references++;
1412 if (!debug)
1413 return;
1414 /* consistency checks: it seems not all the objects are walked in the heap in some cases */
1415 marks = (unsigned char *) g_calloc (hs->objects_hash_size, 1);
1416 if (!marks)
1417 return;
1418 for (i = 0; i < hs->num_roots; ++i) {
1419 oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
1420 if (oi == -1) {
1421 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);
1422 continue;
1424 obj = hs->objects_hash [oi];
1425 if (!marks [oi]) {
1426 marks [oi] = obj->num_refs? MARK_GRAY: MARK_BLACK;
1427 num_marked++;
1430 marked_some = 1;
1431 while (marked_some) {
1432 marked_some = 0;
1433 for (i = 0; i < hs->objects_hash_size; ++i) {
1434 if (marks [i] != MARK_GRAY)
1435 continue;
1436 marks [i] = MARK_BLACK;
1437 obj = hs->objects_hash [i];
1438 for (r = 0; r < obj->num_refs; ++r) {
1439 oi = heap_shot_find_obj_slot (hs, obj->refs [r]);
1440 if (oi == -1) {
1441 fprintf (outfile, "referenced obj %p not found in heap\n", (void*)obj->refs [r]);
1442 continue;
1444 ref = hs->objects_hash [oi];
1445 if (!marks [oi]) {
1446 marks [oi] = ref->num_refs? MARK_GRAY: MARK_BLACK;
1449 marked_some++;
1453 num_unmarked = 0;
1454 for (i = 0; i < hs->objects_hash_size; ++i) {
1455 if (hs->objects_hash [i] && !marks [i]) {
1456 num_unmarked++;
1457 fprintf (outfile, "object %p (%s) unmarked\n", (void*)hs->objects_hash [i], hs->objects_hash [i]->hklass->klass->name);
1460 fprintf (outfile, "Total unmarked: %zd/%zd\n", num_unmarked, hs->objects_count);
1461 g_free (marks);
1464 static void
1465 heap_shot_free_objects (HeapShot *hs)
1467 uintptr_t i;
1468 for (i = 0; i < hs->objects_hash_size; ++i) {
1469 HeapObjectDesc *ho = hs->objects_hash [i];
1470 if (ho)
1471 g_free (ho);
1473 if (hs->objects_hash)
1474 g_free (hs->objects_hash);
1475 hs->objects_hash = NULL;
1476 hs->objects_hash_size = 0;
1477 hs->objects_count = 0;
1481 struct _BackTrace {
1482 BackTrace *next;
1483 unsigned int hash;
1484 int count;
1485 int id;
1486 MethodDesc *methods [1];
1489 static BackTrace *backtrace_hash [HASH_SIZE];
1490 static BackTrace **backtraces = NULL;
1491 static int num_backtraces = 0;
1492 static int next_backtrace = 0;
1494 static int
1495 hash_backtrace (int count, MethodDesc **methods)
1497 int hash = count;
1498 int i;
1499 for (i = 0; i < count; ++i) {
1500 hash = (hash << 5) - hash + methods [i]->method;
1502 return hash;
1505 static int
1506 compare_backtrace (BackTrace *bt, int count, MethodDesc **methods)
1508 int i;
1509 if (bt->count != count)
1510 return 0;
1511 for (i = 0; i < count; ++i)
1512 if (methods [i] != bt->methods [i])
1513 return 0;
1514 return 1;
1517 static BackTrace*
1518 add_backtrace (int count, MethodDesc **methods)
1520 int hash = hash_backtrace (count, methods);
1521 int slot = (hash & 0xffff) % HASH_SIZE;
1522 BackTrace *bt = backtrace_hash [slot];
1523 while (bt) {
1524 if (bt->hash == hash && compare_backtrace (bt, count, methods))
1525 return bt;
1526 bt = bt->next;
1528 bt = (BackTrace *) g_malloc (sizeof (BackTrace) + ((count - 1) * sizeof (void*)));
1529 bt->next = backtrace_hash [slot];
1530 backtrace_hash [slot] = bt;
1531 if (next_backtrace == num_backtraces) {
1532 num_backtraces *= 2;
1533 if (!num_backtraces)
1534 num_backtraces = 16;
1535 backtraces = (BackTrace **) g_realloc (backtraces, sizeof (void*) * num_backtraces);
1537 bt->id = next_backtrace++;
1538 backtraces [bt->id] = bt;
1539 bt->count = count;
1540 bt->hash = hash;
1541 for (slot = 0; slot < count; ++slot)
1542 bt->methods [slot] = methods [slot];
1544 return bt;
1547 typedef struct _MonitorDesc MonitorDesc;
1548 typedef struct _ThreadContext ThreadContext;
1549 typedef struct _DomainContext DomainContext;
1550 typedef struct _RemCtxContext RemCtxContext;
1552 typedef struct {
1553 FILE *file;
1554 #if defined (HAVE_SYS_ZLIB)
1555 gzFile gzfile;
1556 #endif
1557 unsigned char *buf;
1558 int size;
1559 int data_version;
1560 int version_major;
1561 int version_minor;
1562 int timer_overhead;
1563 int pid;
1564 int port;
1565 char *args;
1566 char *arch;
1567 char *os;
1568 uint64_t startup_time;
1569 ThreadContext *threads;
1570 ThreadContext *current_thread;
1571 DomainContext *domains;
1572 DomainContext *current_domain;
1573 RemCtxContext *remctxs;
1574 RemCtxContext *current_remctx;
1575 } ProfContext;
1577 struct _ThreadContext {
1578 ThreadContext *next;
1579 intptr_t thread_id;
1580 char *name;
1581 /* emulated stack */
1582 MethodDesc **stack;
1583 uint64_t *time_stack;
1584 uint64_t *callee_time_stack;
1585 uint64_t last_time;
1586 uint64_t contention_start;
1587 MonitorDesc *monitor;
1588 int stack_size;
1589 int stack_id;
1590 HeapShot *current_heap_shot;
1591 uintptr_t num_roots;
1592 uintptr_t size_roots;
1593 uintptr_t *roots;
1594 uintptr_t *roots_extra;
1595 int *roots_types;
1596 uint64_t gc_start_times [3];
1599 struct _DomainContext {
1600 DomainContext *next;
1601 intptr_t domain_id;
1602 const char *friendly_name;
1605 struct _RemCtxContext {
1606 RemCtxContext *next;
1607 intptr_t remctx_id;
1608 intptr_t domain_id;
1611 static void
1612 ensure_buffer (ProfContext *ctx, int size)
1614 if (ctx->size < size) {
1615 ctx->buf = (unsigned char *) g_realloc (ctx->buf, size);
1616 ctx->size = size;
1620 static int
1621 load_data (ProfContext *ctx, int size)
1623 ensure_buffer (ctx, size);
1624 #if defined (HAVE_SYS_ZLIB)
1625 if (ctx->gzfile) {
1626 int r = gzread (ctx->gzfile, ctx->buf, size);
1627 if (r == 0)
1628 return size == 0? 1: 0;
1629 return r == size;
1630 } else
1631 #endif
1633 int r = fread (ctx->buf, size, 1, ctx->file);
1634 if (r == 0)
1635 return size == 0? 1: 0;
1636 return r;
1640 static ThreadContext*
1641 get_thread (ProfContext *ctx, intptr_t thread_id)
1643 ThreadContext *thread;
1644 if (ctx->current_thread && ctx->current_thread->thread_id == thread_id)
1645 return ctx->current_thread;
1646 thread = ctx->threads;
1647 while (thread) {
1648 if (thread->thread_id == thread_id) {
1649 return thread;
1651 thread = thread->next;
1653 thread = (ThreadContext *) g_calloc (sizeof (ThreadContext), 1);
1654 thread->next = ctx->threads;
1655 ctx->threads = thread;
1656 thread->thread_id = thread_id;
1657 thread->last_time = 0;
1658 thread->stack_id = 0;
1659 thread->stack_size = 32;
1660 thread->stack = (MethodDesc **) g_malloc (thread->stack_size * sizeof (void*));
1661 thread->time_stack = (uint64_t *) g_malloc (thread->stack_size * sizeof (uint64_t));
1662 thread->callee_time_stack = (uint64_t *) g_malloc (thread->stack_size * sizeof (uint64_t));
1663 return thread;
1666 static DomainContext *
1667 get_domain (ProfContext *ctx, intptr_t domain_id)
1669 if (ctx->current_domain && ctx->current_domain->domain_id == domain_id)
1670 return ctx->current_domain;
1672 DomainContext *domain = ctx->domains;
1674 while (domain) {
1675 if (domain->domain_id == domain_id)
1676 return domain;
1678 domain = domain->next;
1681 domain = (DomainContext *) g_calloc (sizeof (DomainContext), 1);
1682 domain->next = ctx->domains;
1683 ctx->domains = domain;
1684 domain->domain_id = domain_id;
1686 return domain;
1689 static RemCtxContext *
1690 get_remctx (ProfContext *ctx, intptr_t remctx_id)
1692 if (ctx->current_remctx && ctx->current_remctx->remctx_id == remctx_id)
1693 return ctx->current_remctx;
1695 RemCtxContext *remctx = ctx->remctxs;
1697 while (remctx) {
1698 if (remctx->remctx_id == remctx_id)
1699 return remctx;
1701 remctx = remctx->next;
1704 remctx = (RemCtxContext *) g_calloc (sizeof (RemCtxContext), 1);
1705 remctx->next = ctx->remctxs;
1706 ctx->remctxs = remctx;
1707 remctx->remctx_id = remctx_id;
1709 return remctx;
1712 static ThreadContext*
1713 load_thread (ProfContext *ctx, intptr_t thread_id)
1715 ThreadContext *thread = get_thread (ctx, thread_id);
1716 ctx->current_thread = thread;
1717 return thread;
1720 static void
1721 ensure_thread_stack (ThreadContext *thread)
1723 if (thread->stack_id == thread->stack_size) {
1724 thread->stack_size *= 2;
1725 thread->stack = (MethodDesc **) g_realloc (thread->stack, thread->stack_size * sizeof (void*));
1726 thread->time_stack = (uint64_t *) g_realloc (thread->time_stack, thread->stack_size * sizeof (uint64_t));
1727 thread->callee_time_stack = (uint64_t *) g_realloc (thread->callee_time_stack, thread->stack_size * sizeof (uint64_t));
1731 static int
1732 add_trace_hashed (CallContext *traces, int size, BackTrace *bt, uint64_t value)
1734 int i;
1735 unsigned int start_pos;
1736 start_pos = bt->hash % size;
1737 i = start_pos;
1738 do {
1739 if (traces [i].bt == bt) {
1740 traces [i].count += value;
1741 return 0;
1742 } else if (!traces [i].bt) {
1743 traces [i].bt = bt;
1744 traces [i].count += value;
1745 return 1;
1747 /* wrap around */
1748 if (++i == size)
1749 i = 0;
1750 } while (i != start_pos);
1751 /* should not happen */
1752 printf ("failed trace store\n");
1753 return 0;
1756 static void
1757 add_trace_bt (BackTrace *bt, TraceDesc *trace, uint64_t value)
1759 int i;
1760 if (!collect_traces)
1761 return;
1762 if (trace->count * 2 >= trace->size) {
1763 CallContext *n;
1764 int old_size = trace->size;
1765 trace->size *= 2;
1766 if (trace->size == 0)
1767 trace->size = 4;
1768 n = (CallContext *) g_calloc (sizeof (CallContext) * trace->size, 1);
1769 for (i = 0; i < old_size; ++i) {
1770 if (trace->traces [i].bt)
1771 add_trace_hashed (n, trace->size, trace->traces [i].bt, trace->traces [i].count);
1773 if (trace->traces)
1774 g_free (trace->traces);
1775 trace->traces = n;
1777 trace->count += add_trace_hashed (trace->traces, trace->size, bt, value);
1780 static BackTrace*
1781 add_trace_thread (ThreadContext *thread, TraceDesc *trace, uint64_t value)
1783 BackTrace *bt;
1784 int count = thread->stack_id;
1785 if (!collect_traces)
1786 return NULL;
1787 if (count > trace_max)
1788 count = trace_max;
1789 bt = add_backtrace (count, thread->stack + thread->stack_id - count);
1790 add_trace_bt (bt, trace, value);
1791 return bt;
1794 static BackTrace*
1795 add_trace_methods (MethodDesc **methods, int count, TraceDesc *trace, uint64_t value)
1797 BackTrace *bt;
1798 if (!collect_traces)
1799 return NULL;
1800 if (count > trace_max)
1801 count = trace_max;
1802 bt = add_backtrace (count, methods);
1803 add_trace_bt (bt, trace, value);
1804 return bt;
1807 static void
1808 thread_add_root (ThreadContext *ctx, uintptr_t obj, int root_type, uintptr_t extra_info)
1810 if (ctx->num_roots == ctx->size_roots) {
1811 int new_size = ctx->size_roots * 2;
1812 if (!new_size)
1813 new_size = 4;
1814 ctx->roots = (uintptr_t *) g_realloc (ctx->roots, new_size * sizeof (uintptr_t));
1815 ctx->roots_extra = (uintptr_t *) g_realloc (ctx->roots_extra, new_size * sizeof (uintptr_t));
1816 ctx->roots_types = (int *) g_realloc (ctx->roots_types, new_size * sizeof (int));
1817 ctx->size_roots = new_size;
1819 ctx->roots_types [ctx->num_roots] = root_type;
1820 ctx->roots_extra [ctx->num_roots] = extra_info;
1821 ctx->roots [ctx->num_roots++] = obj;
1824 static int
1825 compare_callc (const void *a, const void *b)
1827 const CallContext *A = (const CallContext *)a;
1828 const CallContext *B = (const CallContext *)b;
1829 if (B->count == A->count)
1830 return 0;
1831 if (B->count < A->count)
1832 return -1;
1833 return 1;
1836 static void
1837 sort_context_array (TraceDesc* traces)
1839 int i, j;
1840 for (i = 0, j = 0; i < traces->size; ++i) {
1841 if (traces->traces [i].bt) {
1842 traces->traces [j].bt = traces->traces [i].bt;
1843 traces->traces [j].count = traces->traces [i].count;
1844 j++;
1847 qsort (traces->traces, traces->count, sizeof (CallContext), compare_callc);
1850 static void
1851 push_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
1853 ensure_thread_stack (thread);
1854 thread->time_stack [thread->stack_id] = timestamp;
1855 thread->callee_time_stack [thread->stack_id] = 0;
1856 thread->stack [thread->stack_id++] = method;
1857 method->recurse_count++;
1860 static void
1861 pop_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
1863 method->recurse_count--;
1864 if (thread->stack_id > 0 && thread->stack [thread->stack_id - 1] == method) {
1865 uint64_t tdiff;
1866 thread->stack_id--;
1867 method->calls++;
1868 if (timestamp < thread->time_stack [thread->stack_id])
1869 fprintf (outfile, "time went backwards for %s\n", method->name);
1870 tdiff = timestamp - thread->time_stack [thread->stack_id];
1871 if (thread->callee_time_stack [thread->stack_id] > tdiff)
1872 fprintf (outfile, "callee time bigger for %s\n", method->name);
1873 method->self_time += tdiff - thread->callee_time_stack [thread->stack_id];
1874 method->callee_time += thread->callee_time_stack [thread->stack_id];
1875 if (thread->stack_id)
1876 thread->callee_time_stack [thread->stack_id - 1] += tdiff;
1877 //fprintf (outfile, "method %s took %d\n", method->name, (int)(tdiff/1000));
1878 } else {
1879 fprintf (outfile, "unmatched leave at stack pos: %d for method %s\n", thread->stack_id, method->name);
1883 typedef struct {
1884 uint64_t total_time;
1885 uint64_t max_time;
1886 int count;
1887 } GCDesc;
1888 static GCDesc gc_info [3];
1889 static uint64_t max_heap_size;
1890 static uint64_t gc_object_moves;
1891 static int gc_resizes;
1892 typedef struct {
1893 uint64_t created;
1894 uint64_t destroyed;
1895 uint64_t live;
1896 uint64_t max_live;
1897 TraceDesc traces;
1898 TraceDesc destroy_traces;
1899 } HandleInfo;
1900 static HandleInfo handle_info [4];
1902 static const char*
1903 gc_event_name (int ev)
1905 switch (ev) {
1906 case MONO_GC_EVENT_START: return "start";
1907 case MONO_GC_EVENT_MARK_START: return "mark start";
1908 case MONO_GC_EVENT_MARK_END: return "mark end";
1909 case MONO_GC_EVENT_RECLAIM_START: return "reclaim start";
1910 case MONO_GC_EVENT_RECLAIM_END: return "reclaim end";
1911 case MONO_GC_EVENT_END: return "end";
1912 case MONO_GC_EVENT_PRE_STOP_WORLD: return "pre stop";
1913 case MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED: return "pre stop lock";
1914 case MONO_GC_EVENT_POST_STOP_WORLD: return "post stop";
1915 case MONO_GC_EVENT_PRE_START_WORLD: return "pre start";
1916 case MONO_GC_EVENT_POST_START_WORLD: return "post start";
1917 case MONO_GC_EVENT_POST_START_WORLD_UNLOCKED: return "post start unlock";
1918 default:
1919 return "unknown";
1923 static const char*
1924 sync_point_name (int type)
1926 switch (type) {
1927 case SYNC_POINT_PERIODIC: return "periodic";
1928 case SYNC_POINT_WORLD_STOP: return "world stop";
1929 case SYNC_POINT_WORLD_START: return "world start";
1930 default:
1931 return "unknown";
1935 static uint64_t clause_summary [MONO_EXCEPTION_CLAUSE_FAULT + 1];
1936 static uint64_t throw_count = 0;
1937 static TraceDesc exc_traces;
1939 static const char*
1940 clause_name (int type)
1942 switch (type) {
1943 case MONO_EXCEPTION_CLAUSE_NONE: return "catch";
1944 case MONO_EXCEPTION_CLAUSE_FILTER: return "filter";
1945 case MONO_EXCEPTION_CLAUSE_FINALLY: return "finally";
1946 case MONO_EXCEPTION_CLAUSE_FAULT: return "fault";
1947 default: return "invalid";
1951 static uint64_t monitor_contention;
1952 static uint64_t monitor_failed;
1953 static uint64_t monitor_acquired;
1955 struct _MonitorDesc {
1956 MonitorDesc *next;
1957 uintptr_t objid;
1958 uintptr_t contentions;
1959 uint64_t wait_time;
1960 uint64_t max_wait_time;
1961 TraceDesc traces;
1964 static MonitorDesc* monitor_hash [SMALL_HASH_SIZE] = {0};
1965 static int num_monitors = 0;
1967 static MonitorDesc*
1968 lookup_monitor (uintptr_t objid)
1970 int slot = ((objid >> 3) & 0xffff) % SMALL_HASH_SIZE;
1971 MonitorDesc *cd = monitor_hash [slot];
1972 while (cd && cd->objid != objid)
1973 cd = cd->next;
1974 if (!cd) {
1975 cd = (MonitorDesc *) g_calloc (sizeof (MonitorDesc), 1);
1976 cd->objid = objid;
1977 cd->next = monitor_hash [slot];
1978 monitor_hash [slot] = cd;
1979 num_monitors++;
1981 return cd;
1984 static const char*
1985 monitor_ev_name (int ev)
1987 switch (ev) {
1988 case MONO_PROFILER_MONITOR_CONTENTION: return "contended";
1989 case MONO_PROFILER_MONITOR_DONE: return "acquired";
1990 case MONO_PROFILER_MONITOR_FAIL: return "not taken";
1991 default: return "invalid";
1995 static const char*
1996 get_handle_name (int htype)
1998 switch (htype) {
1999 case 0: return "weak";
2000 case 1: return "weaktrack";
2001 case 2: return "normal";
2002 case 3: return "pinned";
2003 default: return "unknown";
2007 static const char*
2008 get_root_name (int rtype)
2010 switch (rtype & MONO_PROFILER_GC_ROOT_TYPEMASK) {
2011 case MONO_PROFILER_GC_ROOT_STACK: return "stack";
2012 case MONO_PROFILER_GC_ROOT_FINALIZER: return "finalizer";
2013 case MONO_PROFILER_GC_ROOT_HANDLE: return "handle";
2014 case MONO_PROFILER_GC_ROOT_OTHER: return "other";
2015 case MONO_PROFILER_GC_ROOT_MISC: return "misc";
2016 default: return "unknown";
2020 static uint64_t
2021 decode_uleb128 (uint8_t *buf, uint8_t **endbuf)
2023 uint64_t res = 0;
2024 int shift = 0;
2026 while (1) {
2027 uint8_t b = *buf++;
2028 res |= (((uint64_t) (b & 0x7f)) << shift);
2030 if (!(b & 0x80))
2031 break;
2033 shift += 7;
2036 *endbuf = buf;
2038 return res;
2041 static intptr_t
2042 decode_sleb128 (uint8_t *buf, uint8_t **endbuf)
2044 uint8_t *p = buf;
2045 intptr_t res = 0;
2046 int shift = 0;
2048 while (1) {
2049 uint8_t b = *p;
2050 p++;
2052 res = res | (((intptr_t) (b & 0x7f)) << shift);
2053 shift += 7;
2055 if (!(b & 0x80)) {
2056 if (shift < sizeof (intptr_t) * 8 && (b & 0x40))
2057 res |= - ((intptr_t) 1 << shift);
2059 break;
2063 *endbuf = p;
2065 return res;
2068 static MethodDesc**
2069 decode_bt (ProfContext *ctx, MethodDesc** sframes, int *size, unsigned char *p, unsigned char **endp, intptr_t ptr_base, intptr_t *method_base)
2071 MethodDesc **frames;
2072 int i;
2073 if (ctx->data_version < 13)
2074 decode_uleb128 (p, &p); /* flags */
2075 int count = decode_uleb128 (p, &p);
2076 if (count > *size)
2077 frames = (MethodDesc **) g_malloc (count * sizeof (void*));
2078 else
2079 frames = sframes;
2080 for (i = 0; i < count; ++i) {
2081 intptr_t ptrdiff = decode_sleb128 (p, &p);
2082 if (ctx->data_version > 12) {
2083 *method_base += ptrdiff;
2084 frames [i] = lookup_method (*method_base);
2085 } else {
2086 frames [i] = lookup_method (ptr_base + ptrdiff);
2089 *size = count;
2090 *endp = p;
2091 return frames;
2094 static void
2095 tracked_creation (uintptr_t obj, ClassDesc *cd, uint64_t size, BackTrace *bt, uint64_t timestamp)
2097 int i;
2098 for (i = 0; i < num_tracked_objects; ++i) {
2099 if (tracked_objects [i] != obj)
2100 continue;
2101 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);
2102 if (bt && bt->count) {
2103 int k;
2104 for (k = 0; k < bt->count; ++k)
2105 fprintf (outfile, "\t%s\n", bt->methods [k]->name);
2110 static void
2111 track_handle (uintptr_t obj, int htype, uint32_t handle, BackTrace *bt, uint64_t timestamp)
2113 int i;
2114 for (i = 0; i < num_tracked_objects; ++i) {
2115 if (tracked_objects [i] != obj)
2116 continue;
2117 fprintf (outfile, "Object %p referenced from handle %u at %.3f secs.\n", (void*)obj, handle, (timestamp - startup_time) / 1000000000.0);
2118 if (bt && bt->count) {
2119 int k;
2120 for (k = 0; k < bt->count; ++k)
2121 fprintf (outfile, "\t%s\n", bt->methods [k]->name);
2126 static void
2127 track_move (uintptr_t src, uintptr_t dst)
2129 int i;
2130 for (i = 0; i < num_tracked_objects; ++i) {
2131 if (tracked_objects [i] == src)
2132 fprintf (outfile, "Object %p moved to %p\n", (void*)src, (void*)dst);
2133 else if (tracked_objects [i] == dst)
2134 fprintf (outfile, "Object %p moved from %p\n", (void*)dst, (void*)src);
2138 static void
2139 track_obj_reference (uintptr_t obj, uintptr_t parent, ClassDesc *cd)
2141 int i;
2142 for (i = 0; i < num_tracked_objects; ++i) {
2143 if (tracked_objects [i] == obj)
2144 fprintf (outfile, "Object %p referenced from %p (%s).\n", (void*)obj, (void*)parent, cd->name);
2148 static void
2149 found_object (uintptr_t obj)
2151 num_tracked_objects ++;
2152 tracked_objects = (uintptr_t *) g_realloc (tracked_objects, num_tracked_objects * sizeof (tracked_objects [0]));
2153 tracked_objects [num_tracked_objects - 1] = obj;
2156 static int num_jit_helpers = 0;
2157 static int jit_helpers_code_size = 0;
2159 static const char*
2160 code_buffer_desc (int type)
2162 switch (type) {
2163 case MONO_PROFILER_CODE_BUFFER_METHOD:
2164 return "method";
2165 case MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE:
2166 return "method trampoline";
2167 case MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE:
2168 return "unbox trampoline";
2169 case MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE:
2170 return "imt trampoline";
2171 case MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE:
2172 return "generics trampoline";
2173 case MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE:
2174 return "specific trampoline";
2175 case MONO_PROFILER_CODE_BUFFER_HELPER:
2176 return "misc helper";
2177 case MONO_PROFILER_CODE_BUFFER_MONITOR:
2178 return "monitor/lock";
2179 case MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE:
2180 return "delegate invoke";
2181 case MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING:
2182 return "exception handling";
2183 default:
2184 return "unspecified";
2188 #define OBJ_ADDR(diff) ((obj_base + diff) << 3)
2189 #define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
2192 /* Stats */
2193 #define BUFFER_HEADER_SIZE 48
2195 typedef struct {
2196 int count, min_size, max_size, bytes;
2197 } EventStat;
2199 static int buffer_count;
2200 static EventStat stats [256];
2202 static void
2203 record_event_stats (int type, int size)
2205 ++stats [type].count;
2206 if (!stats [type].min_size)
2207 stats [type].min_size = size;
2208 stats [type].min_size = MIN (stats [type].min_size, size);
2209 stats [type].max_size = MAX (stats [type].max_size, size);
2210 stats [type].bytes += size;
2213 static int
2214 decode_buffer (ProfContext *ctx)
2216 unsigned char *p;
2217 unsigned char *end;
2218 intptr_t thread_id;
2219 intptr_t ptr_base;
2220 intptr_t obj_base;
2221 intptr_t method_base;
2222 uint64_t time_base;
2223 uint64_t file_offset;
2224 int len, i;
2225 ThreadContext *thread;
2227 #ifdef HAVE_SYS_ZLIB
2228 if (ctx->gzfile)
2229 file_offset = gztell (ctx->gzfile);
2230 else
2231 #endif
2232 file_offset = ftell (ctx->file);
2233 if (!load_data (ctx, 48))
2234 return 0;
2235 p = ctx->buf;
2236 if (read_int32 (p) != BUF_ID) {
2237 fprintf (outfile, "Incorrect buffer id: 0x%x\n", read_int32 (p));
2238 for (i = 0; i < 48; ++i) {
2239 fprintf (outfile, "0x%x%s", p [i], i % 8?" ":"\n");
2241 return 0;
2243 len = read_int32 (p + 4);
2244 time_base = read_int64 (p + 8);
2245 ptr_base = read_int64 (p + 16);
2246 obj_base = read_int64 (p + 24);
2247 thread_id = read_int64 (p + 32);
2248 method_base = read_int64 (p + 40);
2249 if (debug)
2250 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);
2251 thread = load_thread (ctx, thread_id);
2252 if (!load_data (ctx, len))
2253 return 0;
2255 ++buffer_count;
2257 if (!startup_time) {
2258 startup_time = time_base;
2259 if (use_time_filter) {
2260 time_from += startup_time;
2261 time_to += startup_time;
2264 for (i = 0; i < thread->stack_id; ++i)
2265 thread->stack [i]->recurse_count++;
2266 p = ctx->buf;
2267 end = p + len;
2268 while (p < end) {
2269 unsigned char *start = p;
2270 unsigned char event = *p;
2271 switch (*p & 0xf) {
2272 case TYPE_GC: {
2273 int subtype = *p & 0xf0;
2274 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2275 LOG_TIME (time_base, tdiff);
2276 time_base += tdiff;
2277 if (subtype == TYPE_GC_RESIZE) {
2278 uint64_t new_size = decode_uleb128 (p, &p);
2279 if (debug)
2280 fprintf (outfile, "gc heap resized to %llu\n", (unsigned long long) new_size);
2281 gc_resizes++;
2282 if (new_size > max_heap_size)
2283 max_heap_size = new_size;
2284 } else if (subtype == TYPE_GC_EVENT) {
2285 uint64_t ev;
2286 if (ctx->data_version > 12)
2287 ev = *p++;
2288 else
2289 ev = decode_uleb128 (p, &p);
2290 int gen;
2291 if (ctx->data_version > 12)
2292 gen = *p++;
2293 else
2294 gen = decode_uleb128 (p, &p);
2295 if (debug)
2296 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);
2297 if (gen > 2) {
2298 fprintf (outfile, "incorrect gc gen: %d\n", gen);
2299 break;
2301 if (ev == MONO_GC_EVENT_START) {
2302 thread->gc_start_times [gen] = time_base;
2303 gc_info [gen].count++;
2304 } else if (ev == MONO_GC_EVENT_END) {
2305 tdiff = time_base - thread->gc_start_times [gen];
2306 gc_info [gen].total_time += tdiff;
2307 if (tdiff > gc_info [gen].max_time)
2308 gc_info [gen].max_time = tdiff;
2310 } else if (subtype == TYPE_GC_MOVE) {
2311 int j, num = decode_uleb128 (p, &p);
2312 gc_object_moves += num / 2;
2313 for (j = 0; j < num; j += 2) {
2314 intptr_t obj1diff = decode_sleb128 (p, &p);
2315 intptr_t obj2diff = decode_sleb128 (p, &p);
2316 if (num_tracked_objects)
2317 track_move (OBJ_ADDR (obj1diff), OBJ_ADDR (obj2diff));
2318 if (debug) {
2319 fprintf (outfile, "moved obj %p to %p\n", (void*)OBJ_ADDR (obj1diff), (void*)OBJ_ADDR (obj2diff));
2322 } else if (subtype == TYPE_GC_HANDLE_CREATED || subtype == TYPE_GC_HANDLE_CREATED_BT) {
2323 int has_bt = subtype == TYPE_GC_HANDLE_CREATED_BT;
2324 int num_bt = 0;
2325 MethodDesc *sframes [8];
2326 MethodDesc **frames = sframes;
2327 int htype = decode_uleb128 (p, &p);
2328 uint32_t handle = decode_uleb128 (p, &p);
2329 intptr_t objdiff = decode_sleb128 (p, &p);
2330 if (has_bt) {
2331 num_bt = 8;
2332 frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2333 if (!frames) {
2334 fprintf (outfile, "Cannot load backtrace\n");
2335 return 0;
2338 if (htype > 3)
2339 return 0;
2340 if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2341 handle_info [htype].created++;
2342 handle_info [htype].live++;
2343 if (handle_info [htype].live > handle_info [htype].max_live)
2344 handle_info [htype].max_live = handle_info [htype].live;
2345 BackTrace *bt;
2346 if (has_bt)
2347 bt = add_trace_methods (frames, num_bt, &handle_info [htype].traces, 1);
2348 else
2349 bt = add_trace_thread (thread, &handle_info [htype].traces, 1);
2350 if (num_tracked_objects)
2351 track_handle (OBJ_ADDR (objdiff), htype, handle, bt, time_base);
2353 if (debug)
2354 fprintf (outfile, "handle (%s) %u created for object %p\n", get_handle_name (htype), handle, (void*)OBJ_ADDR (objdiff));
2355 if (frames != sframes)
2356 g_free (frames);
2357 } else if (subtype == TYPE_GC_HANDLE_DESTROYED || subtype == TYPE_GC_HANDLE_DESTROYED_BT) {
2358 int has_bt = subtype == TYPE_GC_HANDLE_DESTROYED_BT;
2359 int num_bt = 0;
2360 MethodDesc *sframes [8];
2361 MethodDesc **frames = sframes;
2362 int htype = decode_uleb128 (p, &p);
2363 uint32_t handle = decode_uleb128 (p, &p);
2364 if (has_bt) {
2365 num_bt = 8;
2366 frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2367 if (!frames) {
2368 fprintf (outfile, "Cannot load backtrace\n");
2369 return 0;
2372 if (htype > 3)
2373 return 0;
2374 if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2375 handle_info [htype].destroyed ++;
2376 handle_info [htype].live--;
2377 BackTrace *bt;
2378 if (has_bt)
2379 bt = add_trace_methods (frames, num_bt, &handle_info [htype].destroy_traces, 1);
2380 else
2381 bt = add_trace_thread (thread, &handle_info [htype].destroy_traces, 1);
2382 /* TODO: track_handle_free () - would need to record and keep track of the associated object address... */
2384 if (debug)
2385 fprintf (outfile, "handle (%s) %u destroyed\n", get_handle_name (htype), handle);
2386 if (frames != sframes)
2387 g_free (frames);
2388 } else if (subtype == TYPE_GC_FINALIZE_START) {
2389 // TODO: Generate a finalizer report based on these events.
2390 if (debug)
2391 fprintf (outfile, "gc finalizer queue being processed at %llu\n", (unsigned long long) time_base);
2392 } else if (subtype == TYPE_GC_FINALIZE_END) {
2393 if (debug)
2394 fprintf (outfile, "gc finalizer queue finished processing at %llu\n", (unsigned long long) time_base);
2395 } else if (subtype == TYPE_GC_FINALIZE_OBJECT_START) {
2396 intptr_t objdiff = decode_sleb128 (p, &p);
2397 if (debug)
2398 fprintf (outfile, "gc finalizing object %p at %llu\n", (void *) OBJ_ADDR (objdiff), (unsigned long long) time_base);
2399 } else if (subtype == TYPE_GC_FINALIZE_OBJECT_END) {
2400 intptr_t objdiff = decode_sleb128 (p, &p);
2401 if (debug)
2402 fprintf (outfile, "gc finalized object %p at %llu\n", (void *) OBJ_ADDR (objdiff), (unsigned long long) time_base);
2404 break;
2406 case TYPE_METADATA: {
2407 int subtype = *p & 0xf0;
2408 const char *load_str = subtype == TYPE_END_LOAD ? "loaded" : "unloaded";
2409 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2410 int mtype = *p++;
2411 intptr_t ptrdiff = decode_sleb128 (p, &p);
2412 LOG_TIME (time_base, tdiff);
2413 time_base += tdiff;
2414 if (mtype == TYPE_CLASS) {
2415 intptr_t imptrdiff = decode_sleb128 (p, &p);
2416 if (ctx->data_version < 13)
2417 decode_uleb128 (p, &p); /* flags */
2418 if (debug)
2419 fprintf (outfile, "%s class %p (%s in %p) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (void*)(ptr_base + imptrdiff), (unsigned long long) time_base);
2420 add_class (ptr_base + ptrdiff, (char*)p);
2421 while (*p) p++;
2422 p++;
2423 } else if (mtype == TYPE_IMAGE) {
2424 if (ctx->data_version < 13)
2425 decode_uleb128 (p, &p); /* flags */
2426 if (debug)
2427 fprintf (outfile, "%s image %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
2428 if (subtype == TYPE_END_LOAD)
2429 add_image (ptr_base + ptrdiff, (char*)p);
2430 while (*p) p++;
2431 p++;
2432 if (ctx->data_version >= 16) {
2433 while (*p) p++; // mvid
2434 p++;
2436 } else if (mtype == TYPE_ASSEMBLY) {
2437 if (ctx->data_version > 13)
2438 decode_sleb128 (p, &p); // image
2439 if (ctx->data_version < 13)
2440 decode_uleb128 (p, &p); /* flags */
2441 if (debug)
2442 fprintf (outfile, "%s assembly %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
2443 if (subtype == TYPE_END_LOAD)
2444 add_assembly (ptr_base + ptrdiff, (char*)p);
2445 while (*p) p++;
2446 p++;
2447 } else if (mtype == TYPE_DOMAIN) {
2448 if (ctx->data_version < 13)
2449 decode_uleb128 (p, &p); /* flags */
2450 DomainContext *nd = get_domain (ctx, ptr_base + ptrdiff);
2451 /* no subtype means it's a name event, rather than start/stop */
2452 if (subtype == 0)
2453 nd->friendly_name = pstrdup ((char *) p);
2454 if (debug) {
2455 if (subtype == 0)
2456 fprintf (outfile, "domain %p named at %llu: %s\n", (void *) (ptr_base + ptrdiff), (unsigned long long) time_base, p);
2457 else
2458 fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
2460 if (subtype == 0) {
2461 while (*p) p++;
2462 p++;
2464 } else if (mtype == TYPE_CONTEXT) {
2465 if (ctx->data_version < 13)
2466 decode_uleb128 (p, &p); /* flags */
2467 intptr_t domaindiff = decode_sleb128 (p, &p);
2468 if (debug)
2469 fprintf (outfile, "%s context %p (%p) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), (void *) (ptr_base + domaindiff), (unsigned long long) time_base);
2470 if (subtype == TYPE_END_LOAD)
2471 get_remctx (ctx, ptr_base + ptrdiff)->domain_id = ptr_base + domaindiff;
2472 } else if (mtype == TYPE_THREAD) {
2473 if (ctx->data_version < 13)
2474 decode_uleb128 (p, &p); /* flags */
2475 ThreadContext *nt = get_thread (ctx, ptr_base + ptrdiff);
2476 /* no subtype means it's a name event, rather than start/stop */
2477 if (subtype == 0)
2478 nt->name = pstrdup ((char*)p);
2479 if (debug) {
2480 if (subtype == 0)
2481 fprintf (outfile, "thread %p named at %llu: %s\n", (void*)(ptr_base + ptrdiff), (unsigned long long) time_base, p);
2482 else
2483 fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
2485 if (subtype == 0) {
2486 while (*p) p++;
2487 p++;
2489 } else if (mtype == TYPE_VTABLE) {
2490 intptr_t domaindiff = decode_sleb128 (p, &p);
2491 intptr_t classdiff = decode_sleb128 (p, &p);
2492 if (debug)
2493 fprintf (outfile, "vtable %p for class %p in domain %p at %llu\n", (void *) (ptrdiff + ptr_base), (void *) (classdiff + ptr_base), (void *) (domaindiff + ptr_base), (unsigned long long) time_base);
2494 add_vtable (ptr_base + ptrdiff, ptr_base + classdiff);
2496 break;
2498 case TYPE_ALLOC: {
2499 int has_bt = *p & TYPE_ALLOC_BT;
2500 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2501 intptr_t ptrdiff = decode_sleb128 (p, &p);
2502 intptr_t objdiff = decode_sleb128 (p, &p);
2503 uint64_t len;
2504 int num_bt = 0;
2505 MethodDesc* sframes [8];
2506 MethodDesc** frames = sframes;
2507 ClassDesc *cd;
2508 if (ctx->data_version > 14) {
2509 VTableDesc *vt = lookup_vtable (ptr_base + ptrdiff);
2510 cd = vt->klass;
2511 } else
2512 cd = lookup_class (ptr_base + ptrdiff);
2513 len = decode_uleb128 (p, &p);
2514 LOG_TIME (time_base, tdiff);
2515 time_base += tdiff;
2516 if (debug)
2517 fprintf (outfile, "alloced object %p, size %llu (%s) at %llu\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) len, cd->name, (unsigned long long) time_base);
2518 if (has_bt) {
2519 num_bt = 8;
2520 frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2521 if (!frames) {
2522 fprintf (outfile, "Cannot load backtrace\n");
2523 return 0;
2526 if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2527 BackTrace *bt;
2528 cd->allocs++;
2529 cd->alloc_size += len;
2530 if (has_bt)
2531 bt = add_trace_methods (frames, num_bt, &cd->traces, len);
2532 else
2533 bt = add_trace_thread (thread, &cd->traces, len);
2534 if (find_size && len >= find_size) {
2535 if (!find_name || strstr (cd->name, find_name))
2536 found_object (OBJ_ADDR (objdiff));
2537 } else if (!find_size && find_name && strstr (cd->name, find_name)) {
2538 found_object (OBJ_ADDR (objdiff));
2540 if (num_tracked_objects)
2541 tracked_creation (OBJ_ADDR (objdiff), cd, len, bt, time_base);
2543 if (frames != sframes)
2544 g_free (frames);
2545 break;
2547 case TYPE_METHOD: {
2548 int subtype = *p & 0xf0;
2549 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2550 int64_t ptrdiff = decode_sleb128 (p, &p);
2551 LOG_TIME (time_base, tdiff);
2552 time_base += tdiff;
2553 method_base += ptrdiff;
2554 if (subtype == TYPE_JIT) {
2555 intptr_t codediff = decode_sleb128 (p, &p);
2556 int codelen = decode_uleb128 (p, &p);
2557 MethodDesc *jitted_method;
2558 if (debug)
2559 fprintf (outfile, "jitted method %p (%s), size: %d, code: %p\n", (void*)(method_base), p, codelen, (void*)(ptr_base + codediff));
2560 jitted_method = add_method (method_base, (char*)p, ptr_base + codediff, codelen);
2561 if (!(time_base >= time_from && time_base < time_to))
2562 jitted_method->ignore_jit = 1;
2563 while (*p) p++;
2564 p++;
2565 } else {
2566 MethodDesc *method;
2567 if ((thread_filter && thread_filter != thread->thread_id))
2568 break;
2569 if (!(time_base >= time_from && time_base < time_to))
2570 break;
2571 method = lookup_method (method_base);
2572 if (subtype == TYPE_ENTER) {
2573 add_trace_thread (thread, &method->traces, 1);
2574 push_method (thread, method, time_base);
2575 } else {
2576 pop_method (thread, method, time_base);
2578 if (debug)
2579 fprintf (outfile, "%s method %s\n", subtype == TYPE_ENTER? "enter": subtype == TYPE_EXC_LEAVE? "exleave": "leave", method->name);
2581 break;
2583 case TYPE_HEAP: {
2584 int subtype = *p & 0xf0;
2585 if (subtype == TYPE_HEAP_OBJECT) {
2586 HeapObjectDesc *ho = NULL;
2587 int i;
2588 intptr_t objdiff;
2589 if (ctx->data_version > 12) {
2590 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2591 LOG_TIME (time_base, tdiff);
2592 time_base += tdiff;
2593 objdiff = decode_sleb128 (p, &p);
2594 } else
2595 objdiff = decode_sleb128 (p + 1, &p);
2596 intptr_t ptrdiff = decode_sleb128 (p, &p);
2597 uint64_t size = decode_uleb128 (p, &p);
2598 if (ctx->data_version >= 16)
2599 p++; // generation
2600 uintptr_t num = decode_uleb128 (p, &p);
2601 uintptr_t ref_offset = 0;
2602 uintptr_t last_obj_offset = 0;
2603 ClassDesc *cd;
2604 if (ctx->data_version > 14) {
2605 VTableDesc *vt = lookup_vtable (ptr_base + ptrdiff);
2606 cd = vt->klass;
2607 } else
2608 cd = lookup_class (ptr_base + ptrdiff);
2609 if (size) {
2610 HeapClassDesc *hcd = add_heap_shot_class (thread->current_heap_shot, cd, size);
2611 if (collect_traces) {
2612 ho = alloc_heap_obj (OBJ_ADDR (objdiff), hcd, num);
2613 add_heap_shot_obj (thread->current_heap_shot, ho);
2614 ref_offset = 0;
2616 } else {
2617 if (collect_traces)
2618 ho = heap_shot_obj_add_refs (thread->current_heap_shot, OBJ_ADDR (objdiff), num, &ref_offset);
2620 for (i = 0; i < num; ++i) {
2621 /* FIXME: use object distance to measure how good
2622 * the GC is at keeping related objects close
2624 uintptr_t offset = ctx->data_version > 1? last_obj_offset + decode_uleb128 (p, &p): -1;
2625 intptr_t obj1diff = decode_sleb128 (p, &p);
2626 last_obj_offset = offset;
2627 if (collect_traces)
2628 ho->refs [ref_offset + i] = OBJ_ADDR (obj1diff);
2629 if (num_tracked_objects)
2630 track_obj_reference (OBJ_ADDR (obj1diff), OBJ_ADDR (objdiff), cd);
2632 if (debug && size)
2633 fprintf (outfile, "traced object %p, size %llu (%s), refs: %zd\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) size, cd->name, num);
2634 } else if (subtype == TYPE_HEAP_ROOT) {
2635 uintptr_t num;
2636 if (ctx->data_version > 14) {
2637 int i;
2638 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2639 LOG_TIME (time_base, tdiff);
2640 time_base += tdiff;
2641 num = decode_uleb128 (p, &p);
2642 for (i = 0; i < num; ++i) {
2643 intptr_t ptrdiff = decode_sleb128 (p, &p);
2644 intptr_t objdiff = decode_sleb128 (p, &p);
2646 if (debug)
2647 fprintf (outfile, "root object %p at address %p\n", (void*)OBJ_ADDR (objdiff), (void *) (ptr_base + ptrdiff));
2648 if (collect_traces)
2649 thread_add_root (thread, OBJ_ADDR (objdiff), MONO_PROFILER_GC_ROOT_MISC, 0);
2651 } else {
2652 if (ctx->data_version > 12) {
2653 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2654 LOG_TIME (time_base, tdiff);
2655 time_base += tdiff;
2656 num = decode_uleb128 (p, &p);
2657 } else
2658 num = decode_uleb128 (p + 1, &p);
2659 uintptr_t gc_num G_GNUC_UNUSED = decode_uleb128 (p, &p);
2660 int i;
2661 for (i = 0; i < num; ++i) {
2662 intptr_t objdiff = decode_sleb128 (p, &p);
2663 int root_type;
2664 if (ctx->data_version == 13)
2665 root_type = *p++;
2666 else
2667 root_type = decode_uleb128 (p, &p);
2668 /* we just discard the extra info for now */
2669 uintptr_t extra_info = decode_uleb128 (p, &p);
2670 if (debug)
2671 fprintf (outfile, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff), get_root_name (root_type));
2672 if (collect_traces)
2673 thread_add_root (thread, OBJ_ADDR (objdiff), root_type, extra_info);
2676 } else if (subtype == TYPE_HEAP_ROOT_REGISTER) {
2677 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2678 LOG_TIME (time_base, tdiff);
2679 time_base += tdiff;
2681 int64_t ptrdiff = decode_sleb128 (p, &p);
2682 uint64_t size = decode_uleb128 (p, &p);
2683 int type = *p++;
2684 int64_t keydiff = decode_sleb128 (p, &p);
2685 char *desc = (char*) p;
2686 while (*p++);
2688 if (debug)
2689 fprintf (outfile, "root register address %p size %lld type %d key %p name %s\n", (void *) (ptr_base + ptrdiff), (unsigned long long) size, type, (void *) (ptr_base + keydiff), desc);
2690 } else if (subtype == TYPE_HEAP_ROOT_UNREGISTER) {
2691 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2692 LOG_TIME (time_base, tdiff);
2693 time_base += tdiff;
2694 int64_t ptrdiff = decode_sleb128 (p, &p);
2696 if (debug)
2697 fprintf (outfile, "root unregister address %p\n", (void *) (ptr_base + ptrdiff));
2698 } else if (subtype == TYPE_HEAP_END) {
2699 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2700 LOG_TIME (time_base, tdiff);
2701 time_base += tdiff;
2702 if (debug)
2703 fprintf (outfile, "heap shot end\n");
2704 if (collect_traces) {
2705 HeapShot *hs = thread->current_heap_shot;
2706 if (hs && thread->num_roots) {
2707 /* transfer the root ownershipt to the heapshot */
2708 hs->num_roots = thread->num_roots;
2709 hs->roots = thread->roots;
2710 hs->roots_extra = thread->roots_extra;
2711 hs->roots_types = thread->roots_types;
2712 } else {
2713 g_free (thread->roots);
2714 g_free (thread->roots_extra);
2715 g_free (thread->roots_types);
2717 thread->num_roots = 0;
2718 thread->size_roots = 0;
2719 thread->roots = NULL;
2720 thread->roots_extra = NULL;
2721 thread->roots_types = NULL;
2722 heap_shot_resolve_reverse_refs (hs);
2723 heap_shot_mark_objects (hs);
2724 heap_shot_free_objects (hs);
2726 thread->current_heap_shot = NULL;
2727 } else if (subtype == TYPE_HEAP_START) {
2728 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2729 LOG_TIME (time_base, tdiff);
2730 time_base += tdiff;
2731 if (debug)
2732 fprintf (outfile, "heap shot start\n");
2733 thread->current_heap_shot = new_heap_shot (time_base);
2735 break;
2737 case TYPE_MONITOR: {
2738 int has_bt = *p & TYPE_MONITOR_BT;
2739 int event;
2740 if (ctx->data_version < 13)
2741 event = (*p >> 4) & 0x3;
2742 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2743 if (ctx->data_version > 13)
2744 event = *p++;
2745 intptr_t objdiff = decode_sleb128 (p, &p);
2746 MethodDesc* sframes [8];
2747 MethodDesc** frames = sframes;
2748 int record;
2749 int num_bt = 0;
2750 LOG_TIME (time_base, tdiff);
2751 time_base += tdiff;
2752 record = (!thread_filter || thread_filter == thread->thread_id);
2753 if (!(time_base >= time_from && time_base < time_to))
2754 record = 0;
2755 MonitorDesc *mdesc = lookup_monitor (OBJ_ADDR (objdiff));
2756 if (event == MONO_PROFILER_MONITOR_CONTENTION) {
2757 if (record) {
2758 monitor_contention++;
2759 mdesc->contentions++;
2760 thread->monitor = mdesc;
2761 thread->contention_start = time_base;
2763 } else if (event == MONO_PROFILER_MONITOR_FAIL) {
2764 if (record) {
2765 monitor_failed++;
2766 if (thread->monitor && thread->contention_start) {
2767 uint64_t wait_time = time_base - thread->contention_start;
2768 if (wait_time > thread->monitor->max_wait_time)
2769 thread->monitor->max_wait_time = wait_time;
2770 thread->monitor->wait_time += wait_time;
2771 thread->monitor = NULL;
2772 thread->contention_start = 0;
2775 } else if (event == MONO_PROFILER_MONITOR_DONE) {
2776 if (record) {
2777 monitor_acquired++;
2778 if (thread->monitor && thread->contention_start) {
2779 uint64_t wait_time = time_base - thread->contention_start;
2780 if (wait_time > thread->monitor->max_wait_time)
2781 thread->monitor->max_wait_time = wait_time;
2782 thread->monitor->wait_time += wait_time;
2783 thread->monitor = NULL;
2784 thread->contention_start = 0;
2788 if (has_bt) {
2789 num_bt = 8;
2790 frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2791 if (!frames) {
2792 fprintf (outfile, "Cannot load backtrace\n");
2793 return 0;
2795 if (record && event == MONO_PROFILER_MONITOR_CONTENTION)
2796 add_trace_methods (frames, num_bt, &mdesc->traces, 1);
2797 } else {
2798 if (record)
2799 add_trace_thread (thread, &mdesc->traces, 1);
2801 if (debug)
2802 fprintf (outfile, "monitor %s for object %p\n", monitor_ev_name (event), (void*)OBJ_ADDR (objdiff));
2803 if (frames != sframes)
2804 g_free (frames);
2805 break;
2807 case TYPE_EXCEPTION: {
2808 int subtype = *p & 0x70;
2809 int has_bt = *p & TYPE_THROW_BT;
2810 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2811 MethodDesc* sframes [8];
2812 MethodDesc** frames = sframes;
2813 int record;
2814 LOG_TIME (time_base, tdiff);
2815 time_base += tdiff;
2816 record = (!thread_filter || thread_filter == thread->thread_id);
2817 if (!(time_base >= time_from && time_base < time_to))
2818 record = 0;
2819 if (subtype == TYPE_CLAUSE) {
2820 int clause_type;
2821 if (ctx->data_version > 12)
2822 clause_type = *p++;
2823 else
2824 clause_type = decode_uleb128 (p, &p);
2825 int clause_num = decode_uleb128 (p, &p);
2826 int64_t ptrdiff = decode_sleb128 (p, &p);
2827 method_base += ptrdiff;
2828 if (ctx->data_version > 13)
2829 decode_uleb128 (p, &p); // exception object
2830 if (record)
2831 clause_summary [clause_type]++;
2832 if (debug)
2833 fprintf (outfile, "clause %s (%d) in method %s\n", clause_name (clause_type), clause_num, lookup_method (method_base)->name);
2834 } else {
2835 intptr_t objdiff = decode_sleb128 (p, &p);
2836 if (record)
2837 throw_count++;
2838 if (has_bt) {
2839 has_bt = 8;
2840 frames = decode_bt (ctx, sframes, &has_bt, p, &p, ptr_base, &method_base);
2841 if (!frames) {
2842 fprintf (outfile, "Cannot load backtrace\n");
2843 return 0;
2845 if (record)
2846 add_trace_methods (frames, has_bt, &exc_traces, 1);
2847 } else {
2848 if (record)
2849 add_trace_thread (thread, &exc_traces, 1);
2851 if (frames != sframes)
2852 g_free (frames);
2853 if (debug)
2854 fprintf (outfile, "throw %p\n", (void*)OBJ_ADDR (objdiff));
2856 break;
2858 case TYPE_RUNTIME: {
2859 int subtype = *p & 0xf0;
2860 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2861 LOG_TIME (time_base, tdiff);
2862 time_base += tdiff;
2863 if (subtype == TYPE_JITHELPER) {
2864 int type;
2865 if (ctx->data_version > 12)
2866 type = *p++;
2867 else
2868 type = decode_uleb128 (p, &p);
2869 if (ctx->data_version < 14)
2870 --type;
2871 intptr_t codediff = decode_sleb128 (p, &p);
2872 int codelen = decode_uleb128 (p, &p);
2873 const char *name;
2874 if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
2875 name = (const char *)p;
2876 while (*p) p++;
2877 p++;
2878 } else {
2879 name = code_buffer_desc (type);
2881 num_jit_helpers++;
2882 jit_helpers_code_size += codelen;
2883 if (debug)
2884 fprintf (outfile, "jit helper %s, size: %d, code: %p\n", name, codelen, (void*)(ptr_base + codediff));
2886 break;
2888 case TYPE_SAMPLE: {
2889 int subtype = *p & 0xf0;
2890 if (subtype == TYPE_SAMPLE_HIT) {
2891 int i;
2892 int sample_type;
2893 uint64_t tstamp;
2894 if (ctx->data_version > 12) {
2895 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2896 LOG_TIME (time_base, tdiff);
2897 time_base += tdiff;
2898 if (ctx->data_version < 14)
2899 sample_type = *p++;
2900 else
2901 sample_type = SAMPLE_CYCLES;
2902 tstamp = time_base;
2903 } else {
2904 sample_type = decode_uleb128 (p + 1, &p);
2905 tstamp = decode_uleb128 (p, &p);
2907 void *tid = (void *) thread_id;
2908 if (ctx->data_version > 10)
2909 tid = (void *) (ptr_base + decode_sleb128 (p, &p));
2910 int count = decode_uleb128 (p, &p);
2911 for (i = 0; i < count; ++i) {
2912 uintptr_t ip = ptr_base + decode_sleb128 (p, &p);
2913 if ((tstamp >= time_from && tstamp < time_to))
2914 add_stat_sample (sample_type, ip);
2915 if (debug)
2916 fprintf (outfile, "sample hit, type: %d at %p for thread %p\n", sample_type, (void*)ip, tid);
2918 if (ctx->data_version > 5) {
2919 count = decode_uleb128 (p, &p);
2920 for (i = 0; i < count; ++i) {
2921 MethodDesc *method;
2922 int64_t ptrdiff = decode_sleb128 (p, &p);
2923 method_base += ptrdiff;
2924 method = lookup_method (method_base);
2925 if (debug)
2926 fprintf (outfile, "sample hit bt %d: %s\n", i, method->name);
2927 if (ctx->data_version < 13) {
2928 decode_sleb128 (p, &p); /* il offset */
2929 decode_sleb128 (p, &p); /* native offset */
2933 } else if (subtype == TYPE_SAMPLE_USYM) {
2934 /* un unmanaged symbol description */
2935 uintptr_t addr;
2936 if (ctx->data_version > 12) {
2937 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2938 LOG_TIME (time_base, tdiff);
2939 time_base += tdiff;
2940 addr = ptr_base + decode_sleb128 (p, &p);
2941 } else
2942 addr = ptr_base + decode_sleb128 (p + 1, &p);
2943 uintptr_t size = decode_uleb128 (p, &p);
2944 char *name;
2945 name = pstrdup ((char*)p);
2946 add_unmanaged_symbol (addr, name, size);
2947 if (debug)
2948 fprintf (outfile, "unmanaged symbol %s at %p\n", name, (void*)addr);
2949 while (*p) p++;
2950 p++;
2951 } else if (subtype == TYPE_SAMPLE_UBIN) {
2952 /* un unmanaged binary loaded in memory */
2953 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2954 uintptr_t addr = decode_sleb128 (p, &p);
2955 if (ctx->data_version > 13)
2956 addr += ptr_base;
2957 uint64_t offset G_GNUC_UNUSED = decode_uleb128 (p, &p);
2958 uintptr_t size = decode_uleb128 (p, &p);
2959 char *name;
2960 LOG_TIME (time_base, tdiff);
2961 time_base += tdiff;
2962 name = pstrdup ((char*)p);
2963 add_unmanaged_binary (addr, name, size);
2964 if (debug)
2965 fprintf (outfile, "unmanaged binary %s at %p\n", name, (void*)addr);
2966 while (*p) p++;
2967 p++;
2968 } else if (subtype == TYPE_SAMPLE_COUNTERS_DESC) {
2969 uint64_t i, len;
2970 if (ctx->data_version > 12) {
2971 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2972 LOG_TIME (time_base, tdiff);
2973 time_base += tdiff;
2974 len = decode_uleb128 (p, &p);
2975 } else
2976 len = decode_uleb128 (p + 1, &p);
2977 for (i = 0; i < len; i++) {
2978 uint64_t type, unit, variance, index;
2979 uint64_t section = decode_uleb128 (p, &p);
2980 char *section_str, *name;
2981 if (section != MONO_COUNTER_PERFCOUNTERS) {
2982 section_str = (char*) section_name (section);
2983 } else {
2984 section_str = pstrdup ((char*)p);
2985 while (*p++);
2987 name = pstrdup ((char*)p);
2988 while (*p++);
2989 if (ctx->data_version > 12 && ctx->data_version < 15) {
2990 type = *p++;
2991 unit = *p++;
2992 variance = *p++;
2993 } else {
2994 type = decode_uleb128 (p, &p);
2995 unit = decode_uleb128 (p, &p);
2996 variance = decode_uleb128 (p, &p);
2998 index = decode_uleb128 (p, &p);
2999 add_counter (section_str, name, (int)type, (int)unit, (int)variance, (int)index);
3001 } else if (subtype == TYPE_SAMPLE_COUNTERS) {
3002 int i;
3003 CounterValue *value, *previous = NULL;
3004 CounterList *list;
3005 uint64_t timestamp; // milliseconds since startup
3006 if (ctx->data_version > 12) {
3007 uint64_t tdiff = decode_uleb128 (p + 1, &p);
3008 LOG_TIME (time_base, tdiff);
3009 time_base += tdiff;
3010 timestamp = (time_base - startup_time) / 1000 / 1000;
3011 } else
3012 timestamp = decode_uleb128 (p + 1, &p);
3013 uint64_t time_between = timestamp / 1000 * 1000 * 1000 * 1000 + startup_time;
3014 while (1) {
3015 uint64_t type, index = decode_uleb128 (p, &p);
3016 if (index == 0)
3017 break;
3019 for (list = counters; list; list = list->next) {
3020 if (list->counter->index == (int)index) {
3021 previous = list->counter->values_last;
3022 break;
3026 if (ctx->data_version > 12 && ctx->data_version < 15)
3027 type = *p++;
3028 else
3029 type = decode_uleb128 (p, &p);
3031 value = (CounterValue *) g_calloc (1, sizeof (CounterValue));
3032 value->timestamp = timestamp;
3034 switch (type) {
3035 case MONO_COUNTER_INT:
3036 #if SIZEOF_VOID_P == 4
3037 case MONO_COUNTER_WORD:
3038 #endif
3039 value->buffer = (unsigned char *)g_malloc (sizeof (int32_t));
3040 *(int32_t*)value->buffer = (int32_t)decode_sleb128 (p, &p) + (previous ? (*(int32_t*)previous->buffer) : 0);
3041 break;
3042 case MONO_COUNTER_UINT:
3043 value->buffer = (unsigned char *) g_malloc (sizeof (uint32_t));
3044 *(uint32_t*)value->buffer = (uint32_t)decode_uleb128 (p, &p) + (previous ? (*(uint32_t*)previous->buffer) : 0);
3045 break;
3046 case MONO_COUNTER_LONG:
3047 #if SIZEOF_VOID_P == 8
3048 case MONO_COUNTER_WORD:
3049 #endif
3050 case MONO_COUNTER_TIME_INTERVAL:
3051 value->buffer = (unsigned char *) g_malloc (sizeof (int64_t));
3052 *(int64_t*)value->buffer = (int64_t)decode_sleb128 (p, &p) + (previous ? (*(int64_t*)previous->buffer) : 0);
3053 break;
3054 case MONO_COUNTER_ULONG:
3055 value->buffer = (unsigned char *) g_malloc (sizeof (uint64_t));
3056 *(uint64_t*)value->buffer = (uint64_t)decode_uleb128 (p, &p) + (previous ? (*(uint64_t*)previous->buffer) : 0);
3057 break;
3058 case MONO_COUNTER_DOUBLE:
3059 value->buffer = (unsigned char *) g_malloc (sizeof (double));
3060 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
3061 for (i = 0; i < sizeof (double); i++)
3062 #else
3063 for (i = sizeof (double) - 1; i >= 0; i--)
3064 #endif
3065 value->buffer[i] = *p++;
3066 break;
3067 case MONO_COUNTER_STRING:
3068 if (*p++ == 0) {
3069 value->buffer = NULL;
3070 } else {
3071 value->buffer = (unsigned char*) pstrdup ((char*)p);
3072 while (*p++);
3074 break;
3076 if (time_between >= time_from && time_between <= time_to)
3077 add_counter_value (index, value);
3079 } else {
3080 return 0;
3082 break;
3084 case TYPE_COVERAGE:{
3085 int subtype = *p & 0xf0;
3086 switch (subtype) {
3087 case TYPE_COVERAGE_METHOD: {
3088 p++;
3090 if (ctx->data_version > 12) {
3091 uint64_t tdiff = decode_uleb128 (p, &p);
3092 LOG_TIME (time_base, tdiff);
3093 time_base += tdiff;
3096 while (*p) p++;
3097 p++;
3098 while (*p) p++;
3099 p++;
3100 while (*p) p++;
3101 p++;
3102 while (*p) p++;
3103 p++;
3104 while (*p) p++;
3105 p++;
3107 decode_uleb128 (p, &p);
3108 decode_uleb128 (p, &p);
3109 decode_uleb128 (p, &p);
3111 break;
3113 case TYPE_COVERAGE_STATEMENT: {
3114 p++;
3116 if (ctx->data_version > 12) {
3117 uint64_t tdiff = decode_uleb128 (p, &p);
3118 LOG_TIME (time_base, tdiff);
3119 time_base += tdiff;
3122 decode_uleb128 (p, &p);
3123 decode_uleb128 (p, &p);
3124 decode_uleb128 (p, &p);
3125 decode_uleb128 (p, &p);
3126 decode_uleb128 (p, &p);
3128 break;
3130 case TYPE_COVERAGE_ASSEMBLY: {
3131 p++;
3133 if (ctx->data_version > 12) {
3134 uint64_t tdiff = decode_uleb128 (p, &p);
3135 LOG_TIME (time_base, tdiff);
3136 time_base += tdiff;
3139 while (*p) p++;
3140 p++;
3141 while (*p) p++;
3142 p++;
3143 while (*p) p++;
3144 p++;
3146 decode_uleb128 (p, &p);
3147 decode_uleb128 (p, &p);
3148 decode_uleb128 (p, &p);
3150 break;
3152 case TYPE_COVERAGE_CLASS: {
3153 p++;
3155 if (ctx->data_version > 12) {
3156 uint64_t tdiff = decode_uleb128 (p, &p);
3157 LOG_TIME (time_base, tdiff);
3158 time_base += tdiff;
3161 while (*p) p++;
3162 p++;
3163 while (*p) p++;
3164 p++;
3166 decode_uleb128 (p, &p);
3167 decode_uleb128 (p, &p);
3168 decode_uleb128 (p, &p);
3170 break;
3173 default:
3174 break;
3176 break;
3178 case TYPE_META: {
3179 int subtype = *p & 0xf0;
3180 uint64_t tdiff = decode_uleb128 (p + 1, &p);
3181 LOG_TIME (time_base, tdiff);
3182 time_base += tdiff;
3183 if (subtype == TYPE_SYNC_POINT) {
3184 int type = *p++;
3185 if (debug)
3186 fprintf (outfile, "sync point %i (%s)\n", type, sync_point_name (type));
3187 } else if (subtype == TYPE_AOT_ID) {
3188 if (debug)
3189 fprintf (outfile, "aot id %s\n", p);
3190 while (*p) p++; // aot id
3191 p++;
3193 break;
3195 default:
3196 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);
3197 exit (1);
3199 record_event_stats (event, p - start);
3201 thread->last_time = time_base;
3202 for (i = 0; i < thread->stack_id; ++i)
3203 thread->stack [i]->recurse_count = 0;
3204 return 1;
3207 static int
3208 read_header_string (ProfContext *ctx, char **field)
3210 if (!load_data (ctx, 4))
3211 return 0;
3213 if (!load_data (ctx, read_int32 (ctx->buf)))
3214 return 0;
3216 *field = pstrdup ((const char *) ctx->buf);
3218 return 1;
3221 static ProfContext*
3222 load_file (char *name)
3224 unsigned char *p;
3225 ProfContext *ctx = (ProfContext *) g_calloc (sizeof (ProfContext), 1);
3226 if (strcmp (name, "-") == 0)
3227 ctx->file = stdin;
3228 else
3229 ctx->file = fopen (name, "rb");
3230 if (!ctx->file) {
3231 printf ("Cannot open file: %s\n", name);
3232 exit (1);
3234 #if defined (HAVE_SYS_ZLIB)
3235 if (ctx->file != stdin)
3236 ctx->gzfile = gzdopen (fileno (ctx->file), "rb");
3237 #endif
3238 if (!load_data (ctx, 16))
3239 return NULL;
3240 p = ctx->buf;
3241 if (read_int32 (p) != LOG_HEADER_ID)
3242 return NULL;
3243 p += 4;
3244 ctx->version_major = *p++;
3245 ctx->version_minor = *p++;
3246 ctx->data_version = *p++;
3247 if (ctx->data_version > LOG_DATA_VERSION)
3248 return NULL;
3249 /* reading 64 bit files on 32 bit systems not supported yet */
3250 if (*p++ > sizeof (void*))
3251 return NULL;
3252 ctx->startup_time = read_int64 (p);
3253 p += 8;
3254 // nanoseconds startup time
3255 if (ctx->version_major >= 3)
3256 if (!load_data (ctx, 8))
3257 return NULL;
3258 if (!load_data (ctx, 14))
3259 return NULL;
3260 p = ctx->buf;
3261 ctx->timer_overhead = read_int32 (p);
3262 p += 4;
3263 if (read_int32 (p)) /* flags must be 0 */
3264 return NULL;
3265 p += 4;
3266 ctx->pid = read_int32 (p);
3267 p += 4;
3268 ctx->port = read_int16 (p);
3269 p += 2;
3270 if (ctx->version_major >= 1) {
3271 if (!read_header_string (ctx, &ctx->args))
3272 return NULL;
3273 if (!read_header_string (ctx, &ctx->arch))
3274 return NULL;
3275 if (!read_header_string (ctx, &ctx->os))
3276 return NULL;
3277 } else {
3278 if (!load_data (ctx, 2)) /* old opsys field, was never used */
3279 return NULL;
3281 return ctx;
3284 enum {
3285 ALLOC_SORT_BYTES,
3286 ALLOC_SORT_COUNT
3288 static int alloc_sort_mode = ALLOC_SORT_BYTES;
3290 static int
3291 compare_class (const void *a, const void *b)
3293 ClassDesc *const *A = (ClassDesc *const *)a;
3294 ClassDesc *const *B = (ClassDesc *const *)b;
3295 uint64_t vala, valb;
3296 if (alloc_sort_mode == ALLOC_SORT_BYTES) {
3297 vala = (*A)->alloc_size;
3298 valb = (*B)->alloc_size;
3299 } else {
3300 vala = (*A)->allocs;
3301 valb = (*B)->allocs;
3303 if (valb == vala)
3304 return 0;
3305 if (valb < vala)
3306 return -1;
3307 return 1;
3310 static void
3311 dump_header (ProfContext *ctx)
3313 time_t st = ctx->startup_time / 1000;
3314 char *t = ctime (&st);
3315 fprintf (outfile, "\nMono log profiler data\n");
3316 fprintf (outfile, "\tProfiler version: %d.%d\n", ctx->version_major, ctx->version_minor);
3317 fprintf (outfile, "\tData version: %d\n", ctx->data_version);
3318 if (ctx->version_major >= 1) {
3319 fprintf (outfile, "\tArguments: %s\n", ctx->args);
3320 fprintf (outfile, "\tArchitecture: %s\n", ctx->arch);
3321 fprintf (outfile, "\tOperating system: %s\n", ctx->os);
3323 fprintf (outfile, "\tMean timer overhead: %d nanoseconds\n", ctx->timer_overhead);
3324 fprintf (outfile, "\tProgram startup: %s", t);
3325 if (ctx->pid)
3326 fprintf (outfile, "\tProgram ID: %d\n", ctx->pid);
3327 if (ctx->port)
3328 fprintf (outfile, "\tServer listening on: %d\n", ctx->port);
3331 static void
3332 dump_traces (TraceDesc *traces, const char *desc)
3334 int j;
3335 if (!show_traces)
3336 return;
3337 if (!traces->count)
3338 return;
3339 sort_context_array (traces);
3340 for (j = 0; j < traces->count; ++j) {
3341 int k;
3342 BackTrace *bt;
3343 bt = traces->traces [j].bt;
3344 if (!bt->count)
3345 continue;
3346 fprintf (outfile, "\t%llu %s from:\n", (unsigned long long) traces->traces [j].count, desc);
3347 for (k = 0; k < bt->count; ++k)
3348 fprintf (outfile, "\t\t%s\n", bt->methods [k]->name);
3352 static void
3353 dump_threads (ProfContext *ctx)
3355 ThreadContext *thread;
3356 fprintf (outfile, "\nThread summary\n");
3357 for (thread = ctx->threads; thread; thread = thread->next) {
3358 if (thread->thread_id) {
3359 fprintf (outfile, "\tThread: %p, name: \"%s\"\n", (void*)thread->thread_id, thread->name? thread->name: "");
3364 static void
3365 dump_domains (ProfContext *ctx)
3367 fprintf (outfile, "\nDomain summary\n");
3369 for (DomainContext *domain = ctx->domains; domain; domain = domain->next)
3370 fprintf (outfile, "\tDomain: %p, friendly name: \"%s\"\n", (void *) domain->domain_id, domain->friendly_name);
3373 static void
3374 dump_remctxs (ProfContext *ctx)
3376 fprintf (outfile, "\nContext summary\n");
3378 for (RemCtxContext *remctx = ctx->remctxs; remctx; remctx = remctx->next)
3379 fprintf (outfile, "\tContext: %p, domain: %p\n", (void *) remctx->remctx_id, (void *) remctx->domain_id);
3382 static void
3383 dump_exceptions (void)
3385 int i;
3386 fprintf (outfile, "\nException summary\n");
3387 fprintf (outfile, "\tThrows: %llu\n", (unsigned long long) throw_count);
3388 dump_traces (&exc_traces, "throws");
3389 for (i = 0; i <= MONO_EXCEPTION_CLAUSE_FAULT; ++i) {
3390 if (!clause_summary [i])
3391 continue;
3392 fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), (unsigned long long) clause_summary [i]);
3396 static int
3397 compare_monitor (const void *a, const void *b)
3399 MonitorDesc *const *A = (MonitorDesc *const *)a;
3400 MonitorDesc *const *B = (MonitorDesc *const *)b;
3401 if ((*B)->wait_time == (*A)->wait_time)
3402 return 0;
3403 if ((*B)->wait_time < (*A)->wait_time)
3404 return -1;
3405 return 1;
3408 static void
3409 dump_monitors (void)
3411 MonitorDesc **monitors;
3412 int i, j;
3413 if (!num_monitors)
3414 return;
3415 monitors = (MonitorDesc **) g_malloc (sizeof (void*) * num_monitors);
3416 for (i = 0, j = 0; i < SMALL_HASH_SIZE; ++i) {
3417 MonitorDesc *mdesc = monitor_hash [i];
3418 while (mdesc) {
3419 monitors [j++] = mdesc;
3420 mdesc = mdesc->next;
3423 qsort (monitors, num_monitors, sizeof (void*), compare_monitor);
3424 fprintf (outfile, "\nMonitor lock summary\n");
3425 for (i = 0; i < num_monitors; ++i) {
3426 MonitorDesc *mdesc = monitors [i];
3427 fprintf (outfile, "\tLock object %p: %d contentions\n", (void*)mdesc->objid, (int)mdesc->contentions);
3428 fprintf (outfile, "\t\t%.6f secs total wait time, %.6f max, %.6f average\n",
3429 mdesc->wait_time/1000000000.0, mdesc->max_wait_time/1000000000.0, mdesc->wait_time/1000000000.0/mdesc->contentions);
3430 dump_traces (&mdesc->traces, "contentions");
3432 fprintf (outfile, "\tLock contentions: %llu\n", (unsigned long long) monitor_contention);
3433 fprintf (outfile, "\tLock acquired: %llu\n", (unsigned long long) monitor_acquired);
3434 fprintf (outfile, "\tLock failures: %llu\n", (unsigned long long) monitor_failed);
3437 static void
3438 dump_gcs (void)
3440 int i;
3441 fprintf (outfile, "\nGC summary\n");
3442 fprintf (outfile, "\tGC resizes: %d\n", gc_resizes);
3443 fprintf (outfile, "\tMax heap size: %llu\n", (unsigned long long) max_heap_size);
3444 fprintf (outfile, "\tObject moves: %llu\n", (unsigned long long) gc_object_moves);
3445 for (i = 0; i < 3; ++i) {
3446 if (!gc_info [i].count)
3447 continue;
3448 fprintf (outfile, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
3449 i, gc_info [i].count,
3450 (unsigned long long) (gc_info [i].max_time / 1000),
3451 (unsigned long long) (gc_info [i].total_time / 1000),
3452 (unsigned long long) (gc_info [i].total_time / gc_info [i].count / 1000));
3454 for (i = 0; i < 3; ++i) {
3455 if (!handle_info [i].max_live)
3456 continue;
3457 fprintf (outfile, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
3458 get_handle_name (i),
3459 (unsigned long long) (handle_info [i].created),
3460 (unsigned long long) (handle_info [i].destroyed),
3461 (unsigned long long) (handle_info [i].max_live));
3462 dump_traces (&handle_info [i].traces, "created");
3463 dump_traces (&handle_info [i].destroy_traces, "destroyed");
3467 static void
3468 dump_jit (void)
3470 int i;
3471 int code_size = 0;
3472 int compiled_methods = 0;
3473 MethodDesc* m;
3474 fprintf (outfile, "\nJIT summary\n");
3475 for (i = 0; i < HASH_SIZE; ++i) {
3476 m = method_hash [i];
3477 for (m = method_hash [i]; m; m = m->next) {
3478 if (!m->code || m->ignore_jit)
3479 continue;
3480 compiled_methods++;
3481 code_size += m->len;
3484 fprintf (outfile, "\tCompiled methods: %d\n", compiled_methods);
3485 fprintf (outfile, "\tGenerated code size: %d\n", code_size);
3486 fprintf (outfile, "\tJIT helpers: %d\n", num_jit_helpers);
3487 fprintf (outfile, "\tJIT helpers code size: %d\n", jit_helpers_code_size);
3490 static void
3491 dump_allocations (void)
3493 int i, c;
3494 intptr_t allocs = 0;
3495 uint64_t size = 0;
3496 int header_done = 0;
3497 ClassDesc **classes = (ClassDesc **) g_malloc (num_classes * sizeof (void*));
3498 ClassDesc *cd;
3499 c = 0;
3500 for (i = 0; i < HASH_SIZE; ++i) {
3501 cd = class_hash [i];
3502 while (cd) {
3503 classes [c++] = cd;
3504 cd = cd->next;
3507 qsort (classes, num_classes, sizeof (void*), compare_class);
3508 for (i = 0; i < num_classes; ++i) {
3509 cd = classes [i];
3510 if (!cd->allocs)
3511 continue;
3512 allocs += cd->allocs;
3513 size += cd->alloc_size;
3514 if (!header_done++) {
3515 fprintf (outfile, "\nAllocation summary\n");
3516 fprintf (outfile, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
3518 fprintf (outfile, "%10llu %10zd %8llu %s\n",
3519 (unsigned long long) (cd->alloc_size),
3520 cd->allocs,
3521 (unsigned long long) (cd->alloc_size / cd->allocs),
3522 cd->name);
3523 dump_traces (&cd->traces, "bytes");
3525 if (allocs)
3526 fprintf (outfile, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size, allocs);
3529 enum {
3530 METHOD_SORT_TOTAL,
3531 METHOD_SORT_SELF,
3532 METHOD_SORT_CALLS
3535 static int method_sort_mode = METHOD_SORT_TOTAL;
3537 static int
3538 compare_method (const void *a, const void *b)
3540 MethodDesc *const *A = (MethodDesc *const *)a;
3541 MethodDesc *const *B = (MethodDesc *const *)b;
3542 uint64_t vala, valb;
3543 if (method_sort_mode == METHOD_SORT_SELF) {
3544 vala = (*A)->self_time;
3545 valb = (*B)->self_time;
3546 } else if (method_sort_mode == METHOD_SORT_CALLS) {
3547 vala = (*A)->calls;
3548 valb = (*B)->calls;
3549 } else {
3550 vala = (*A)->total_time;
3551 valb = (*B)->total_time;
3553 if (vala == valb)
3554 return 0;
3555 if (valb < vala)
3556 return -1;
3557 return 1;
3560 static void
3561 dump_metadata (void)
3563 fprintf (outfile, "\nMetadata summary\n");
3564 fprintf (outfile, "\tLoaded images: %d\n", num_images);
3565 if (verbose) {
3566 ImageDesc *image;
3567 int i;
3568 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
3569 image = image_hash [i];
3570 while (image) {
3571 fprintf (outfile, "\t\t%s\n", image->filename);
3572 image = image->next;
3576 fprintf (outfile, "\tLoaded assemblies: %d\n", num_assemblies);
3577 if (verbose) {
3578 AssemblyDesc *assembly;
3579 int i;
3580 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
3581 assembly = assembly_hash [i];
3582 while (assembly) {
3583 fprintf (outfile, "\t\t%s\n", assembly->asmname);
3584 assembly = assembly->next;
3590 static void
3591 dump_methods (void)
3593 int i, c;
3594 uint64_t calls = 0;
3595 int header_done = 0;
3596 MethodDesc **methods = (MethodDesc **) g_malloc (num_methods * sizeof (void*));
3597 MethodDesc *cd;
3598 c = 0;
3599 for (i = 0; i < HASH_SIZE; ++i) {
3600 cd = method_hash [i];
3601 while (cd) {
3602 cd->total_time = cd->self_time + cd->callee_time;
3603 methods [c++] = cd;
3604 cd = cd->next;
3607 qsort (methods, num_methods, sizeof (void*), compare_method);
3608 for (i = 0; i < num_methods; ++i) {
3609 uint64_t msecs;
3610 uint64_t smsecs;
3611 cd = methods [i];
3612 if (!cd->calls)
3613 continue;
3614 calls += cd->calls;
3615 msecs = cd->total_time / 1000000;
3616 smsecs = (cd->total_time - cd->callee_time) / 1000000;
3617 if (!msecs && !verbose)
3618 continue;
3619 if (!header_done++) {
3620 fprintf (outfile, "\nMethod call summary\n");
3621 fprintf (outfile, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
3623 fprintf (outfile, "%8llu %8llu %10llu %s\n",
3624 (unsigned long long) (msecs),
3625 (unsigned long long) (smsecs),
3626 (unsigned long long) (cd->calls),
3627 cd->name);
3628 dump_traces (&cd->traces, "calls");
3630 if (calls)
3631 fprintf (outfile, "Total calls: %llu\n", (unsigned long long) calls);
3634 static int
3635 compare_heap_class (const void *a, const void *b)
3637 HeapClassDesc *const *A = (HeapClassDesc *const *)a;
3638 HeapClassDesc *const *B = (HeapClassDesc *const *)b;
3639 uint64_t vala, valb;
3640 if (alloc_sort_mode == ALLOC_SORT_BYTES) {
3641 vala = (*A)->total_size;
3642 valb = (*B)->total_size;
3643 } else {
3644 vala = (*A)->count;
3645 valb = (*B)->count;
3647 if (valb == vala)
3648 return 0;
3649 if (valb < vala)
3650 return -1;
3651 return 1;
3654 static int
3655 compare_rev_class (const void *a, const void *b)
3657 const HeapClassRevRef *A = (const HeapClassRevRef *)a;
3658 const HeapClassRevRef *B = (const HeapClassRevRef *)b;
3659 if (B->count == A->count)
3660 return 0;
3661 if (B->count < A->count)
3662 return -1;
3663 return 1;
3666 static void
3667 dump_rev_claases (HeapClassRevRef *revs, int count)
3669 int j;
3670 if (!show_traces)
3671 return;
3672 if (!count)
3673 return;
3674 for (j = 0; j < count; ++j) {
3675 HeapClassDesc *cd = revs [j].klass;
3676 fprintf (outfile, "\t\t%llu references from: %s\n",
3677 (unsigned long long) (revs [j].count),
3678 cd->klass->name);
3682 static void
3683 heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
3685 uint64_t size = 0;
3686 uint64_t count = 0;
3687 int ccount = 0;
3688 int i;
3689 HeapClassDesc *cd;
3690 HeapClassDesc **sorted;
3691 sorted = (HeapClassDesc **) g_malloc (sizeof (void*) * hs->class_count);
3692 for (i = 0; i < hs->hash_size; ++i) {
3693 cd = hs->class_hash [i];
3694 if (!cd)
3695 continue;
3696 count += cd->count;
3697 size += cd->total_size;
3698 sorted [ccount++] = cd;
3700 hs->sorted = sorted;
3701 qsort (sorted, ccount, sizeof (void*), compare_heap_class);
3702 fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %zd\n",
3703 hs_num,
3704 (hs->timestamp - startup_time)/1000000000.0,
3705 (unsigned long long) (size),
3706 (unsigned long long) (count),
3707 ccount, hs->num_roots);
3708 if (!verbose && ccount > 30)
3709 ccount = 30;
3710 fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
3711 for (i = 0; i < ccount; ++i) {
3712 HeapClassRevRef *rev_sorted;
3713 int j, k;
3714 HeapClassDesc *ocd = NULL;
3715 cd = sorted [i];
3716 if (last_hs)
3717 ocd = heap_class_lookup (last_hs, cd->klass);
3718 fprintf (outfile, "\t%10llu %10llu %8llu %s",
3719 (unsigned long long) (cd->total_size),
3720 (unsigned long long) (cd->count),
3721 (unsigned long long) (cd->total_size / cd->count),
3722 cd->klass->name);
3723 if (ocd) {
3724 int64_t bdiff = cd->total_size - ocd->total_size;
3725 int64_t cdiff = cd->count - ocd->count;
3726 fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", (long long) bdiff, (long long) cdiff);
3727 } else {
3728 fprintf (outfile, "\n");
3730 if (!collect_traces)
3731 continue;
3732 rev_sorted = (HeapClassRevRef *) g_malloc (cd->rev_count * sizeof (HeapClassRevRef));
3733 k = 0;
3734 for (j = 0; j < cd->rev_hash_size; ++j) {
3735 if (cd->rev_hash [j].klass)
3736 rev_sorted [k++] = cd->rev_hash [j];
3738 assert (cd->rev_count == k);
3739 qsort (rev_sorted, cd->rev_count, sizeof (HeapClassRevRef), compare_rev_class);
3740 if (cd->root_references)
3741 fprintf (outfile, "\t\t%zd root references (%zd pinning)\n", cd->root_references, cd->pinned_references);
3742 dump_rev_claases (rev_sorted, cd->rev_count);
3743 g_free (rev_sorted);
3745 g_free (sorted);
3748 static int
3749 compare_heap_shots (const void *a, const void *b)
3751 HeapShot *const *A = (HeapShot *const *)a;
3752 HeapShot *const *B = (HeapShot *const *)b;
3753 if ((*B)->timestamp == (*A)->timestamp)
3754 return 0;
3755 if ((*B)->timestamp > (*A)->timestamp)
3756 return -1;
3757 return 1;
3760 static void
3761 dump_heap_shots (void)
3763 HeapShot **hs_sorted;
3764 HeapShot *hs;
3765 HeapShot *last_hs = NULL;
3766 int i;
3767 if (!heap_shots)
3768 return;
3769 hs_sorted = (HeapShot **) g_malloc (num_heap_shots * sizeof (void*));
3770 fprintf (outfile, "\nHeap shot summary\n");
3771 i = 0;
3772 for (hs = heap_shots; hs; hs = hs->next)
3773 hs_sorted [i++] = hs;
3774 qsort (hs_sorted, num_heap_shots, sizeof (void*), compare_heap_shots);
3775 for (i = 0; i < num_heap_shots; ++i) {
3776 hs = hs_sorted [i];
3777 heap_shot_summary (hs, i, last_hs);
3778 last_hs = hs;
3782 #define DUMP_EVENT_STAT(EVENT,SUBTYPE) dump_event (#EVENT, #SUBTYPE, EVENT, SUBTYPE);
3784 static void
3785 dump_event (const char *event_name, const char *subtype_name, int event, int subtype)
3787 int idx = event | subtype;
3788 EventStat evt = stats [idx];
3789 if (!evt.count)
3790 return;
3792 fprintf (outfile, "\t%16s\t%26s\tcount %6d\tmin %3d\tmax %6d\tbytes %d\n", event_name, subtype_name, evt.count, evt.min_size, evt.max_size, evt.bytes);
3795 static void
3796 dump_stats (void)
3798 fprintf (outfile, "\nMlpd statistics\n");
3799 fprintf (outfile, "\tBuffer count %d\toverhead %d (%d bytes per header)\n", buffer_count, buffer_count * BUFFER_HEADER_SIZE, BUFFER_HEADER_SIZE);
3800 fprintf (outfile, "\nEvent details:\n");
3802 DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_NO_BT);
3803 DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_BT);
3805 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_EVENT);
3806 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_RESIZE);
3807 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_MOVE);
3808 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED);
3809 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED);
3810 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED_BT);
3811 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED_BT);
3812 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_FINALIZE_START);
3813 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_FINALIZE_END);
3814 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_FINALIZE_OBJECT_START);
3815 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_FINALIZE_OBJECT_END);
3817 DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_LOAD);
3818 DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_UNLOAD);
3820 DUMP_EVENT_STAT (TYPE_METHOD, TYPE_LEAVE);
3821 DUMP_EVENT_STAT (TYPE_METHOD, TYPE_ENTER);
3822 DUMP_EVENT_STAT (TYPE_METHOD, TYPE_EXC_LEAVE);
3823 DUMP_EVENT_STAT (TYPE_METHOD, TYPE_JIT);
3825 DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_THROW_NO_BT);
3826 DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_THROW_BT);
3827 DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_CLAUSE);
3829 DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_NO_BT);
3830 DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_BT);
3832 DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_START);
3833 DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_END);
3834 DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_OBJECT);
3835 DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_ROOT);
3837 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_HIT);
3838 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_USYM);
3839 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_UBIN);
3840 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS_DESC);
3841 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS);
3843 DUMP_EVENT_STAT (TYPE_RUNTIME, TYPE_JITHELPER);
3845 DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_ASSEMBLY);
3846 DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_METHOD);
3847 DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_STATEMENT);
3848 DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_CLASS);
3850 DUMP_EVENT_STAT (TYPE_META, TYPE_SYNC_POINT);
3851 DUMP_EVENT_STAT (TYPE_META, TYPE_AOT_ID);
3856 static void
3857 flush_context (ProfContext *ctx)
3859 ThreadContext *thread;
3860 /* FIXME: sometimes there are leftovers: indagate */
3861 for (thread = ctx->threads; thread; thread = thread->next) {
3862 while (thread->stack_id) {
3863 if (debug)
3864 fprintf (outfile, "thread %p has %d items on stack\n", (void*)thread->thread_id, thread->stack_id);
3865 pop_method (thread, thread->stack [thread->stack_id - 1], thread->last_time);
3870 static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,domain,context,heapshot,counters";
3872 static const char*
3873 match_option (const char *p, const char *opt)
3875 int len = strlen (opt);
3876 if (strncmp (p, opt, len) == 0) {
3877 if (p [len] == ',')
3878 len++;
3879 return p + len;
3881 return p;
3884 static int
3885 print_reports (ProfContext *ctx, const char *reps, int parse_only)
3887 const char *opt;
3888 const char *p;
3889 for (p = reps; *p; p = opt) {
3890 if ((opt = match_option (p, "header")) != p) {
3891 if (!parse_only)
3892 dump_header (ctx);
3893 continue;
3895 if ((opt = match_option (p, "thread")) != p) {
3896 if (!parse_only)
3897 dump_threads (ctx);
3898 continue;
3900 if ((opt = match_option (p, "domain")) != p) {
3901 if (!parse_only)
3902 dump_domains (ctx);
3903 continue;
3905 if ((opt = match_option (p, "context")) != p) {
3906 if (!parse_only)
3907 dump_remctxs (ctx);
3908 continue;
3910 if ((opt = match_option (p, "gc")) != p) {
3911 if (!parse_only)
3912 dump_gcs ();
3913 continue;
3915 if ((opt = match_option (p, "jit")) != p) {
3916 if (!parse_only)
3917 dump_jit ();
3918 continue;
3920 if ((opt = match_option (p, "alloc")) != p) {
3921 if (!parse_only)
3922 dump_allocations ();
3923 continue;
3925 if ((opt = match_option (p, "call")) != p) {
3926 if (!parse_only)
3927 dump_methods ();
3928 continue;
3930 if ((opt = match_option (p, "metadata")) != p) {
3931 if (!parse_only)
3932 dump_metadata ();
3933 continue;
3935 if ((opt = match_option (p, "exception")) != p) {
3936 if (!parse_only)
3937 dump_exceptions ();
3938 continue;
3940 if ((opt = match_option (p, "monitor")) != p) {
3941 if (!parse_only)
3942 dump_monitors ();
3943 continue;
3945 if ((opt = match_option (p, "heapshot")) != p) {
3946 if (!parse_only)
3947 dump_heap_shots ();
3948 continue;
3950 if ((opt = match_option (p, "sample")) != p) {
3951 if (!parse_only)
3952 dump_samples ();
3953 continue;
3955 if ((opt = match_option (p, "counters")) != p) {
3956 if (!parse_only)
3957 dump_counters ();
3958 continue;
3960 if ((opt = match_option (p, "coverage")) != p) {
3961 printf ("The log profiler no longer supports code coverage. Please use the dedicated coverage profiler instead. See mono-profilers(1) for more information.\n");
3962 continue;
3964 if ((opt = match_option (p, "stats")) != p) {
3965 if (!parse_only)
3966 dump_stats ();
3967 continue;
3969 return 0;
3971 return 1;
3974 static int
3975 add_find_spec (const char *p)
3977 if (p [0] == 'S' && p [1] == ':') {
3978 char *vale;
3979 find_size = strtoul (p + 2, &vale, 10);
3980 return 1;
3981 } else if (p [0] == 'T' && p [1] == ':') {
3982 find_name = p + 2;
3983 return 1;
3985 return 0;
3988 static void
3989 usage (void)
3991 printf ("Mono log profiler report version %d.%d\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR);
3992 printf ("Usage: mprof-report [OPTIONS] FILENAME\n");
3993 printf ("FILENAME can be '-' to read from standard input.\n");
3994 printf ("Options:\n");
3995 printf ("\t--help display this help\n");
3996 printf ("\t--out=FILE write to FILE instead of stdout\n");
3997 printf ("\t--traces collect and show backtraces\n");
3998 printf ("\t--maxframes=NUM limit backtraces to NUM entries\n");
3999 printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
4000 printf ("\t %s\n", reports);
4001 printf ("\t--method-sort=MODE sort methods according to MODE: total, self, calls\n");
4002 printf ("\t--alloc-sort=MODE sort allocations according to MODE: bytes, count\n");
4003 printf ("\t--counters-sort=MODE sort counters according to MODE: time, category\n");
4004 printf ("\t only accessible in verbose mode\n");
4005 printf ("\t--track=OB1[,OB2...] track what happens to objects OBJ1, O2 etc.\n");
4006 printf ("\t--find=FINDSPEC find and track objects matching FINFSPEC, where FINDSPEC is:\n");
4007 printf ("\t S:minimum_size or T:partial_name\n");
4008 printf ("\t--thread=THREADID consider just the data for thread THREADID\n");
4009 printf ("\t--time=FROM-TO consider data FROM seconds from startup up to TO seconds\n");
4010 printf ("\t--verbose increase verbosity level\n");
4011 printf ("\t--debug display decoding debug info for mprof-report devs\n");
4015 main (int argc, char *argv[])
4017 ProfContext *ctx;
4018 int i;
4019 outfile = stdout;
4020 for (i = 1; i < argc; ++i) {
4021 if (strcmp ("--debug", argv [i]) == 0) {
4022 debug++;
4023 } else if (strcmp ("--help", argv [i]) == 0) {
4024 usage ();
4025 return 0;
4026 } else if (strncmp ("--alloc-sort=", argv [i], 13) == 0) {
4027 const char *val = argv [i] + 13;
4028 if (strcmp (val, "bytes") == 0) {
4029 alloc_sort_mode = ALLOC_SORT_BYTES;
4030 } else if (strcmp (val, "count") == 0) {
4031 alloc_sort_mode = ALLOC_SORT_COUNT;
4032 } else {
4033 usage ();
4034 return 1;
4036 } else if (strncmp ("--method-sort=", argv [i], 14) == 0) {
4037 const char *val = argv [i] + 14;
4038 if (strcmp (val, "total") == 0) {
4039 method_sort_mode = METHOD_SORT_TOTAL;
4040 } else if (strcmp (val, "self") == 0) {
4041 method_sort_mode = METHOD_SORT_SELF;
4042 } else if (strcmp (val, "calls") == 0) {
4043 method_sort_mode = METHOD_SORT_CALLS;
4044 } else {
4045 usage ();
4046 return 1;
4048 } else if (strncmp ("--counters-sort=", argv [i], 16) == 0) {
4049 const char *val = argv [i] + 16;
4050 if (strcmp (val, "time") == 0) {
4051 counters_sort_mode = COUNTERS_SORT_TIME;
4052 } else if (strcmp (val, "category") == 0) {
4053 counters_sort_mode = COUNTERS_SORT_CATEGORY;
4054 } else {
4055 usage ();
4056 return 1;
4058 } else if (strncmp ("--reports=", argv [i], 10) == 0) {
4059 const char *val = argv [i] + 10;
4060 if (!print_reports (NULL, val, 1)) {
4061 usage ();
4062 return 1;
4064 reports = val;
4065 } else if (strncmp ("--out=", argv [i], 6) == 0) {
4066 const char *val = argv [i] + 6;
4067 outfile = fopen (val, "w");
4068 if (!outfile) {
4069 printf ("Cannot open output file: %s\n", val);
4070 return 1;
4072 } else if (strncmp ("--maxframes=", argv [i], 12) == 0) {
4073 const char *val = argv [i] + 12;
4074 char *vale;
4075 trace_max = strtoul (val, &vale, 10);
4076 } else if (strncmp ("--find=", argv [i], 7) == 0) {
4077 const char *val = argv [i] + 7;
4078 if (!add_find_spec (val)) {
4079 usage ();
4080 return 1;
4082 } else if (strncmp ("--track=", argv [i], 8) == 0) {
4083 const char *val = argv [i] + 8;
4084 char *vale;
4085 while (*val) {
4086 uintptr_t tracked_obj;
4087 if (*val == ',') {
4088 val++;
4089 continue;
4091 tracked_obj = strtoul (val, &vale, 0);
4092 found_object (tracked_obj);
4093 val = vale;
4095 } else if (strncmp ("--thread=", argv [i], 9) == 0) {
4096 const char *val = argv [i] + 9;
4097 char *vale;
4098 thread_filter = strtoul (val, &vale, 0);
4099 } else if (strncmp ("--time=", argv [i], 7) == 0) {
4100 char *val = pstrdup (argv [i] + 7);
4101 double from_secs, to_secs;
4102 char *top = strchr (val, '-');
4103 if (!top) {
4104 usage ();
4105 return 1;
4107 *top++ = 0;
4108 from_secs = atof (val);
4109 to_secs = atof (top);
4110 g_free (val);
4111 if (from_secs > to_secs) {
4112 usage ();
4113 return 1;
4115 time_from = from_secs * 1000000000;
4116 time_to = to_secs * 1000000000;
4117 use_time_filter = 1;
4118 } else if (strcmp ("--verbose", argv [i]) == 0) {
4119 verbose++;
4120 } else if (strcmp ("--traces", argv [i]) == 0) {
4121 show_traces = 1;
4122 collect_traces = 1;
4123 } else if (strncmp ("--coverage-out=", argv [i], 15) == 0) {
4124 // For backwards compatibility.
4125 } else {
4126 break;
4129 if (i >= argc) {
4130 usage ();
4131 return 2;
4133 ctx = load_file (argv [i]);
4134 if (!ctx) {
4135 printf ("Not a log profiler data file (or unsupported version).\n");
4136 return 1;
4138 while (decode_buffer (ctx));
4139 flush_context (ctx);
4140 if (num_tracked_objects)
4141 return 0;
4142 print_reports (ctx, reports, 0);
4143 return 0;