[Sockets]: Always reset internal SAEA completion when reattempting connection in...
[mono-project.git] / mono / profiler / mprof-report.c
blob48f89ca59555527d21d782cb4a475055ed041a88
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_INTERP: return "Mono Interp";
318 case MONO_COUNTER_JIT: return "Mono JIT";
319 case MONO_COUNTER_GC: return "Mono GC";
320 case MONO_COUNTER_METADATA: return "Mono Metadata";
321 case MONO_COUNTER_GENERICS: return "Mono Generics";
322 case MONO_COUNTER_SECURITY: return "Mono Security";
323 case MONO_COUNTER_RUNTIME: return "Mono Runtime";
324 case MONO_COUNTER_SYSTEM: return "Mono System";
325 case MONO_COUNTER_PROFILER: return "Mono Profiler";
326 default: return "<unknown>";
330 static const char*
331 type_name (int type)
333 switch (type) {
334 case MONO_COUNTER_INT: return "Int";
335 case MONO_COUNTER_UINT: return "UInt";
336 case MONO_COUNTER_WORD: return "Word";
337 case MONO_COUNTER_LONG: return "Long";
338 case MONO_COUNTER_ULONG: return "ULong";
339 case MONO_COUNTER_DOUBLE: return "Double";
340 case MONO_COUNTER_STRING: return "String";
341 case MONO_COUNTER_TIME_INTERVAL: return "Time Interval";
342 default: return "<unknown>";
346 static const char*
347 unit_name (int unit)
349 switch (unit) {
350 case MONO_COUNTER_RAW: return "Raw";
351 case MONO_COUNTER_BYTES: return "Bytes";
352 case MONO_COUNTER_TIME: return "Time";
353 case MONO_COUNTER_COUNT: return "Count";
354 case MONO_COUNTER_PERCENTAGE: return "Percentage";
355 default: return "<unknown>";
359 static const char*
360 variance_name (int variance)
362 switch (variance) {
363 case MONO_COUNTER_MONOTONIC: return "Monotonic";
364 case MONO_COUNTER_CONSTANT: return "Constant";
365 case MONO_COUNTER_VARIABLE: return "Variable";
366 default: return "<unknown>";
370 static void
371 dump_counters_value (Counter *counter, const char *key_format, const char *key, void *value)
373 char format[32];
375 if (value == NULL) {
376 snprintf (format, sizeof (format), "%s : %%s\n", key_format);
377 fprintf (outfile, format, key, "<null>");
378 } else {
379 switch (counter->type) {
380 case MONO_COUNTER_INT:
381 #if SIZEOF_VOID_P == 4
382 case MONO_COUNTER_WORD:
383 #endif
384 snprintf (format, sizeof (format), "%s : %%d\n", key_format);
385 fprintf (outfile, format, key, *(int32_t*)value);
386 break;
387 case MONO_COUNTER_UINT:
388 snprintf (format, sizeof (format), "%s : %%u\n", key_format);
389 fprintf (outfile, format, key, *(uint32_t*)value);
390 break;
391 case MONO_COUNTER_LONG:
392 #if SIZEOF_VOID_P == 8
393 case MONO_COUNTER_WORD:
394 #endif
395 case MONO_COUNTER_TIME_INTERVAL:
396 if (counter->type == MONO_COUNTER_LONG && counter->unit == MONO_COUNTER_TIME) {
397 snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
398 fprintf (outfile, format, key, (double)*(int64_t*)value / 10000.0);
399 } else if (counter->type == MONO_COUNTER_TIME_INTERVAL) {
400 snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
401 fprintf (outfile, format, key, (double)*(int64_t*)value / 1000.0);
402 } else {
403 snprintf (format, sizeof (format), "%s : %%u\n", key_format);
404 fprintf (outfile, format, key, *(int64_t*)value);
406 break;
407 case MONO_COUNTER_ULONG:
408 snprintf (format, sizeof (format), "%s : %%llu\n", key_format);
409 fprintf (outfile, format, key, *(uint64_t*)value);
410 break;
411 case MONO_COUNTER_DOUBLE:
412 snprintf (format, sizeof (format), "%s : %%f\n", key_format);
413 fprintf (outfile, format, key, *(double*)value);
414 break;
415 case MONO_COUNTER_STRING:
416 snprintf (format, sizeof (format), "%s : %%s\n", key_format);
417 fprintf (outfile, format, key, *(char*)value);
418 break;
423 static void
424 dump_counters (void)
426 Counter *counter;
427 CounterValue *cvalue;
428 CounterTimestamp *ctimestamp;
429 CounterSection *csection;
430 CounterList *clist;
431 char strtimestamp[17];
432 int i, section_printed;
434 fprintf (outfile, "\nCounters:\n");
436 if (!verbose) {
437 char counters_to_print[][64] = {
438 "Methods from AOT",
439 "Methods JITted using mono JIT",
440 "Methods JITted using LLVM",
441 "Total time spent JITting (sec)",
442 "User Time",
443 "System Time",
444 "Total Time",
445 "Working Set",
446 "Private Bytes",
447 "Virtual Bytes",
448 "Page Faults",
449 "CPU Load Average - 1min",
450 "CPU Load Average - 5min",
451 "CPU Load Average - 15min",
455 for (csection = counters_sections; csection; csection = csection->next) {
456 section_printed = 0;
458 for (clist = csection->counters; clist; clist = clist->next) {
459 counter = clist->counter;
460 if (!counter->values_last)
461 continue;
463 for (i = 0; counters_to_print [i][0] != 0; i++) {
464 if (strcmp (counters_to_print [i], counter->name) == 0) {
465 if (!section_printed) {
466 fprintf (outfile, "\t%s:\n", csection->value);
467 section_printed = 1;
470 dump_counters_value (counter, "\t\t%-30s", counter->name, counter->values_last->buffer);
471 break;
476 } else if (counters_sort_mode == COUNTERS_SORT_TIME) {
477 for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
478 fprintf (outfile, "\t%llu:%02llu:%02llu:%02llu.%03llu:\n",
479 (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 / 24 % 1000),
480 (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 % 24),
481 (unsigned long long) (ctimestamp->value / 1000 / 60 % 60),
482 (unsigned long long) (ctimestamp->value / 1000 % 60),
483 (unsigned long long) (ctimestamp->value % 1000));
485 for (csection = ctimestamp->sections; csection; csection = csection->next) {
486 fprintf (outfile, "\t\t%s:\n", csection->value);
488 for (clist = csection->counters; clist; clist = clist->next) {
489 counter = clist->counter;
490 for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
491 if (cvalue->timestamp != ctimestamp->value)
492 continue;
494 dump_counters_value (counter, "\t\t\t%-30s", counter->name, cvalue->buffer);
499 } else if (counters_sort_mode == COUNTERS_SORT_CATEGORY) {
500 for (csection = counters_sections; csection; csection = csection->next) {
501 fprintf (outfile, "\t%s:\n", csection->value);
503 for (clist = csection->counters; clist; clist = clist->next) {
504 counter = clist->counter;
505 fprintf (outfile, "\t\t%s: [type: %s, unit: %s, variance: %s]\n",
506 counter->name, type_name (counter->type), unit_name (counter->unit), variance_name (counter->variance));
508 for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
509 snprintf (strtimestamp, sizeof (strtimestamp), "%llu:%02llu:%02llu:%02llu.%03llu",
510 (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 / 24 % 1000),
511 (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 % 24),
512 (unsigned long long) (cvalue->timestamp / 1000 / 60 % 60),
513 (unsigned long long) (cvalue->timestamp / 1000 % 60),
514 (unsigned long long) (cvalue->timestamp % 1000));
516 dump_counters_value (counter, "\t\t\t%s", strtimestamp, cvalue->buffer);
523 static int num_images;
524 typedef struct _ImageDesc ImageDesc;
525 struct _ImageDesc {
526 ImageDesc *next;
527 intptr_t image;
528 char *filename;
531 static ImageDesc* image_hash [SMALL_HASH_SIZE] = {0};
533 static void
534 add_image (intptr_t image, char *name)
536 int slot = ((image >> 2) & 0xffff) % SMALL_HASH_SIZE;
537 ImageDesc *cd = (ImageDesc *) g_malloc (sizeof (ImageDesc));
538 cd->image = image;
539 cd->filename = pstrdup (name);
540 cd->next = image_hash [slot];
541 image_hash [slot] = cd;
542 num_images++;
545 static int num_assemblies;
547 typedef struct _AssemblyDesc AssemblyDesc;
548 struct _AssemblyDesc {
549 AssemblyDesc *next;
550 intptr_t assembly;
551 char *asmname;
554 static AssemblyDesc* assembly_hash [SMALL_HASH_SIZE] = {0};
556 static void
557 add_assembly (intptr_t assembly, char *name)
559 int slot = ((assembly >> 2) & 0xffff) % SMALL_HASH_SIZE;
560 AssemblyDesc *cd = (AssemblyDesc *) g_malloc (sizeof (AssemblyDesc));
561 cd->assembly = assembly;
562 cd->asmname = pstrdup (name);
563 cd->next = assembly_hash [slot];
564 assembly_hash [slot] = cd;
565 num_assemblies++;
568 typedef struct _BackTrace BackTrace;
569 typedef struct {
570 uint64_t count;
571 BackTrace *bt;
572 } CallContext;
574 typedef struct {
575 int count;
576 int size;
577 CallContext *traces;
578 } TraceDesc;
580 typedef struct _ClassDesc ClassDesc;
581 struct _ClassDesc {
582 ClassDesc *next;
583 intptr_t klass;
584 char *name;
585 intptr_t allocs;
586 uint64_t alloc_size;
587 TraceDesc traces;
590 static ClassDesc* class_hash [HASH_SIZE] = {0};
591 static int num_classes = 0;
593 static ClassDesc*
594 add_class (intptr_t klass, const char *name)
596 int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
597 ClassDesc *cd;
598 cd = class_hash [slot];
599 while (cd && cd->klass != klass)
600 cd = cd->next;
601 /* we resolved an unknown class (unless we had the code unloaded) */
602 if (cd) {
603 /*printf ("resolved unknown: %s\n", name);*/
604 g_free (cd->name);
605 cd->name = pstrdup (name);
606 return cd;
608 cd = (ClassDesc *) g_calloc (sizeof (ClassDesc), 1);
609 cd->klass = klass;
610 cd->name = pstrdup (name);
611 cd->next = class_hash [slot];
612 cd->allocs = 0;
613 cd->alloc_size = 0;
614 cd->traces.count = 0;
615 cd->traces.size = 0;
616 cd->traces.traces = NULL;
617 class_hash [slot] = cd;
618 num_classes++;
619 return cd;
622 static ClassDesc *
623 lookup_class (intptr_t klass)
625 int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
626 ClassDesc *cd = class_hash [slot];
627 while (cd && cd->klass != klass)
628 cd = cd->next;
629 if (!cd) {
630 char buf [128];
631 snprintf (buf, sizeof (buf), "unresolved class %p", (void*)klass);
632 return add_class (klass, buf);
634 return cd;
637 typedef struct _VTableDesc VTableDesc;
638 struct _VTableDesc {
639 VTableDesc *next;
640 intptr_t vtable;
641 ClassDesc *klass;
644 static VTableDesc* vtable_hash [HASH_SIZE] = {0};
646 static VTableDesc*
647 add_vtable (intptr_t vtable, intptr_t klass)
649 int slot = ((vtable >> 2) & 0xffff) % HASH_SIZE;
651 VTableDesc *vt = vtable_hash [slot];
653 while (vt && vt->vtable != vtable)
654 vt = vt->next;
656 if (vt)
657 return vt;
659 vt = (VTableDesc *) g_calloc (sizeof (VTableDesc), 1);
661 vt->vtable = vtable;
662 vt->klass = lookup_class (klass);
663 vt->next = vtable_hash [slot];
665 vtable_hash [slot] = vt;
667 return vt;
670 static VTableDesc *
671 lookup_vtable (intptr_t vtable)
673 int slot = ((vtable >> 2) & 0xffff) % HASH_SIZE;
674 VTableDesc *vt = vtable_hash [slot];
676 while (vt && vt->vtable != vtable)
677 vt = vt->next;
679 if (!vt)
680 return add_vtable (vtable, 0);
682 return vt;
685 typedef struct _MethodDesc MethodDesc;
686 struct _MethodDesc {
687 MethodDesc *next;
688 intptr_t method;
689 char *name;
690 intptr_t code;
691 int len;
692 int recurse_count;
693 int sample_hits;
694 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 */
695 uint64_t calls;
696 uint64_t total_time;
697 uint64_t callee_time;
698 uint64_t self_time;
699 TraceDesc traces;
702 static MethodDesc* method_hash [HASH_SIZE] = {0};
703 static int num_methods = 0;
705 static MethodDesc*
706 add_method (intptr_t method, const char *name, intptr_t code, int len)
708 int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
709 MethodDesc *cd;
710 cd = method_hash [slot];
711 while (cd && cd->method != method)
712 cd = cd->next;
713 /* we resolved an unknown method (unless we had the code unloaded) */
714 if (cd) {
715 cd->code = code;
716 cd->len = len;
717 /*printf ("resolved unknown: %s\n", name);*/
718 g_free (cd->name);
719 cd->name = pstrdup (name);
720 return cd;
722 cd = (MethodDesc *) g_calloc (sizeof (MethodDesc), 1);
723 cd->method = method;
724 cd->name = pstrdup (name);
725 cd->code = code;
726 cd->len = len;
727 cd->calls = 0;
728 cd->total_time = 0;
729 cd->traces.count = 0;
730 cd->traces.size = 0;
731 cd->traces.traces = NULL;
732 cd->next = method_hash [slot];
733 method_hash [slot] = cd;
734 num_methods++;
735 return cd;
738 static MethodDesc *
739 lookup_method (intptr_t method)
741 int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
742 MethodDesc *cd = method_hash [slot];
743 while (cd && cd->method != method)
744 cd = cd->next;
745 if (!cd) {
746 char buf [128];
747 snprintf (buf, sizeof (buf), "unknown method %p", (void*)method);
748 return add_method (method, buf, 0, 0);
750 return cd;
753 static int num_stat_samples = 0;
754 static int size_stat_samples = 0;
755 uintptr_t *stat_samples = NULL;
756 int *stat_sample_desc = NULL;
758 static void
759 add_stat_sample (int type, uintptr_t ip) {
760 if (num_stat_samples == size_stat_samples) {
761 size_stat_samples *= 2;
762 if (!size_stat_samples)
763 size_stat_samples = 32;
764 stat_samples = (uintptr_t *) g_realloc (stat_samples, size_stat_samples * sizeof (uintptr_t));
765 stat_sample_desc = (int *) g_realloc (stat_sample_desc, size_stat_samples * sizeof (int));
767 stat_samples [num_stat_samples] = ip;
768 stat_sample_desc [num_stat_samples++] = type;
771 static MethodDesc*
772 lookup_method_by_ip (uintptr_t ip)
774 int i;
775 MethodDesc* m;
776 /* dumb */
777 for (i = 0; i < HASH_SIZE; ++i) {
778 m = method_hash [i];
779 while (m) {
780 //printf ("checking %p against %p-%p\n", (void*)ip, (void*)(m->code), (void*)(m->code + m->len));
781 if (ip >= (uintptr_t)m->code && ip < (uintptr_t)m->code + m->len) {
782 return m;
784 m = m->next;
787 return NULL;
790 static int
791 compare_method_samples (const void *a, const void *b)
793 MethodDesc *const *A = (MethodDesc *const *)a;
794 MethodDesc *const *B = (MethodDesc *const *)b;
795 if ((*A)->sample_hits == (*B)->sample_hits)
796 return 0;
797 if ((*B)->sample_hits < (*A)->sample_hits)
798 return -1;
799 return 1;
802 typedef struct _UnmanagedSymbol UnmanagedSymbol;
803 struct _UnmanagedSymbol {
804 UnmanagedSymbol *parent;
805 char *name;
806 int is_binary;
807 uintptr_t addr;
808 uintptr_t size;
809 uintptr_t sample_hits;
812 static UnmanagedSymbol **usymbols = NULL;
813 static int usymbols_size = 0;
814 static int usymbols_num = 0;
816 static int
817 compare_usymbol_addr (const void *a, const void *b)
819 UnmanagedSymbol *const *A = (UnmanagedSymbol *const *)a;
820 UnmanagedSymbol *const *B = (UnmanagedSymbol *const *)b;
821 if ((*B)->addr == (*A)->addr)
822 return 0;
823 if ((*B)->addr > (*A)->addr)
824 return -1;
825 return 1;
828 static int
829 compare_usymbol_samples (const void *a, const void *b)
831 UnmanagedSymbol *const *A = (UnmanagedSymbol *const *)a;
832 UnmanagedSymbol *const *B = (UnmanagedSymbol *const *)b;
833 if ((*B)->sample_hits == (*A)->sample_hits)
834 return 0;
835 if ((*B)->sample_hits < (*A)->sample_hits)
836 return -1;
837 return 1;
840 static void
841 add_unmanaged_symbol (uintptr_t addr, char *name, uintptr_t size)
843 UnmanagedSymbol *sym;
844 if (usymbols_num == usymbols_size) {
845 int new_size = usymbols_size * 2;
846 if (!new_size)
847 new_size = 16;
848 usymbols = (UnmanagedSymbol **) g_realloc (usymbols, sizeof (void*) * new_size);
849 usymbols_size = new_size;
851 sym = (UnmanagedSymbol *) g_calloc (sizeof (UnmanagedSymbol), 1);
852 sym->addr = addr;
853 sym->name = name;
854 sym->size = size;
855 usymbols [usymbols_num++] = sym;
858 /* only valid after the symbols are sorted */
859 static UnmanagedSymbol*
860 lookup_unmanaged_symbol (uintptr_t addr)
862 int r = usymbols_num - 1;
863 int l = 0;
864 UnmanagedSymbol *sym;
865 int last_best = -1;
866 while (r >= l) {
867 int m = (l + r) / 2;
868 sym = usymbols [m];
869 if (addr == sym->addr)
870 return sym;
871 if (addr < sym->addr) {
872 r = m - 1;
873 } else if (addr > sym->addr) {
874 l = m + 1;
875 last_best = m;
878 if (last_best >= 0 && (addr - usymbols [last_best]->addr) < 4096)
879 return usymbols [last_best];
880 return NULL;
883 /* we use the same structure for binaries */
884 static UnmanagedSymbol **ubinaries = NULL;
885 static int ubinaries_size = 0;
886 static int ubinaries_num = 0;
888 static void
889 add_unmanaged_binary (uintptr_t addr, char *name, uintptr_t size)
891 UnmanagedSymbol *sym;
892 if (ubinaries_num == ubinaries_size) {
893 int new_size = ubinaries_size * 2;
894 if (!new_size)
895 new_size = 16;
896 ubinaries = (UnmanagedSymbol **) g_realloc (ubinaries, sizeof (void*) * new_size);
897 ubinaries_size = new_size;
899 sym = (UnmanagedSymbol *) g_calloc (sizeof (UnmanagedSymbol), 1);
900 sym->addr = addr;
901 sym->name = name;
902 sym->size = size;
903 sym->is_binary = 1;
904 ubinaries [ubinaries_num++] = sym;
907 static UnmanagedSymbol*
908 lookup_unmanaged_binary (uintptr_t addr)
910 int i;
911 for (i = 0; i < ubinaries_num; ++i) {
912 UnmanagedSymbol *ubin = ubinaries [i];
913 if (addr >= ubin->addr && addr < ubin->addr + ubin->size) {
914 return ubin;
917 return NULL;
920 // For backwards compatibility.
921 enum {
922 TYPE_SAMPLE_UBIN = 2 << 4,
924 TYPE_COVERAGE = 9,
926 TYPE_COVERAGE_ASSEMBLY = 0 << 4,
927 TYPE_COVERAGE_METHOD = 1 << 4,
928 TYPE_COVERAGE_STATEMENT = 2 << 4,
929 TYPE_COVERAGE_CLASS = 3 << 4,
932 enum {
933 SAMPLE_CYCLES = 1,
934 SAMPLE_INSTRUCTIONS,
935 SAMPLE_CACHE_MISSES,
936 SAMPLE_CACHE_REFS,
937 SAMPLE_BRANCHES,
938 SAMPLE_BRANCH_MISSES,
941 enum {
942 MONO_GC_EVENT_MARK_START = 1,
943 MONO_GC_EVENT_MARK_END = 2,
944 MONO_GC_EVENT_RECLAIM_START = 3,
945 MONO_GC_EVENT_RECLAIM_END = 4,
948 static const char*
949 sample_type_name (int type)
951 switch (type) {
952 case SAMPLE_CYCLES: return "cycles";
953 case SAMPLE_INSTRUCTIONS: return "instructions retired";
954 case SAMPLE_CACHE_MISSES: return "cache misses";
955 case SAMPLE_CACHE_REFS: return "cache references";
956 case SAMPLE_BRANCHES: return "executed branches";
957 case SAMPLE_BRANCH_MISSES: return "unpredicted branches";
959 return "unknown";
962 static void
963 set_usym_parent (UnmanagedSymbol** cachedus, int count)
965 int i;
966 for (i = 0; i < count; ++i) {
967 UnmanagedSymbol *ubin = lookup_unmanaged_binary (cachedus [i]->addr);
968 if (ubin == cachedus [i])
969 continue;
970 cachedus [i]->parent = ubin;
974 static void
975 print_usym (UnmanagedSymbol* um)
977 if (um->parent)
978 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);
979 else
980 fprintf (outfile, "\t%6zd %6.2f %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name);
983 static int
984 sym_percent (uintptr_t sample_hits)
986 double pc;
987 if (verbose)
988 return 1;
989 pc = sample_hits*100.0/num_stat_samples;
990 return pc >= 0.1;
993 static void
994 dump_samples (void)
996 int i, u;
997 int count = 0, msize = 0;
998 int unmanaged_hits = 0;
999 int unresolved_hits = 0;
1000 MethodDesc** cachedm = NULL;
1001 int ucount = 0, usize = 0;
1002 UnmanagedSymbol** cachedus = NULL;
1003 if (!num_stat_samples)
1004 return;
1005 mono_qsort (usymbols, usymbols_num, sizeof (UnmanagedSymbol*), compare_usymbol_addr);
1006 for (i = 0; i < num_stat_samples; ++i) {
1007 MethodDesc *m = lookup_method_by_ip (stat_samples [i]);
1008 if (m) {
1009 if (!m->sample_hits) {
1010 if (count == msize) {
1011 msize *= 2;
1012 if (!msize)
1013 msize = 4;
1014 cachedm = (MethodDesc **) g_realloc (cachedm, sizeof (void*) * msize);
1016 cachedm [count++] = m;
1018 m->sample_hits++;
1019 } else {
1020 UnmanagedSymbol *usym = lookup_unmanaged_symbol (stat_samples [i]);
1021 if (!usym) {
1022 unresolved_hits++;
1023 //printf ("unmanaged hit at %p\n", (void*)stat_samples [i]);
1024 usym = lookup_unmanaged_binary (stat_samples [i]);
1026 if (usym) {
1027 if (!usym->sample_hits) {
1028 if (ucount == usize) {
1029 usize *= 2;
1030 if (!usize)
1031 usize = 4;
1032 cachedus = (UnmanagedSymbol **) g_realloc (cachedus, sizeof (void*) * usize);
1034 cachedus [ucount++] = usym;
1036 usym->sample_hits++;
1038 unmanaged_hits++;
1041 mono_qsort (cachedm, count, sizeof (MethodDesc*), compare_method_samples);
1042 mono_qsort (cachedus, ucount, sizeof (UnmanagedSymbol*), compare_usymbol_samples);
1043 set_usym_parent (cachedus, ucount);
1044 fprintf (outfile, "\nStatistical samples summary\n");
1045 fprintf (outfile, "\tSample type: %s\n", sample_type_name (stat_sample_desc [0]));
1046 fprintf (outfile, "\tUnmanaged hits: %6d (%4.1f%%)\n", unmanaged_hits, (100.0*unmanaged_hits)/num_stat_samples);
1047 fprintf (outfile, "\tManaged hits: %6d (%4.1f%%)\n", num_stat_samples - unmanaged_hits, (100.0*(num_stat_samples-unmanaged_hits))/num_stat_samples);
1048 fprintf (outfile, "\tUnresolved hits: %6d (%4.1f%%)\n", unresolved_hits, (100.0*unresolved_hits)/num_stat_samples);
1049 fprintf (outfile, "\t%6s %6s %s\n", "Hits", "%", "Method name");
1050 i = 0;
1051 u = 0;
1052 while (i < count || u < ucount) {
1053 if (i < count) {
1054 MethodDesc *m = cachedm [i];
1055 if (u < ucount) {
1056 UnmanagedSymbol *um = cachedus [u];
1057 if (um->sample_hits > m->sample_hits) {
1058 if (!sym_percent (um->sample_hits))
1059 break;
1060 print_usym (um);
1061 u++;
1062 continue;
1065 if (!sym_percent (m->sample_hits))
1066 break;
1067 fprintf (outfile, "\t%6d %6.2f %s\n", m->sample_hits, m->sample_hits*100.0/num_stat_samples, m->name);
1068 i++;
1069 continue;
1071 if (u < ucount) {
1072 UnmanagedSymbol *um = cachedus [u];
1073 if (!sym_percent (um->sample_hits))
1074 break;
1075 print_usym (um);
1076 u++;
1077 continue;
1082 typedef struct _HeapClassDesc HeapClassDesc;
1083 typedef struct {
1084 HeapClassDesc *klass;
1085 uint64_t count;
1086 } HeapClassRevRef;
1088 struct _HeapClassDesc {
1089 ClassDesc *klass;
1090 int64_t count;
1091 int64_t total_size;
1092 HeapClassRevRef *rev_hash;
1093 int rev_hash_size;
1094 int rev_count;
1095 uintptr_t pinned_references;
1096 uintptr_t root_references;
1099 static int
1100 add_rev_class_hashed (HeapClassRevRef *rev_hash, uintptr_t size, HeapClassDesc *hklass, uint64_t value)
1102 uintptr_t i;
1103 uintptr_t start_pos;
1104 start_pos = (hklass->klass->klass >> 2) % size;
1105 assert (start_pos < size);
1106 i = start_pos;
1107 do {
1108 if (rev_hash [i].klass == hklass) {
1109 rev_hash [i].count += value;
1110 return 0;
1111 } else if (!rev_hash [i].klass) {
1112 rev_hash [i].klass = hklass;
1113 rev_hash [i].count += value;
1114 start_pos = 0;
1115 for (i = 0; i < size; ++i)
1116 if (rev_hash [i].klass && rev_hash [i].klass->klass == hklass->klass)
1117 start_pos ++;
1118 assert (start_pos == 1);
1119 return 1;
1121 /* wrap around */
1122 if (++i == size)
1123 i = 0;
1124 } while (i != start_pos);
1125 /* should not happen */
1126 printf ("failed revref store\n");
1127 return 0;
1130 static void
1131 add_heap_class_rev (HeapClassDesc *from, HeapClassDesc *to)
1133 uintptr_t i;
1134 if (to->rev_count * 2 >= to->rev_hash_size) {
1135 HeapClassRevRef *n;
1136 uintptr_t old_size = to->rev_hash_size;
1137 to->rev_hash_size *= 2;
1138 if (to->rev_hash_size == 0)
1139 to->rev_hash_size = 4;
1140 n = (HeapClassRevRef *) g_calloc (sizeof (HeapClassRevRef) * to->rev_hash_size, 1);
1141 for (i = 0; i < old_size; ++i) {
1142 if (to->rev_hash [i].klass)
1143 add_rev_class_hashed (n, to->rev_hash_size, to->rev_hash [i].klass, to->rev_hash [i].count);
1145 if (to->rev_hash)
1146 g_free (to->rev_hash);
1147 to->rev_hash = n;
1149 to->rev_count += add_rev_class_hashed (to->rev_hash, to->rev_hash_size, from, 1);
1152 typedef struct {
1153 uintptr_t objaddr;
1154 HeapClassDesc *hklass;
1155 uintptr_t num_refs;
1156 uintptr_t refs [0];
1157 } HeapObjectDesc;
1159 typedef struct _HeapShot HeapShot;
1160 struct _HeapShot {
1161 HeapShot *next;
1162 uint64_t timestamp;
1163 int class_count;
1164 int hash_size;
1165 HeapClassDesc **class_hash;
1166 HeapClassDesc **sorted;
1167 HeapObjectDesc **objects_hash;
1168 uintptr_t objects_count;
1169 uintptr_t objects_hash_size;
1170 uintptr_t num_roots;
1171 uintptr_t *roots;
1172 uintptr_t *roots_extra;
1173 int *roots_types;
1176 static HeapShot *heap_shots = NULL;
1177 static int num_heap_shots = 0;
1179 static HeapShot*
1180 new_heap_shot (uint64_t timestamp)
1182 HeapShot *hs = (HeapShot *) g_calloc (sizeof (HeapShot), 1);
1183 hs->hash_size = 4;
1184 hs->class_hash = (HeapClassDesc **) g_calloc (sizeof (void*), hs->hash_size);
1185 hs->timestamp = timestamp;
1186 num_heap_shots++;
1187 hs->next = heap_shots;
1188 heap_shots = hs;
1189 return hs;
1192 static HeapClassDesc*
1193 heap_class_lookup (HeapShot *hs, ClassDesc *klass)
1195 int i;
1196 unsigned int start_pos;
1197 start_pos = ((uintptr_t)klass->klass >> 2) % hs->hash_size;
1198 i = start_pos;
1199 do {
1200 HeapClassDesc* cd = hs->class_hash [i];
1201 if (!cd)
1202 return NULL;
1203 if (cd->klass == klass)
1204 return cd;
1205 /* wrap around */
1206 if (++i == hs->hash_size)
1207 i = 0;
1208 } while (i != start_pos);
1209 return NULL;
1212 static int
1213 add_heap_hashed (HeapClassDesc **hash, HeapClassDesc **retv, uintptr_t hsize, ClassDesc *klass, uint64_t size, uint64_t count)
1215 uintptr_t i;
1216 uintptr_t start_pos;
1217 start_pos = ((uintptr_t)klass->klass >> 2) % hsize;
1218 i = start_pos;
1219 do {
1220 if (hash [i] && hash [i]->klass == klass) {
1221 hash [i]->total_size += size;
1222 hash [i]->count += count;
1223 *retv = hash [i];
1224 return 0;
1225 } else if (!hash [i]) {
1226 if (*retv) {
1227 hash [i] = *retv;
1228 return 1;
1230 hash [i] = (HeapClassDesc *) g_calloc (sizeof (HeapClassDesc), 1);
1231 hash [i]->klass = klass;
1232 hash [i]->total_size += size;
1233 hash [i]->count += count;
1234 *retv = hash [i];
1235 return 1;
1237 /* wrap around */
1238 if (++i == hsize)
1239 i = 0;
1240 } while (i != start_pos);
1241 /* should not happen */
1242 printf ("failed heap class store\n");
1243 return 0;
1246 static HeapClassDesc*
1247 add_heap_shot_class (HeapShot *hs, ClassDesc *klass, uint64_t size)
1249 HeapClassDesc *res;
1250 int i;
1251 if (hs->class_count * 2 >= hs->hash_size) {
1252 HeapClassDesc **n;
1253 int old_size = hs->hash_size;
1254 hs->hash_size *= 2;
1255 if (hs->hash_size == 0)
1256 hs->hash_size = 4;
1257 n = (HeapClassDesc **) g_calloc (sizeof (void*) * hs->hash_size, 1);
1258 for (i = 0; i < old_size; ++i) {
1259 res = hs->class_hash [i];
1260 if (hs->class_hash [i])
1261 add_heap_hashed (n, &res, hs->hash_size, hs->class_hash [i]->klass, hs->class_hash [i]->total_size, hs->class_hash [i]->count);
1263 if (hs->class_hash)
1264 g_free (hs->class_hash);
1265 hs->class_hash = n;
1267 res = NULL;
1268 hs->class_count += add_heap_hashed (hs->class_hash, &res, hs->hash_size, klass, size, 1);
1269 //if (res->count == 1)
1270 // printf ("added heap class: %s\n", res->klass->name);
1271 return res;
1274 static HeapObjectDesc*
1275 alloc_heap_obj (uintptr_t objaddr, HeapClassDesc *hklass, uintptr_t num_refs)
1277 HeapObjectDesc* ho = (HeapObjectDesc *) g_calloc (sizeof (HeapObjectDesc) + num_refs * sizeof (uintptr_t), 1);
1278 ho->objaddr = objaddr;
1279 ho->hklass = hklass;
1280 ho->num_refs = num_refs;
1281 return ho;
1284 static uintptr_t
1285 heap_shot_find_obj_slot (HeapShot *hs, uintptr_t objaddr)
1287 uintptr_t i;
1288 uintptr_t start_pos;
1289 HeapObjectDesc **hash = hs->objects_hash;
1290 if (hs->objects_hash_size == 0)
1291 return -1;
1292 start_pos = ((uintptr_t)objaddr >> 3) % hs->objects_hash_size;
1293 i = start_pos;
1294 do {
1295 if (hash [i] && hash [i]->objaddr == objaddr) {
1296 return i;
1297 } else if (!hash [i]) {
1298 break; /* fail */
1300 /* wrap around */
1301 if (++i == hs->objects_hash_size)
1302 i = 0;
1303 } while (i != start_pos);
1304 /* should not happen */
1305 //printf ("failed heap obj slot\n");
1306 return -1;
1309 static HeapObjectDesc*
1310 heap_shot_obj_add_refs (HeapShot *hs, uintptr_t objaddr, uintptr_t num, uintptr_t *ref_offset)
1312 HeapObjectDesc **hash = hs->objects_hash;
1313 uintptr_t i = heap_shot_find_obj_slot (hs, objaddr);
1314 if (i >= 0) {
1315 HeapObjectDesc* ho = alloc_heap_obj (objaddr, hash [i]->hklass, hash [i]->num_refs + num);
1316 *ref_offset = hash [i]->num_refs;
1317 memcpy (ho->refs, hash [i]->refs, hash [i]->num_refs * sizeof (uintptr_t));
1318 g_free (hash [i]);
1319 hash [i] = ho;
1320 return ho;
1322 /* should not happen */
1323 printf ("failed heap obj update\n");
1324 return NULL;
1328 static uintptr_t
1329 add_heap_hashed_obj (HeapObjectDesc **hash, uintptr_t hsize, HeapObjectDesc *obj)
1331 uintptr_t i;
1332 uintptr_t start_pos;
1333 start_pos = ((uintptr_t)obj->objaddr >> 3) % hsize;
1334 i = start_pos;
1335 do {
1336 if (hash [i] && hash [i]->objaddr == obj->objaddr) {
1337 printf ("duplicate object!\n");
1338 return 0;
1339 } else if (!hash [i]) {
1340 hash [i] = obj;
1341 return 1;
1343 /* wrap around */
1344 if (++i == hsize)
1345 i = 0;
1346 } while (i != start_pos);
1347 /* should not happen */
1348 printf ("failed heap obj store\n");
1349 return 0;
1352 static void
1353 add_heap_shot_obj (HeapShot *hs, HeapObjectDesc *obj)
1355 uintptr_t i;
1356 if (hs->objects_count * 2 >= hs->objects_hash_size) {
1357 HeapObjectDesc **n;
1358 uintptr_t old_size = hs->objects_hash_size;
1359 hs->objects_hash_size *= 2;
1360 if (hs->objects_hash_size == 0)
1361 hs->objects_hash_size = 4;
1362 n = (HeapObjectDesc **) g_calloc (sizeof (void*) * hs->objects_hash_size, 1);
1363 for (i = 0; i < old_size; ++i) {
1364 if (hs->objects_hash [i])
1365 add_heap_hashed_obj (n, hs->objects_hash_size, hs->objects_hash [i]);
1367 if (hs->objects_hash)
1368 g_free (hs->objects_hash);
1369 hs->objects_hash = n;
1371 hs->objects_count += add_heap_hashed_obj (hs->objects_hash, hs->objects_hash_size, obj);
1374 static void
1375 heap_shot_resolve_reverse_refs (HeapShot *hs)
1377 uintptr_t i;
1378 for (i = 0; i < hs->objects_hash_size; ++i) {
1379 uintptr_t r;
1380 HeapObjectDesc *ho = hs->objects_hash [i];
1381 if (!ho)
1382 continue;
1383 for (r = 0; r < ho->num_refs; ++r) {
1384 uintptr_t oi = heap_shot_find_obj_slot (hs, ho->refs [r]);
1385 add_heap_class_rev (ho->hklass, hs->objects_hash [oi]->hklass);
1390 #define MARK_GRAY 1
1391 #define MARK_BLACK 2
1393 static void
1394 heap_shot_mark_objects (HeapShot *hs)
1396 uintptr_t i, oi, r;
1397 unsigned char *marks;
1398 HeapObjectDesc *obj, *ref;
1399 int marked_some;
1400 uintptr_t num_marked = 0, num_unmarked;
1401 for (i = 0; i < hs->num_roots; ++i) {
1402 HeapClassDesc *cd;
1403 oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
1404 if (oi == -1) {
1405 continue;
1407 obj = hs->objects_hash [oi];
1408 cd = obj->hklass;
1409 if (hs->roots_types [i] & MONO_PROFILER_GC_ROOT_PINNING)
1410 cd->pinned_references++;
1411 cd->root_references++;
1413 if (!debug)
1414 return;
1415 /* consistency checks: it seems not all the objects are walked in the heap in some cases */
1416 marks = (unsigned char *) g_calloc (hs->objects_hash_size, 1);
1417 if (!marks)
1418 return;
1419 for (i = 0; i < hs->num_roots; ++i) {
1420 oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
1421 if (oi == -1) {
1422 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);
1423 continue;
1425 obj = hs->objects_hash [oi];
1426 if (!marks [oi]) {
1427 marks [oi] = obj->num_refs? MARK_GRAY: MARK_BLACK;
1428 num_marked++;
1431 marked_some = 1;
1432 while (marked_some) {
1433 marked_some = 0;
1434 for (i = 0; i < hs->objects_hash_size; ++i) {
1435 if (marks [i] != MARK_GRAY)
1436 continue;
1437 marks [i] = MARK_BLACK;
1438 obj = hs->objects_hash [i];
1439 for (r = 0; r < obj->num_refs; ++r) {
1440 oi = heap_shot_find_obj_slot (hs, obj->refs [r]);
1441 if (oi == -1) {
1442 fprintf (outfile, "referenced obj %p not found in heap\n", (void*)obj->refs [r]);
1443 continue;
1445 ref = hs->objects_hash [oi];
1446 if (!marks [oi]) {
1447 marks [oi] = ref->num_refs? MARK_GRAY: MARK_BLACK;
1450 marked_some++;
1454 num_unmarked = 0;
1455 for (i = 0; i < hs->objects_hash_size; ++i) {
1456 if (hs->objects_hash [i] && !marks [i]) {
1457 num_unmarked++;
1458 fprintf (outfile, "object %p (%s) unmarked\n", (void*)hs->objects_hash [i], hs->objects_hash [i]->hklass->klass->name);
1461 fprintf (outfile, "Total unmarked: %zd/%zd\n", num_unmarked, hs->objects_count);
1462 g_free (marks);
1465 static void
1466 heap_shot_free_objects (HeapShot *hs)
1468 uintptr_t i;
1469 for (i = 0; i < hs->objects_hash_size; ++i) {
1470 HeapObjectDesc *ho = hs->objects_hash [i];
1471 if (ho)
1472 g_free (ho);
1474 if (hs->objects_hash)
1475 g_free (hs->objects_hash);
1476 hs->objects_hash = NULL;
1477 hs->objects_hash_size = 0;
1478 hs->objects_count = 0;
1482 struct _BackTrace {
1483 BackTrace *next;
1484 unsigned int hash;
1485 int count;
1486 int id;
1487 MethodDesc *methods [1];
1490 static BackTrace *backtrace_hash [HASH_SIZE];
1491 static BackTrace **backtraces = NULL;
1492 static int num_backtraces = 0;
1493 static int next_backtrace = 0;
1495 static int
1496 hash_backtrace (int count, MethodDesc **methods)
1498 int hash = count;
1499 int i;
1500 for (i = 0; i < count; ++i) {
1501 hash = (hash << 5) - hash + methods [i]->method;
1503 return hash;
1506 static int
1507 compare_backtrace (BackTrace *bt, int count, MethodDesc **methods)
1509 int i;
1510 if (bt->count != count)
1511 return 0;
1512 for (i = 0; i < count; ++i)
1513 if (methods [i] != bt->methods [i])
1514 return 0;
1515 return 1;
1518 static BackTrace*
1519 add_backtrace (int count, MethodDesc **methods)
1521 int hash = hash_backtrace (count, methods);
1522 int slot = (hash & 0xffff) % HASH_SIZE;
1523 BackTrace *bt = backtrace_hash [slot];
1524 while (bt) {
1525 if (bt->hash == hash && compare_backtrace (bt, count, methods))
1526 return bt;
1527 bt = bt->next;
1529 bt = (BackTrace *) g_malloc (sizeof (BackTrace) + ((count - 1) * sizeof (void*)));
1530 bt->next = backtrace_hash [slot];
1531 backtrace_hash [slot] = bt;
1532 if (next_backtrace == num_backtraces) {
1533 num_backtraces *= 2;
1534 if (!num_backtraces)
1535 num_backtraces = 16;
1536 backtraces = (BackTrace **) g_realloc (backtraces, sizeof (void*) * num_backtraces);
1538 bt->id = next_backtrace++;
1539 backtraces [bt->id] = bt;
1540 bt->count = count;
1541 bt->hash = hash;
1542 for (slot = 0; slot < count; ++slot)
1543 bt->methods [slot] = methods [slot];
1545 return bt;
1548 typedef struct _MonitorDesc MonitorDesc;
1549 typedef struct _ThreadContext ThreadContext;
1550 typedef struct _DomainContext DomainContext;
1551 typedef struct _RemCtxContext RemCtxContext;
1553 typedef struct {
1554 FILE *file;
1555 #if defined (HAVE_SYS_ZLIB)
1556 gzFile gzfile;
1557 #endif
1558 unsigned char *buf;
1559 int size;
1560 int data_version;
1561 int version_major;
1562 int version_minor;
1563 int timer_overhead;
1564 int pid;
1565 int port;
1566 char *args;
1567 char *arch;
1568 char *os;
1569 uint64_t startup_time;
1570 ThreadContext *threads;
1571 ThreadContext *current_thread;
1572 DomainContext *domains;
1573 DomainContext *current_domain;
1574 RemCtxContext *remctxs;
1575 RemCtxContext *current_remctx;
1576 } ProfContext;
1578 struct _ThreadContext {
1579 ThreadContext *next;
1580 intptr_t thread_id;
1581 char *name;
1582 /* emulated stack */
1583 MethodDesc **stack;
1584 uint64_t *time_stack;
1585 uint64_t *callee_time_stack;
1586 uint64_t last_time;
1587 uint64_t contention_start;
1588 MonitorDesc *monitor;
1589 int stack_size;
1590 int stack_id;
1591 HeapShot *current_heap_shot;
1592 uintptr_t num_roots;
1593 uintptr_t size_roots;
1594 uintptr_t *roots;
1595 uintptr_t *roots_extra;
1596 int *roots_types;
1597 uint64_t gc_start_times [3];
1600 struct _DomainContext {
1601 DomainContext *next;
1602 intptr_t domain_id;
1603 const char *friendly_name;
1606 struct _RemCtxContext {
1607 RemCtxContext *next;
1608 intptr_t remctx_id;
1609 intptr_t domain_id;
1612 static void
1613 ensure_buffer (ProfContext *ctx, int size)
1615 if (ctx->size < size) {
1616 ctx->buf = (unsigned char *) g_realloc (ctx->buf, size);
1617 ctx->size = size;
1621 static int
1622 load_data (ProfContext *ctx, int size)
1624 ensure_buffer (ctx, size);
1625 #if defined (HAVE_SYS_ZLIB)
1626 if (ctx->gzfile) {
1627 int r = gzread (ctx->gzfile, ctx->buf, size);
1628 if (r == 0)
1629 return size == 0? 1: 0;
1630 return r == size;
1631 } else
1632 #endif
1634 int r = fread (ctx->buf, size, 1, ctx->file);
1635 if (r == 0)
1636 return size == 0? 1: 0;
1637 return r;
1641 static ThreadContext*
1642 get_thread (ProfContext *ctx, intptr_t thread_id)
1644 ThreadContext *thread;
1645 if (ctx->current_thread && ctx->current_thread->thread_id == thread_id)
1646 return ctx->current_thread;
1647 thread = ctx->threads;
1648 while (thread) {
1649 if (thread->thread_id == thread_id) {
1650 return thread;
1652 thread = thread->next;
1654 thread = (ThreadContext *) g_calloc (sizeof (ThreadContext), 1);
1655 thread->next = ctx->threads;
1656 ctx->threads = thread;
1657 thread->thread_id = thread_id;
1658 thread->last_time = 0;
1659 thread->stack_id = 0;
1660 thread->stack_size = 32;
1661 thread->stack = (MethodDesc **) g_malloc (thread->stack_size * sizeof (void*));
1662 thread->time_stack = (uint64_t *) g_malloc (thread->stack_size * sizeof (uint64_t));
1663 thread->callee_time_stack = (uint64_t *) g_malloc (thread->stack_size * sizeof (uint64_t));
1664 return thread;
1667 static DomainContext *
1668 get_domain (ProfContext *ctx, intptr_t domain_id)
1670 if (ctx->current_domain && ctx->current_domain->domain_id == domain_id)
1671 return ctx->current_domain;
1673 DomainContext *domain = ctx->domains;
1675 while (domain) {
1676 if (domain->domain_id == domain_id)
1677 return domain;
1679 domain = domain->next;
1682 domain = (DomainContext *) g_calloc (sizeof (DomainContext), 1);
1683 domain->next = ctx->domains;
1684 ctx->domains = domain;
1685 domain->domain_id = domain_id;
1687 return domain;
1690 static RemCtxContext *
1691 get_remctx (ProfContext *ctx, intptr_t remctx_id)
1693 if (ctx->current_remctx && ctx->current_remctx->remctx_id == remctx_id)
1694 return ctx->current_remctx;
1696 RemCtxContext *remctx = ctx->remctxs;
1698 while (remctx) {
1699 if (remctx->remctx_id == remctx_id)
1700 return remctx;
1702 remctx = remctx->next;
1705 remctx = (RemCtxContext *) g_calloc (sizeof (RemCtxContext), 1);
1706 remctx->next = ctx->remctxs;
1707 ctx->remctxs = remctx;
1708 remctx->remctx_id = remctx_id;
1710 return remctx;
1713 static ThreadContext*
1714 load_thread (ProfContext *ctx, intptr_t thread_id)
1716 ThreadContext *thread = get_thread (ctx, thread_id);
1717 ctx->current_thread = thread;
1718 return thread;
1721 static void
1722 ensure_thread_stack (ThreadContext *thread)
1724 if (thread->stack_id == thread->stack_size) {
1725 thread->stack_size *= 2;
1726 thread->stack = (MethodDesc **) g_realloc (thread->stack, thread->stack_size * sizeof (void*));
1727 thread->time_stack = (uint64_t *) g_realloc (thread->time_stack, thread->stack_size * sizeof (uint64_t));
1728 thread->callee_time_stack = (uint64_t *) g_realloc (thread->callee_time_stack, thread->stack_size * sizeof (uint64_t));
1732 static int
1733 add_trace_hashed (CallContext *traces, int size, BackTrace *bt, uint64_t value)
1735 int i;
1736 unsigned int start_pos;
1737 start_pos = bt->hash % size;
1738 i = start_pos;
1739 do {
1740 if (traces [i].bt == bt) {
1741 traces [i].count += value;
1742 return 0;
1743 } else if (!traces [i].bt) {
1744 traces [i].bt = bt;
1745 traces [i].count += value;
1746 return 1;
1748 /* wrap around */
1749 if (++i == size)
1750 i = 0;
1751 } while (i != start_pos);
1752 /* should not happen */
1753 printf ("failed trace store\n");
1754 return 0;
1757 static void
1758 add_trace_bt (BackTrace *bt, TraceDesc *trace, uint64_t value)
1760 int i;
1761 if (!collect_traces)
1762 return;
1763 if (trace->count * 2 >= trace->size) {
1764 CallContext *n;
1765 int old_size = trace->size;
1766 trace->size *= 2;
1767 if (trace->size == 0)
1768 trace->size = 4;
1769 n = (CallContext *) g_calloc (sizeof (CallContext) * trace->size, 1);
1770 for (i = 0; i < old_size; ++i) {
1771 if (trace->traces [i].bt)
1772 add_trace_hashed (n, trace->size, trace->traces [i].bt, trace->traces [i].count);
1774 if (trace->traces)
1775 g_free (trace->traces);
1776 trace->traces = n;
1778 trace->count += add_trace_hashed (trace->traces, trace->size, bt, value);
1781 static BackTrace*
1782 add_trace_thread (ThreadContext *thread, TraceDesc *trace, uint64_t value)
1784 BackTrace *bt;
1785 int count = thread->stack_id;
1786 if (!collect_traces)
1787 return NULL;
1788 if (count > trace_max)
1789 count = trace_max;
1790 bt = add_backtrace (count, thread->stack + thread->stack_id - count);
1791 add_trace_bt (bt, trace, value);
1792 return bt;
1795 static BackTrace*
1796 add_trace_methods (MethodDesc **methods, int count, TraceDesc *trace, uint64_t value)
1798 BackTrace *bt;
1799 if (!collect_traces)
1800 return NULL;
1801 if (count > trace_max)
1802 count = trace_max;
1803 bt = add_backtrace (count, methods);
1804 add_trace_bt (bt, trace, value);
1805 return bt;
1808 static void
1809 thread_add_root (ThreadContext *ctx, uintptr_t obj, int root_type, uintptr_t extra_info)
1811 if (ctx->num_roots == ctx->size_roots) {
1812 int new_size = ctx->size_roots * 2;
1813 if (!new_size)
1814 new_size = 4;
1815 ctx->roots = (uintptr_t *) g_realloc (ctx->roots, new_size * sizeof (uintptr_t));
1816 ctx->roots_extra = (uintptr_t *) g_realloc (ctx->roots_extra, new_size * sizeof (uintptr_t));
1817 ctx->roots_types = (int *) g_realloc (ctx->roots_types, new_size * sizeof (int));
1818 ctx->size_roots = new_size;
1820 ctx->roots_types [ctx->num_roots] = root_type;
1821 ctx->roots_extra [ctx->num_roots] = extra_info;
1822 ctx->roots [ctx->num_roots++] = obj;
1825 static int
1826 compare_callc (const void *a, const void *b)
1828 const CallContext *A = (const CallContext *)a;
1829 const CallContext *B = (const CallContext *)b;
1830 if (B->count == A->count)
1831 return 0;
1832 if (B->count < A->count)
1833 return -1;
1834 return 1;
1837 static void
1838 sort_context_array (TraceDesc* traces)
1840 int i, j;
1841 for (i = 0, j = 0; i < traces->size; ++i) {
1842 if (traces->traces [i].bt) {
1843 traces->traces [j].bt = traces->traces [i].bt;
1844 traces->traces [j].count = traces->traces [i].count;
1845 j++;
1848 mono_qsort (traces->traces, traces->count, sizeof (CallContext), compare_callc);
1851 static void
1852 push_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
1854 ensure_thread_stack (thread);
1855 thread->time_stack [thread->stack_id] = timestamp;
1856 thread->callee_time_stack [thread->stack_id] = 0;
1857 thread->stack [thread->stack_id++] = method;
1858 method->recurse_count++;
1861 static void
1862 pop_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
1864 method->recurse_count--;
1865 if (thread->stack_id > 0 && thread->stack [thread->stack_id - 1] == method) {
1866 uint64_t tdiff;
1867 thread->stack_id--;
1868 method->calls++;
1869 if (timestamp < thread->time_stack [thread->stack_id])
1870 fprintf (outfile, "time went backwards for %s\n", method->name);
1871 tdiff = timestamp - thread->time_stack [thread->stack_id];
1872 if (thread->callee_time_stack [thread->stack_id] > tdiff)
1873 fprintf (outfile, "callee time bigger for %s\n", method->name);
1874 method->self_time += tdiff - thread->callee_time_stack [thread->stack_id];
1875 method->callee_time += thread->callee_time_stack [thread->stack_id];
1876 if (thread->stack_id)
1877 thread->callee_time_stack [thread->stack_id - 1] += tdiff;
1878 //fprintf (outfile, "method %s took %d\n", method->name, (int)(tdiff/1000));
1879 } else {
1880 fprintf (outfile, "unmatched leave at stack pos: %d for method %s\n", thread->stack_id, method->name);
1884 typedef struct {
1885 uint64_t total_time;
1886 uint64_t max_time;
1887 int count;
1888 } GCDesc;
1889 static GCDesc gc_info [3];
1890 static uint64_t max_heap_size;
1891 static uint64_t gc_object_moves;
1892 static int gc_resizes;
1893 typedef struct {
1894 uint64_t created;
1895 uint64_t destroyed;
1896 uint64_t live;
1897 uint64_t max_live;
1898 TraceDesc traces;
1899 TraceDesc destroy_traces;
1900 } HandleInfo;
1901 static HandleInfo handle_info [4];
1903 static const char*
1904 gc_event_name (int ev)
1906 switch (ev) {
1907 case MONO_GC_EVENT_START: return "start";
1908 case MONO_GC_EVENT_MARK_START: return "mark start";
1909 case MONO_GC_EVENT_MARK_END: return "mark end";
1910 case MONO_GC_EVENT_RECLAIM_START: return "reclaim start";
1911 case MONO_GC_EVENT_RECLAIM_END: return "reclaim end";
1912 case MONO_GC_EVENT_END: return "end";
1913 case MONO_GC_EVENT_PRE_STOP_WORLD: return "pre stop";
1914 case MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED: return "pre stop lock";
1915 case MONO_GC_EVENT_POST_STOP_WORLD: return "post stop";
1916 case MONO_GC_EVENT_PRE_START_WORLD: return "pre start";
1917 case MONO_GC_EVENT_POST_START_WORLD: return "post start";
1918 case MONO_GC_EVENT_POST_START_WORLD_UNLOCKED: return "post start unlock";
1919 default:
1920 return "unknown";
1924 static const char*
1925 sync_point_name (int type)
1927 switch (type) {
1928 case SYNC_POINT_PERIODIC: return "periodic";
1929 case SYNC_POINT_WORLD_STOP: return "world stop";
1930 case SYNC_POINT_WORLD_START: return "world start";
1931 default:
1932 return "unknown";
1936 static uint64_t clause_summary [MONO_EXCEPTION_CLAUSE_FAULT + 1];
1937 static uint64_t throw_count = 0;
1938 static TraceDesc exc_traces;
1940 static const char*
1941 clause_name (int type)
1943 switch (type) {
1944 case MONO_EXCEPTION_CLAUSE_NONE: return "catch";
1945 case MONO_EXCEPTION_CLAUSE_FILTER: return "filter";
1946 case MONO_EXCEPTION_CLAUSE_FINALLY: return "finally";
1947 case MONO_EXCEPTION_CLAUSE_FAULT: return "fault";
1948 default: return "invalid";
1952 static uint64_t monitor_contention;
1953 static uint64_t monitor_failed;
1954 static uint64_t monitor_acquired;
1956 struct _MonitorDesc {
1957 MonitorDesc *next;
1958 uintptr_t objid;
1959 uintptr_t contentions;
1960 uint64_t wait_time;
1961 uint64_t max_wait_time;
1962 TraceDesc traces;
1965 static MonitorDesc* monitor_hash [SMALL_HASH_SIZE] = {0};
1966 static int num_monitors = 0;
1968 static MonitorDesc*
1969 lookup_monitor (uintptr_t objid)
1971 int slot = ((objid >> 3) & 0xffff) % SMALL_HASH_SIZE;
1972 MonitorDesc *cd = monitor_hash [slot];
1973 while (cd && cd->objid != objid)
1974 cd = cd->next;
1975 if (!cd) {
1976 cd = (MonitorDesc *) g_calloc (sizeof (MonitorDesc), 1);
1977 cd->objid = objid;
1978 cd->next = monitor_hash [slot];
1979 monitor_hash [slot] = cd;
1980 num_monitors++;
1982 return cd;
1985 static const char*
1986 monitor_ev_name (int ev)
1988 switch (ev) {
1989 case MONO_PROFILER_MONITOR_CONTENTION: return "contended";
1990 case MONO_PROFILER_MONITOR_DONE: return "acquired";
1991 case MONO_PROFILER_MONITOR_FAIL: return "not taken";
1992 default: return "invalid";
1996 static const char*
1997 get_handle_name (int htype)
1999 switch (htype) {
2000 case 0: return "weak";
2001 case 1: return "weaktrack";
2002 case 2: return "normal";
2003 case 3: return "pinned";
2004 default: return "unknown";
2008 static const char*
2009 get_root_name (int rtype)
2011 switch (rtype & MONO_PROFILER_GC_ROOT_TYPEMASK) {
2012 case MONO_PROFILER_GC_ROOT_STACK: return "stack";
2013 case MONO_PROFILER_GC_ROOT_FINALIZER: return "finalizer";
2014 case MONO_PROFILER_GC_ROOT_HANDLE: return "handle";
2015 case MONO_PROFILER_GC_ROOT_OTHER: return "other";
2016 case MONO_PROFILER_GC_ROOT_MISC: return "misc";
2017 default: return "unknown";
2021 static uint64_t
2022 decode_uleb128 (uint8_t *buf, uint8_t **endbuf)
2024 uint64_t res = 0;
2025 int shift = 0;
2027 while (1) {
2028 uint8_t b = *buf++;
2029 res |= (((uint64_t) (b & 0x7f)) << shift);
2031 if (!(b & 0x80))
2032 break;
2034 shift += 7;
2037 *endbuf = buf;
2039 return res;
2042 static intptr_t
2043 decode_sleb128 (uint8_t *buf, uint8_t **endbuf)
2045 uint8_t *p = buf;
2046 intptr_t res = 0;
2047 int shift = 0;
2049 while (1) {
2050 uint8_t b = *p;
2051 p++;
2053 res = res | (((intptr_t) (b & 0x7f)) << shift);
2054 shift += 7;
2056 if (!(b & 0x80)) {
2057 if (shift < sizeof (intptr_t) * 8 && (b & 0x40))
2058 res |= - ((intptr_t) 1 << shift);
2060 break;
2064 *endbuf = p;
2066 return res;
2069 static MethodDesc**
2070 decode_bt (ProfContext *ctx, MethodDesc** sframes, int *size, unsigned char *p, unsigned char **endp, intptr_t ptr_base, intptr_t *method_base)
2072 MethodDesc **frames;
2073 int i;
2074 if (ctx->data_version < 13)
2075 decode_uleb128 (p, &p); /* flags */
2076 int count = decode_uleb128 (p, &p);
2077 if (count > *size)
2078 frames = (MethodDesc **) g_malloc (count * sizeof (void*));
2079 else
2080 frames = sframes;
2081 for (i = 0; i < count; ++i) {
2082 intptr_t ptrdiff = decode_sleb128 (p, &p);
2083 if (ctx->data_version > 12) {
2084 *method_base += ptrdiff;
2085 frames [i] = lookup_method (*method_base);
2086 } else {
2087 frames [i] = lookup_method (ptr_base + ptrdiff);
2090 *size = count;
2091 *endp = p;
2092 return frames;
2095 static void
2096 tracked_creation (uintptr_t obj, ClassDesc *cd, uint64_t size, BackTrace *bt, uint64_t timestamp)
2098 int i;
2099 for (i = 0; i < num_tracked_objects; ++i) {
2100 if (tracked_objects [i] != obj)
2101 continue;
2102 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);
2103 if (bt && bt->count) {
2104 int k;
2105 for (k = 0; k < bt->count; ++k)
2106 fprintf (outfile, "\t%s\n", bt->methods [k]->name);
2111 static void
2112 track_handle (uintptr_t obj, int htype, uint32_t handle, BackTrace *bt, uint64_t timestamp)
2114 int i;
2115 for (i = 0; i < num_tracked_objects; ++i) {
2116 if (tracked_objects [i] != obj)
2117 continue;
2118 fprintf (outfile, "Object %p referenced from handle %u at %.3f secs.\n", (void*)obj, handle, (timestamp - startup_time) / 1000000000.0);
2119 if (bt && bt->count) {
2120 int k;
2121 for (k = 0; k < bt->count; ++k)
2122 fprintf (outfile, "\t%s\n", bt->methods [k]->name);
2127 static void
2128 track_move (uintptr_t src, uintptr_t dst)
2130 int i;
2131 for (i = 0; i < num_tracked_objects; ++i) {
2132 if (tracked_objects [i] == src)
2133 fprintf (outfile, "Object %p moved to %p\n", (void*)src, (void*)dst);
2134 else if (tracked_objects [i] == dst)
2135 fprintf (outfile, "Object %p moved from %p\n", (void*)dst, (void*)src);
2139 static void
2140 track_obj_reference (uintptr_t obj, uintptr_t parent, ClassDesc *cd)
2142 int i;
2143 for (i = 0; i < num_tracked_objects; ++i) {
2144 if (tracked_objects [i] == obj)
2145 fprintf (outfile, "Object %p referenced from %p (%s).\n", (void*)obj, (void*)parent, cd->name);
2149 static void
2150 found_object (uintptr_t obj)
2152 num_tracked_objects ++;
2153 tracked_objects = (uintptr_t *) g_realloc (tracked_objects, num_tracked_objects * sizeof (tracked_objects [0]));
2154 tracked_objects [num_tracked_objects - 1] = obj;
2157 static int num_jit_helpers = 0;
2158 static int jit_helpers_code_size = 0;
2160 static const char*
2161 code_buffer_desc (int type)
2163 switch (type) {
2164 case MONO_PROFILER_CODE_BUFFER_METHOD:
2165 return "method";
2166 case MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE:
2167 return "method trampoline";
2168 case MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE:
2169 return "unbox trampoline";
2170 case MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE:
2171 return "imt trampoline";
2172 case MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE:
2173 return "generics trampoline";
2174 case MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE:
2175 return "specific trampoline";
2176 case MONO_PROFILER_CODE_BUFFER_HELPER:
2177 return "misc helper";
2178 case MONO_PROFILER_CODE_BUFFER_MONITOR:
2179 return "monitor/lock";
2180 case MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE:
2181 return "delegate invoke";
2182 case MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING:
2183 return "exception handling";
2184 default:
2185 return "unspecified";
2189 #define OBJ_ADDR(diff) ((obj_base + diff) << 3)
2190 #define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
2193 /* Stats */
2194 #define BUFFER_HEADER_SIZE 48
2196 typedef struct {
2197 int count, min_size, max_size, bytes;
2198 } EventStat;
2200 static int buffer_count;
2201 static EventStat stats [256];
2203 static void
2204 record_event_stats (int type, int size)
2206 ++stats [type].count;
2207 if (!stats [type].min_size)
2208 stats [type].min_size = size;
2209 stats [type].min_size = MIN (stats [type].min_size, size);
2210 stats [type].max_size = MAX (stats [type].max_size, size);
2211 stats [type].bytes += size;
2214 static int
2215 decode_buffer (ProfContext *ctx)
2217 unsigned char *p;
2218 unsigned char *end;
2219 intptr_t thread_id;
2220 intptr_t ptr_base;
2221 intptr_t obj_base;
2222 intptr_t method_base;
2223 uint64_t time_base;
2224 uint64_t file_offset;
2225 int len, i;
2226 ThreadContext *thread;
2228 #ifdef HAVE_SYS_ZLIB
2229 if (ctx->gzfile)
2230 file_offset = gztell (ctx->gzfile);
2231 else
2232 #endif
2233 file_offset = ftell (ctx->file);
2234 if (!load_data (ctx, 48))
2235 return 0;
2236 p = ctx->buf;
2237 if (read_int32 (p) != BUF_ID) {
2238 fprintf (outfile, "Incorrect buffer id: 0x%x\n", read_int32 (p));
2239 for (i = 0; i < 48; ++i) {
2240 fprintf (outfile, "0x%x%s", p [i], i % 8?" ":"\n");
2242 return 0;
2244 len = read_int32 (p + 4);
2245 time_base = read_int64 (p + 8);
2246 ptr_base = read_int64 (p + 16);
2247 obj_base = read_int64 (p + 24);
2248 thread_id = read_int64 (p + 32);
2249 method_base = read_int64 (p + 40);
2250 if (debug)
2251 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);
2252 thread = load_thread (ctx, thread_id);
2253 if (!load_data (ctx, len))
2254 return 0;
2256 ++buffer_count;
2258 if (!startup_time) {
2259 startup_time = time_base;
2260 if (use_time_filter) {
2261 time_from += startup_time;
2262 time_to += startup_time;
2265 for (i = 0; i < thread->stack_id; ++i)
2266 thread->stack [i]->recurse_count++;
2267 p = ctx->buf;
2268 end = p + len;
2269 while (p < end) {
2270 unsigned char *start = p;
2271 unsigned char event = *p;
2272 switch (*p & 0xf) {
2273 case TYPE_GC: {
2274 int subtype = *p & 0xf0;
2275 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2276 LOG_TIME (time_base, tdiff);
2277 time_base += tdiff;
2278 if (subtype == TYPE_GC_RESIZE) {
2279 uint64_t new_size = decode_uleb128 (p, &p);
2280 if (debug)
2281 fprintf (outfile, "gc heap resized to %llu\n", (unsigned long long) new_size);
2282 gc_resizes++;
2283 if (new_size > max_heap_size)
2284 max_heap_size = new_size;
2285 } else if (subtype == TYPE_GC_EVENT) {
2286 uint64_t ev;
2287 if (ctx->data_version > 12)
2288 ev = *p++;
2289 else
2290 ev = decode_uleb128 (p, &p);
2291 int gen;
2292 if (ctx->data_version > 12)
2293 gen = *p++;
2294 else
2295 gen = decode_uleb128 (p, &p);
2296 if (debug)
2297 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);
2298 if (gen > 2) {
2299 fprintf (outfile, "incorrect gc gen: %d\n", gen);
2300 break;
2302 if (ev == MONO_GC_EVENT_START) {
2303 thread->gc_start_times [gen] = time_base;
2304 gc_info [gen].count++;
2305 } else if (ev == MONO_GC_EVENT_END) {
2306 tdiff = time_base - thread->gc_start_times [gen];
2307 gc_info [gen].total_time += tdiff;
2308 if (tdiff > gc_info [gen].max_time)
2309 gc_info [gen].max_time = tdiff;
2311 } else if (subtype == TYPE_GC_MOVE) {
2312 int j, num = decode_uleb128 (p, &p);
2313 gc_object_moves += num / 2;
2314 for (j = 0; j < num; j += 2) {
2315 intptr_t obj1diff = decode_sleb128 (p, &p);
2316 intptr_t obj2diff = decode_sleb128 (p, &p);
2317 if (num_tracked_objects)
2318 track_move (OBJ_ADDR (obj1diff), OBJ_ADDR (obj2diff));
2319 if (debug) {
2320 fprintf (outfile, "moved obj %p to %p\n", (void*)OBJ_ADDR (obj1diff), (void*)OBJ_ADDR (obj2diff));
2323 } else if (subtype == TYPE_GC_HANDLE_CREATED || subtype == TYPE_GC_HANDLE_CREATED_BT) {
2324 int has_bt = subtype == TYPE_GC_HANDLE_CREATED_BT;
2325 int num_bt = 0;
2326 MethodDesc *sframes [8];
2327 MethodDesc **frames = sframes;
2328 int htype = decode_uleb128 (p, &p);
2329 uint32_t handle = decode_uleb128 (p, &p);
2330 intptr_t objdiff = decode_sleb128 (p, &p);
2331 if (has_bt) {
2332 num_bt = 8;
2333 frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2334 if (!frames) {
2335 fprintf (outfile, "Cannot load backtrace\n");
2336 return 0;
2339 if (htype > 3)
2340 return 0;
2341 if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2342 handle_info [htype].created++;
2343 handle_info [htype].live++;
2344 if (handle_info [htype].live > handle_info [htype].max_live)
2345 handle_info [htype].max_live = handle_info [htype].live;
2346 BackTrace *bt;
2347 if (has_bt)
2348 bt = add_trace_methods (frames, num_bt, &handle_info [htype].traces, 1);
2349 else
2350 bt = add_trace_thread (thread, &handle_info [htype].traces, 1);
2351 if (num_tracked_objects)
2352 track_handle (OBJ_ADDR (objdiff), htype, handle, bt, time_base);
2354 if (debug)
2355 fprintf (outfile, "handle (%s) %u created for object %p\n", get_handle_name (htype), handle, (void*)OBJ_ADDR (objdiff));
2356 if (frames != sframes)
2357 g_free (frames);
2358 } else if (subtype == TYPE_GC_HANDLE_DESTROYED || subtype == TYPE_GC_HANDLE_DESTROYED_BT) {
2359 int has_bt = subtype == TYPE_GC_HANDLE_DESTROYED_BT;
2360 int num_bt = 0;
2361 MethodDesc *sframes [8];
2362 MethodDesc **frames = sframes;
2363 int htype = decode_uleb128 (p, &p);
2364 uint32_t handle = decode_uleb128 (p, &p);
2365 if (has_bt) {
2366 num_bt = 8;
2367 frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2368 if (!frames) {
2369 fprintf (outfile, "Cannot load backtrace\n");
2370 return 0;
2373 if (htype > 3)
2374 return 0;
2375 if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2376 handle_info [htype].destroyed ++;
2377 handle_info [htype].live--;
2378 BackTrace *bt;
2379 if (has_bt)
2380 bt = add_trace_methods (frames, num_bt, &handle_info [htype].destroy_traces, 1);
2381 else
2382 bt = add_trace_thread (thread, &handle_info [htype].destroy_traces, 1);
2383 /* TODO: track_handle_free () - would need to record and keep track of the associated object address... */
2385 if (debug)
2386 fprintf (outfile, "handle (%s) %u destroyed\n", get_handle_name (htype), handle);
2387 if (frames != sframes)
2388 g_free (frames);
2389 } else if (subtype == TYPE_GC_FINALIZE_START) {
2390 // TODO: Generate a finalizer report based on these events.
2391 if (debug)
2392 fprintf (outfile, "gc finalizer queue being processed at %llu\n", (unsigned long long) time_base);
2393 } else if (subtype == TYPE_GC_FINALIZE_END) {
2394 if (debug)
2395 fprintf (outfile, "gc finalizer queue finished processing at %llu\n", (unsigned long long) time_base);
2396 } else if (subtype == TYPE_GC_FINALIZE_OBJECT_START) {
2397 intptr_t objdiff = decode_sleb128 (p, &p);
2398 if (debug)
2399 fprintf (outfile, "gc finalizing object %p at %llu\n", (void *) OBJ_ADDR (objdiff), (unsigned long long) time_base);
2400 } else if (subtype == TYPE_GC_FINALIZE_OBJECT_END) {
2401 intptr_t objdiff = decode_sleb128 (p, &p);
2402 if (debug)
2403 fprintf (outfile, "gc finalized object %p at %llu\n", (void *) OBJ_ADDR (objdiff), (unsigned long long) time_base);
2405 break;
2407 case TYPE_METADATA: {
2408 int subtype = *p & 0xf0;
2409 const char *load_str = subtype == TYPE_END_LOAD ? "loaded" : "unloaded";
2410 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2411 int mtype = *p++;
2412 intptr_t ptrdiff = decode_sleb128 (p, &p);
2413 LOG_TIME (time_base, tdiff);
2414 time_base += tdiff;
2415 if (mtype == TYPE_CLASS) {
2416 intptr_t imptrdiff = decode_sleb128 (p, &p);
2417 if (ctx->data_version < 13)
2418 decode_uleb128 (p, &p); /* flags */
2419 if (debug)
2420 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);
2421 add_class (ptr_base + ptrdiff, (char*)p);
2422 while (*p) p++;
2423 p++;
2424 } else if (mtype == TYPE_IMAGE) {
2425 if (ctx->data_version < 13)
2426 decode_uleb128 (p, &p); /* flags */
2427 if (debug)
2428 fprintf (outfile, "%s image %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
2429 if (subtype == TYPE_END_LOAD)
2430 add_image (ptr_base + ptrdiff, (char*)p);
2431 while (*p) p++;
2432 p++;
2433 if (ctx->data_version >= 16) {
2434 while (*p) p++; // mvid
2435 p++;
2437 } else if (mtype == TYPE_ASSEMBLY) {
2438 if (ctx->data_version > 13)
2439 decode_sleb128 (p, &p); // image
2440 if (ctx->data_version < 13)
2441 decode_uleb128 (p, &p); /* flags */
2442 if (debug)
2443 fprintf (outfile, "%s assembly %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
2444 if (subtype == TYPE_END_LOAD)
2445 add_assembly (ptr_base + ptrdiff, (char*)p);
2446 while (*p) p++;
2447 p++;
2448 } else if (mtype == TYPE_DOMAIN) {
2449 if (ctx->data_version < 13)
2450 decode_uleb128 (p, &p); /* flags */
2451 DomainContext *nd = get_domain (ctx, ptr_base + ptrdiff);
2452 /* no subtype means it's a name event, rather than start/stop */
2453 if (subtype == 0)
2454 nd->friendly_name = pstrdup ((char *) p);
2455 if (debug) {
2456 if (subtype == 0)
2457 fprintf (outfile, "domain %p named at %llu: %s\n", (void *) (ptr_base + ptrdiff), (unsigned long long) time_base, p);
2458 else
2459 fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
2461 if (subtype == 0) {
2462 while (*p) p++;
2463 p++;
2465 } else if (mtype == TYPE_CONTEXT) {
2466 if (ctx->data_version < 13)
2467 decode_uleb128 (p, &p); /* flags */
2468 intptr_t domaindiff = decode_sleb128 (p, &p);
2469 if (debug)
2470 fprintf (outfile, "%s context %p (%p) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), (void *) (ptr_base + domaindiff), (unsigned long long) time_base);
2471 if (subtype == TYPE_END_LOAD)
2472 get_remctx (ctx, ptr_base + ptrdiff)->domain_id = ptr_base + domaindiff;
2473 } else if (mtype == TYPE_THREAD) {
2474 if (ctx->data_version < 13)
2475 decode_uleb128 (p, &p); /* flags */
2476 ThreadContext *nt = get_thread (ctx, ptr_base + ptrdiff);
2477 /* no subtype means it's a name event, rather than start/stop */
2478 if (subtype == 0)
2479 nt->name = pstrdup ((char*)p);
2480 if (debug) {
2481 if (subtype == 0)
2482 fprintf (outfile, "thread %p named at %llu: %s\n", (void*)(ptr_base + ptrdiff), (unsigned long long) time_base, p);
2483 else
2484 fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
2486 if (subtype == 0) {
2487 while (*p) p++;
2488 p++;
2490 } else if (mtype == TYPE_VTABLE) {
2491 intptr_t domaindiff = decode_sleb128 (p, &p);
2492 intptr_t classdiff = decode_sleb128 (p, &p);
2493 if (debug)
2494 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);
2495 add_vtable (ptr_base + ptrdiff, ptr_base + classdiff);
2497 break;
2499 case TYPE_ALLOC: {
2500 int has_bt = *p & TYPE_ALLOC_BT;
2501 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2502 intptr_t ptrdiff = decode_sleb128 (p, &p);
2503 intptr_t objdiff = decode_sleb128 (p, &p);
2504 uint64_t len;
2505 int num_bt = 0;
2506 MethodDesc* sframes [8];
2507 MethodDesc** frames = sframes;
2508 ClassDesc *cd;
2509 if (ctx->data_version > 14) {
2510 VTableDesc *vt = lookup_vtable (ptr_base + ptrdiff);
2511 cd = vt->klass;
2512 } else
2513 cd = lookup_class (ptr_base + ptrdiff);
2514 len = decode_uleb128 (p, &p);
2515 LOG_TIME (time_base, tdiff);
2516 time_base += tdiff;
2517 if (debug)
2518 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);
2519 if (has_bt) {
2520 num_bt = 8;
2521 frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2522 if (!frames) {
2523 fprintf (outfile, "Cannot load backtrace\n");
2524 return 0;
2527 if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2528 BackTrace *bt;
2529 cd->allocs++;
2530 cd->alloc_size += len;
2531 if (has_bt)
2532 bt = add_trace_methods (frames, num_bt, &cd->traces, len);
2533 else
2534 bt = add_trace_thread (thread, &cd->traces, len);
2535 if (find_size && len >= find_size) {
2536 if (!find_name || strstr (cd->name, find_name))
2537 found_object (OBJ_ADDR (objdiff));
2538 } else if (!find_size && find_name && strstr (cd->name, find_name)) {
2539 found_object (OBJ_ADDR (objdiff));
2541 if (num_tracked_objects)
2542 tracked_creation (OBJ_ADDR (objdiff), cd, len, bt, time_base);
2544 if (frames != sframes)
2545 g_free (frames);
2546 break;
2548 case TYPE_METHOD: {
2549 int subtype = *p & 0xf0;
2550 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2551 int64_t ptrdiff = decode_sleb128 (p, &p);
2552 LOG_TIME (time_base, tdiff);
2553 time_base += tdiff;
2554 method_base += ptrdiff;
2555 if (subtype == TYPE_JIT) {
2556 intptr_t codediff = decode_sleb128 (p, &p);
2557 int codelen = decode_uleb128 (p, &p);
2558 MethodDesc *jitted_method;
2559 if (debug)
2560 fprintf (outfile, "jitted method %p (%s), size: %d, code: %p\n", (void*)(method_base), p, codelen, (void*)(ptr_base + codediff));
2561 jitted_method = add_method (method_base, (char*)p, ptr_base + codediff, codelen);
2562 if (!(time_base >= time_from && time_base < time_to))
2563 jitted_method->ignore_jit = 1;
2564 while (*p) p++;
2565 p++;
2566 } else {
2567 MethodDesc *method;
2568 if ((thread_filter && thread_filter != thread->thread_id))
2569 break;
2570 if (!(time_base >= time_from && time_base < time_to))
2571 break;
2572 method = lookup_method (method_base);
2573 if (subtype == TYPE_ENTER) {
2574 add_trace_thread (thread, &method->traces, 1);
2575 push_method (thread, method, time_base);
2576 } else {
2577 pop_method (thread, method, time_base);
2579 if (debug)
2580 fprintf (outfile, "%s method %s\n", subtype == TYPE_ENTER? "enter": subtype == TYPE_EXC_LEAVE? "exleave": "leave", method->name);
2582 break;
2584 case TYPE_HEAP: {
2585 int subtype = *p & 0xf0;
2586 if (subtype == TYPE_HEAP_OBJECT) {
2587 HeapObjectDesc *ho = NULL;
2588 int i;
2589 intptr_t objdiff;
2590 if (ctx->data_version > 12) {
2591 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2592 LOG_TIME (time_base, tdiff);
2593 time_base += tdiff;
2594 objdiff = decode_sleb128 (p, &p);
2595 } else
2596 objdiff = decode_sleb128 (p + 1, &p);
2597 intptr_t ptrdiff = decode_sleb128 (p, &p);
2598 uint64_t size = decode_uleb128 (p, &p);
2599 if (ctx->data_version >= 16)
2600 p++; // generation
2601 uintptr_t num = decode_uleb128 (p, &p);
2602 uintptr_t ref_offset = 0;
2603 uintptr_t last_obj_offset = 0;
2604 ClassDesc *cd;
2605 if (ctx->data_version > 14) {
2606 VTableDesc *vt = lookup_vtable (ptr_base + ptrdiff);
2607 cd = vt->klass;
2608 } else
2609 cd = lookup_class (ptr_base + ptrdiff);
2610 if (size) {
2611 HeapClassDesc *hcd = add_heap_shot_class (thread->current_heap_shot, cd, size);
2612 if (collect_traces) {
2613 ho = alloc_heap_obj (OBJ_ADDR (objdiff), hcd, num);
2614 add_heap_shot_obj (thread->current_heap_shot, ho);
2615 ref_offset = 0;
2617 } else {
2618 if (collect_traces)
2619 ho = heap_shot_obj_add_refs (thread->current_heap_shot, OBJ_ADDR (objdiff), num, &ref_offset);
2621 for (i = 0; i < num; ++i) {
2622 /* FIXME: use object distance to measure how good
2623 * the GC is at keeping related objects close
2625 uintptr_t offset = ctx->data_version > 1? last_obj_offset + decode_uleb128 (p, &p): -1;
2626 intptr_t obj1diff = decode_sleb128 (p, &p);
2627 last_obj_offset = offset;
2628 if (collect_traces)
2629 ho->refs [ref_offset + i] = OBJ_ADDR (obj1diff);
2630 if (num_tracked_objects)
2631 track_obj_reference (OBJ_ADDR (obj1diff), OBJ_ADDR (objdiff), cd);
2633 if (debug && size)
2634 fprintf (outfile, "traced object %p, size %llu (%s), refs: %zd\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) size, cd->name, num);
2635 } else if (subtype == TYPE_HEAP_ROOT) {
2636 uintptr_t num;
2637 if (ctx->data_version > 14) {
2638 int i;
2639 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2640 LOG_TIME (time_base, tdiff);
2641 time_base += tdiff;
2642 num = decode_uleb128 (p, &p);
2643 for (i = 0; i < num; ++i) {
2644 intptr_t ptrdiff = decode_sleb128 (p, &p);
2645 intptr_t objdiff = decode_sleb128 (p, &p);
2647 if (debug)
2648 fprintf (outfile, "root object %p at address %p\n", (void*)OBJ_ADDR (objdiff), (void *) (ptr_base + ptrdiff));
2649 if (collect_traces)
2650 thread_add_root (thread, OBJ_ADDR (objdiff), MONO_PROFILER_GC_ROOT_MISC, 0);
2652 } else {
2653 if (ctx->data_version > 12) {
2654 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2655 LOG_TIME (time_base, tdiff);
2656 time_base += tdiff;
2657 num = decode_uleb128 (p, &p);
2658 } else
2659 num = decode_uleb128 (p + 1, &p);
2660 uintptr_t gc_num G_GNUC_UNUSED = decode_uleb128 (p, &p);
2661 int i;
2662 for (i = 0; i < num; ++i) {
2663 intptr_t objdiff = decode_sleb128 (p, &p);
2664 int root_type;
2665 if (ctx->data_version == 13)
2666 root_type = *p++;
2667 else
2668 root_type = decode_uleb128 (p, &p);
2669 /* we just discard the extra info for now */
2670 uintptr_t extra_info = decode_uleb128 (p, &p);
2671 if (debug)
2672 fprintf (outfile, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff), get_root_name (root_type));
2673 if (collect_traces)
2674 thread_add_root (thread, OBJ_ADDR (objdiff), root_type, extra_info);
2677 } else if (subtype == TYPE_HEAP_ROOT_REGISTER) {
2678 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2679 LOG_TIME (time_base, tdiff);
2680 time_base += tdiff;
2682 int64_t ptrdiff = decode_sleb128 (p, &p);
2683 uint64_t size = decode_uleb128 (p, &p);
2684 int type = *p++;
2685 int64_t keydiff = decode_sleb128 (p, &p);
2686 char *desc = (char*) p;
2687 while (*p++);
2689 if (debug)
2690 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);
2691 } else if (subtype == TYPE_HEAP_ROOT_UNREGISTER) {
2692 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2693 LOG_TIME (time_base, tdiff);
2694 time_base += tdiff;
2695 int64_t ptrdiff = decode_sleb128 (p, &p);
2697 if (debug)
2698 fprintf (outfile, "root unregister address %p\n", (void *) (ptr_base + ptrdiff));
2699 } else if (subtype == TYPE_HEAP_END) {
2700 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2701 LOG_TIME (time_base, tdiff);
2702 time_base += tdiff;
2703 if (debug)
2704 fprintf (outfile, "heap shot end\n");
2705 if (collect_traces) {
2706 HeapShot *hs = thread->current_heap_shot;
2707 if (hs && thread->num_roots) {
2708 /* transfer the root ownershipt to the heapshot */
2709 hs->num_roots = thread->num_roots;
2710 hs->roots = thread->roots;
2711 hs->roots_extra = thread->roots_extra;
2712 hs->roots_types = thread->roots_types;
2713 } else {
2714 g_free (thread->roots);
2715 g_free (thread->roots_extra);
2716 g_free (thread->roots_types);
2718 thread->num_roots = 0;
2719 thread->size_roots = 0;
2720 thread->roots = NULL;
2721 thread->roots_extra = NULL;
2722 thread->roots_types = NULL;
2723 heap_shot_resolve_reverse_refs (hs);
2724 heap_shot_mark_objects (hs);
2725 heap_shot_free_objects (hs);
2727 thread->current_heap_shot = NULL;
2728 } else if (subtype == TYPE_HEAP_START) {
2729 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2730 LOG_TIME (time_base, tdiff);
2731 time_base += tdiff;
2732 if (debug)
2733 fprintf (outfile, "heap shot start\n");
2734 thread->current_heap_shot = new_heap_shot (time_base);
2736 break;
2738 case TYPE_MONITOR: {
2739 int has_bt = *p & TYPE_MONITOR_BT;
2740 int event;
2741 if (ctx->data_version < 13)
2742 event = (*p >> 4) & 0x3;
2743 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2744 if (ctx->data_version > 13)
2745 event = *p++;
2746 intptr_t objdiff = decode_sleb128 (p, &p);
2747 MethodDesc* sframes [8];
2748 MethodDesc** frames = sframes;
2749 int record;
2750 int num_bt = 0;
2751 LOG_TIME (time_base, tdiff);
2752 time_base += tdiff;
2753 record = (!thread_filter || thread_filter == thread->thread_id);
2754 if (!(time_base >= time_from && time_base < time_to))
2755 record = 0;
2756 MonitorDesc *mdesc = lookup_monitor (OBJ_ADDR (objdiff));
2757 if (event == MONO_PROFILER_MONITOR_CONTENTION) {
2758 if (record) {
2759 monitor_contention++;
2760 mdesc->contentions++;
2761 thread->monitor = mdesc;
2762 thread->contention_start = time_base;
2764 } else if (event == MONO_PROFILER_MONITOR_FAIL) {
2765 if (record) {
2766 monitor_failed++;
2767 if (thread->monitor && thread->contention_start) {
2768 uint64_t wait_time = time_base - thread->contention_start;
2769 if (wait_time > thread->monitor->max_wait_time)
2770 thread->monitor->max_wait_time = wait_time;
2771 thread->monitor->wait_time += wait_time;
2772 thread->monitor = NULL;
2773 thread->contention_start = 0;
2776 } else if (event == MONO_PROFILER_MONITOR_DONE) {
2777 if (record) {
2778 monitor_acquired++;
2779 if (thread->monitor && thread->contention_start) {
2780 uint64_t wait_time = time_base - thread->contention_start;
2781 if (wait_time > thread->monitor->max_wait_time)
2782 thread->monitor->max_wait_time = wait_time;
2783 thread->monitor->wait_time += wait_time;
2784 thread->monitor = NULL;
2785 thread->contention_start = 0;
2789 if (has_bt) {
2790 num_bt = 8;
2791 frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2792 if (!frames) {
2793 fprintf (outfile, "Cannot load backtrace\n");
2794 return 0;
2796 if (record && event == MONO_PROFILER_MONITOR_CONTENTION)
2797 add_trace_methods (frames, num_bt, &mdesc->traces, 1);
2798 } else {
2799 if (record)
2800 add_trace_thread (thread, &mdesc->traces, 1);
2802 if (debug)
2803 fprintf (outfile, "monitor %s for object %p\n", monitor_ev_name (event), (void*)OBJ_ADDR (objdiff));
2804 if (frames != sframes)
2805 g_free (frames);
2806 break;
2808 case TYPE_EXCEPTION: {
2809 int subtype = *p & 0x70;
2810 int has_bt = *p & TYPE_THROW_BT;
2811 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2812 MethodDesc* sframes [8];
2813 MethodDesc** frames = sframes;
2814 int record;
2815 LOG_TIME (time_base, tdiff);
2816 time_base += tdiff;
2817 record = (!thread_filter || thread_filter == thread->thread_id);
2818 if (!(time_base >= time_from && time_base < time_to))
2819 record = 0;
2820 if (subtype == TYPE_CLAUSE) {
2821 int clause_type;
2822 if (ctx->data_version > 12)
2823 clause_type = *p++;
2824 else
2825 clause_type = decode_uleb128 (p, &p);
2826 int clause_num = decode_uleb128 (p, &p);
2827 int64_t ptrdiff = decode_sleb128 (p, &p);
2828 method_base += ptrdiff;
2829 if (ctx->data_version > 13)
2830 decode_uleb128 (p, &p); // exception object
2831 if (record)
2832 clause_summary [clause_type]++;
2833 if (debug)
2834 fprintf (outfile, "clause %s (%d) in method %s\n", clause_name (clause_type), clause_num, lookup_method (method_base)->name);
2835 } else {
2836 intptr_t objdiff = decode_sleb128 (p, &p);
2837 if (record)
2838 throw_count++;
2839 if (has_bt) {
2840 has_bt = 8;
2841 frames = decode_bt (ctx, sframes, &has_bt, p, &p, ptr_base, &method_base);
2842 if (!frames) {
2843 fprintf (outfile, "Cannot load backtrace\n");
2844 return 0;
2846 if (record)
2847 add_trace_methods (frames, has_bt, &exc_traces, 1);
2848 } else {
2849 if (record)
2850 add_trace_thread (thread, &exc_traces, 1);
2852 if (frames != sframes)
2853 g_free (frames);
2854 if (debug)
2855 fprintf (outfile, "throw %p\n", (void*)OBJ_ADDR (objdiff));
2857 break;
2859 case TYPE_RUNTIME: {
2860 int subtype = *p & 0xf0;
2861 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2862 LOG_TIME (time_base, tdiff);
2863 time_base += tdiff;
2864 if (subtype == TYPE_JITHELPER) {
2865 int type;
2866 if (ctx->data_version > 12)
2867 type = *p++;
2868 else
2869 type = decode_uleb128 (p, &p);
2870 if (ctx->data_version < 14)
2871 --type;
2872 intptr_t codediff = decode_sleb128 (p, &p);
2873 int codelen = decode_uleb128 (p, &p);
2874 const char *name;
2875 if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
2876 name = (const char *)p;
2877 while (*p) p++;
2878 p++;
2879 } else {
2880 name = code_buffer_desc (type);
2882 num_jit_helpers++;
2883 jit_helpers_code_size += codelen;
2884 if (debug)
2885 fprintf (outfile, "jit helper %s, size: %d, code: %p\n", name, codelen, (void*)(ptr_base + codediff));
2887 break;
2889 case TYPE_SAMPLE: {
2890 int subtype = *p & 0xf0;
2891 if (subtype == TYPE_SAMPLE_HIT) {
2892 int i;
2893 int sample_type;
2894 uint64_t tstamp;
2895 if (ctx->data_version > 12) {
2896 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2897 LOG_TIME (time_base, tdiff);
2898 time_base += tdiff;
2899 if (ctx->data_version < 14)
2900 sample_type = *p++;
2901 else
2902 sample_type = SAMPLE_CYCLES;
2903 tstamp = time_base;
2904 } else {
2905 sample_type = decode_uleb128 (p + 1, &p);
2906 tstamp = decode_uleb128 (p, &p);
2908 void *tid = (void *) thread_id;
2909 if (ctx->data_version > 10)
2910 tid = (void *) (ptr_base + decode_sleb128 (p, &p));
2911 int count = decode_uleb128 (p, &p);
2912 for (i = 0; i < count; ++i) {
2913 uintptr_t ip = ptr_base + decode_sleb128 (p, &p);
2914 if ((tstamp >= time_from && tstamp < time_to))
2915 add_stat_sample (sample_type, ip);
2916 if (debug)
2917 fprintf (outfile, "sample hit, type: %d at %p for thread %p\n", sample_type, (void*)ip, tid);
2919 if (ctx->data_version > 5) {
2920 count = decode_uleb128 (p, &p);
2921 for (i = 0; i < count; ++i) {
2922 MethodDesc *method;
2923 int64_t ptrdiff = decode_sleb128 (p, &p);
2924 method_base += ptrdiff;
2925 method = lookup_method (method_base);
2926 if (debug)
2927 fprintf (outfile, "sample hit bt %d: %s\n", i, method->name);
2928 if (ctx->data_version < 13) {
2929 decode_sleb128 (p, &p); /* il offset */
2930 decode_sleb128 (p, &p); /* native offset */
2934 } else if (subtype == TYPE_SAMPLE_USYM) {
2935 /* un unmanaged symbol description */
2936 uintptr_t addr;
2937 if (ctx->data_version > 12) {
2938 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2939 LOG_TIME (time_base, tdiff);
2940 time_base += tdiff;
2941 addr = ptr_base + decode_sleb128 (p, &p);
2942 } else
2943 addr = ptr_base + decode_sleb128 (p + 1, &p);
2944 uintptr_t size = decode_uleb128 (p, &p);
2945 char *name;
2946 name = pstrdup ((char*)p);
2947 add_unmanaged_symbol (addr, name, size);
2948 if (debug)
2949 fprintf (outfile, "unmanaged symbol %s at %p\n", name, (void*)addr);
2950 while (*p) p++;
2951 p++;
2952 } else if (subtype == TYPE_SAMPLE_UBIN) {
2953 /* un unmanaged binary loaded in memory */
2954 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2955 uintptr_t addr = decode_sleb128 (p, &p);
2956 if (ctx->data_version > 13)
2957 addr += ptr_base;
2958 uint64_t offset G_GNUC_UNUSED = decode_uleb128 (p, &p);
2959 uintptr_t size = decode_uleb128 (p, &p);
2960 char *name;
2961 LOG_TIME (time_base, tdiff);
2962 time_base += tdiff;
2963 name = pstrdup ((char*)p);
2964 add_unmanaged_binary (addr, name, size);
2965 if (debug)
2966 fprintf (outfile, "unmanaged binary %s at %p\n", name, (void*)addr);
2967 while (*p) p++;
2968 p++;
2969 } else if (subtype == TYPE_SAMPLE_COUNTERS_DESC) {
2970 uint64_t i, len;
2971 if (ctx->data_version > 12) {
2972 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2973 LOG_TIME (time_base, tdiff);
2974 time_base += tdiff;
2975 len = decode_uleb128 (p, &p);
2976 } else
2977 len = decode_uleb128 (p + 1, &p);
2978 for (i = 0; i < len; i++) {
2979 uint64_t type, unit, variance, index;
2980 uint64_t section = decode_uleb128 (p, &p);
2981 char *section_str, *name;
2982 if (section != MONO_COUNTER_PERFCOUNTERS) {
2983 section_str = (char*) section_name (section);
2984 } else {
2985 section_str = pstrdup ((char*)p);
2986 while (*p++);
2988 name = pstrdup ((char*)p);
2989 while (*p++);
2990 if (ctx->data_version > 12 && ctx->data_version < 15) {
2991 type = *p++;
2992 unit = *p++;
2993 variance = *p++;
2994 } else {
2995 type = decode_uleb128 (p, &p);
2996 unit = decode_uleb128 (p, &p);
2997 variance = decode_uleb128 (p, &p);
2999 index = decode_uleb128 (p, &p);
3000 add_counter (section_str, name, (int)type, (int)unit, (int)variance, (int)index);
3002 } else if (subtype == TYPE_SAMPLE_COUNTERS) {
3003 int i;
3004 CounterValue *value, *previous = NULL;
3005 CounterList *list;
3006 uint64_t timestamp; // milliseconds since startup
3007 if (ctx->data_version > 12) {
3008 uint64_t tdiff = decode_uleb128 (p + 1, &p);
3009 LOG_TIME (time_base, tdiff);
3010 time_base += tdiff;
3011 timestamp = (time_base - startup_time) / 1000 / 1000;
3012 } else
3013 timestamp = decode_uleb128 (p + 1, &p);
3014 uint64_t time_between = timestamp / 1000 * 1000 * 1000 * 1000 + startup_time;
3015 while (1) {
3016 uint64_t type, index = decode_uleb128 (p, &p);
3017 if (index == 0)
3018 break;
3020 for (list = counters; list; list = list->next) {
3021 if (list->counter->index == (int)index) {
3022 previous = list->counter->values_last;
3023 break;
3027 if (ctx->data_version > 12 && ctx->data_version < 15)
3028 type = *p++;
3029 else
3030 type = decode_uleb128 (p, &p);
3032 value = (CounterValue *) g_calloc (1, sizeof (CounterValue));
3033 value->timestamp = timestamp;
3035 switch (type) {
3036 case MONO_COUNTER_INT:
3037 #if SIZEOF_VOID_P == 4
3038 case MONO_COUNTER_WORD:
3039 #endif
3040 value->buffer = (unsigned char *)g_malloc (sizeof (int32_t));
3041 *(int32_t*)value->buffer = (int32_t)decode_sleb128 (p, &p) + (previous ? (*(int32_t*)previous->buffer) : 0);
3042 break;
3043 case MONO_COUNTER_UINT:
3044 value->buffer = (unsigned char *) g_malloc (sizeof (uint32_t));
3045 *(uint32_t*)value->buffer = (uint32_t)decode_uleb128 (p, &p) + (previous ? (*(uint32_t*)previous->buffer) : 0);
3046 break;
3047 case MONO_COUNTER_LONG:
3048 #if SIZEOF_VOID_P == 8
3049 case MONO_COUNTER_WORD:
3050 #endif
3051 case MONO_COUNTER_TIME_INTERVAL:
3052 value->buffer = (unsigned char *) g_malloc (sizeof (int64_t));
3053 *(int64_t*)value->buffer = (int64_t)decode_sleb128 (p, &p) + (previous ? (*(int64_t*)previous->buffer) : 0);
3054 break;
3055 case MONO_COUNTER_ULONG:
3056 value->buffer = (unsigned char *) g_malloc (sizeof (uint64_t));
3057 *(uint64_t*)value->buffer = (uint64_t)decode_uleb128 (p, &p) + (previous ? (*(uint64_t*)previous->buffer) : 0);
3058 break;
3059 case MONO_COUNTER_DOUBLE:
3060 value->buffer = (unsigned char *) g_malloc (sizeof (double));
3061 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
3062 for (i = 0; i < sizeof (double); i++)
3063 #else
3064 for (i = sizeof (double) - 1; i >= 0; i--)
3065 #endif
3066 value->buffer[i] = *p++;
3067 break;
3068 case MONO_COUNTER_STRING:
3069 if (*p++ == 0) {
3070 value->buffer = NULL;
3071 } else {
3072 value->buffer = (unsigned char*) pstrdup ((char*)p);
3073 while (*p++);
3075 break;
3077 if (time_between >= time_from && time_between <= time_to)
3078 add_counter_value (index, value);
3080 } else {
3081 return 0;
3083 break;
3085 case TYPE_COVERAGE:{
3086 int subtype = *p & 0xf0;
3087 switch (subtype) {
3088 case TYPE_COVERAGE_METHOD: {
3089 p++;
3091 if (ctx->data_version > 12) {
3092 uint64_t tdiff = decode_uleb128 (p, &p);
3093 LOG_TIME (time_base, tdiff);
3094 time_base += tdiff;
3097 while (*p) p++;
3098 p++;
3099 while (*p) p++;
3100 p++;
3101 while (*p) p++;
3102 p++;
3103 while (*p) p++;
3104 p++;
3105 while (*p) p++;
3106 p++;
3108 decode_uleb128 (p, &p);
3109 decode_uleb128 (p, &p);
3110 decode_uleb128 (p, &p);
3112 break;
3114 case TYPE_COVERAGE_STATEMENT: {
3115 p++;
3117 if (ctx->data_version > 12) {
3118 uint64_t tdiff = decode_uleb128 (p, &p);
3119 LOG_TIME (time_base, tdiff);
3120 time_base += tdiff;
3123 decode_uleb128 (p, &p);
3124 decode_uleb128 (p, &p);
3125 decode_uleb128 (p, &p);
3126 decode_uleb128 (p, &p);
3127 decode_uleb128 (p, &p);
3129 break;
3131 case TYPE_COVERAGE_ASSEMBLY: {
3132 p++;
3134 if (ctx->data_version > 12) {
3135 uint64_t tdiff = decode_uleb128 (p, &p);
3136 LOG_TIME (time_base, tdiff);
3137 time_base += tdiff;
3140 while (*p) p++;
3141 p++;
3142 while (*p) p++;
3143 p++;
3144 while (*p) p++;
3145 p++;
3147 decode_uleb128 (p, &p);
3148 decode_uleb128 (p, &p);
3149 decode_uleb128 (p, &p);
3151 break;
3153 case TYPE_COVERAGE_CLASS: {
3154 p++;
3156 if (ctx->data_version > 12) {
3157 uint64_t tdiff = decode_uleb128 (p, &p);
3158 LOG_TIME (time_base, tdiff);
3159 time_base += tdiff;
3162 while (*p) p++;
3163 p++;
3164 while (*p) p++;
3165 p++;
3167 decode_uleb128 (p, &p);
3168 decode_uleb128 (p, &p);
3169 decode_uleb128 (p, &p);
3171 break;
3174 default:
3175 break;
3177 break;
3179 case TYPE_META: {
3180 int subtype = *p & 0xf0;
3181 uint64_t tdiff = decode_uleb128 (p + 1, &p);
3182 LOG_TIME (time_base, tdiff);
3183 time_base += tdiff;
3184 if (subtype == TYPE_SYNC_POINT) {
3185 int type = *p++;
3186 if (debug)
3187 fprintf (outfile, "sync point %i (%s)\n", type, sync_point_name (type));
3188 } else if (subtype == TYPE_AOT_ID) {
3189 if (debug)
3190 fprintf (outfile, "aot id %s\n", p);
3191 while (*p) p++; // aot id
3192 p++;
3194 break;
3196 default:
3197 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);
3198 exit (1);
3200 record_event_stats (event, p - start);
3202 thread->last_time = time_base;
3203 for (i = 0; i < thread->stack_id; ++i)
3204 thread->stack [i]->recurse_count = 0;
3205 return 1;
3208 static int
3209 read_header_string (ProfContext *ctx, char **field)
3211 if (!load_data (ctx, 4))
3212 return 0;
3214 if (!load_data (ctx, read_int32 (ctx->buf)))
3215 return 0;
3217 *field = pstrdup ((const char *) ctx->buf);
3219 return 1;
3222 static ProfContext*
3223 load_file (char *name)
3225 unsigned char *p;
3226 ProfContext *ctx = (ProfContext *) g_calloc (sizeof (ProfContext), 1);
3227 if (strcmp (name, "-") == 0)
3228 ctx->file = stdin;
3229 else
3230 ctx->file = fopen (name, "rb");
3231 if (!ctx->file) {
3232 printf ("Cannot open file: %s\n", name);
3233 exit (1);
3235 #if defined (HAVE_SYS_ZLIB)
3236 if (ctx->file != stdin)
3237 ctx->gzfile = gzdopen (fileno (ctx->file), "rb");
3238 #endif
3239 if (!load_data (ctx, 16))
3240 return NULL;
3241 p = ctx->buf;
3242 if (read_int32 (p) != LOG_HEADER_ID)
3243 return NULL;
3244 p += 4;
3245 ctx->version_major = *p++;
3246 ctx->version_minor = *p++;
3247 ctx->data_version = *p++;
3248 if (ctx->data_version > LOG_DATA_VERSION)
3249 return NULL;
3250 /* reading 64 bit files on 32 bit systems not supported yet */
3251 if (*p++ > sizeof (void*))
3252 return NULL;
3253 ctx->startup_time = read_int64 (p);
3254 p += 8;
3255 // nanoseconds startup time
3256 if (ctx->version_major >= 3)
3257 if (!load_data (ctx, 8))
3258 return NULL;
3259 if (!load_data (ctx, 14))
3260 return NULL;
3261 p = ctx->buf;
3262 ctx->timer_overhead = read_int32 (p);
3263 p += 4;
3264 if (read_int32 (p)) /* flags must be 0 */
3265 return NULL;
3266 p += 4;
3267 ctx->pid = read_int32 (p);
3268 p += 4;
3269 ctx->port = read_int16 (p);
3270 p += 2;
3271 if (ctx->version_major >= 1) {
3272 if (!read_header_string (ctx, &ctx->args))
3273 return NULL;
3274 if (!read_header_string (ctx, &ctx->arch))
3275 return NULL;
3276 if (!read_header_string (ctx, &ctx->os))
3277 return NULL;
3278 } else {
3279 if (!load_data (ctx, 2)) /* old opsys field, was never used */
3280 return NULL;
3282 return ctx;
3285 enum {
3286 ALLOC_SORT_BYTES,
3287 ALLOC_SORT_COUNT
3289 static int alloc_sort_mode = ALLOC_SORT_BYTES;
3291 static int
3292 compare_class (const void *a, const void *b)
3294 ClassDesc *const *A = (ClassDesc *const *)a;
3295 ClassDesc *const *B = (ClassDesc *const *)b;
3296 uint64_t vala, valb;
3297 if (alloc_sort_mode == ALLOC_SORT_BYTES) {
3298 vala = (*A)->alloc_size;
3299 valb = (*B)->alloc_size;
3300 } else {
3301 vala = (*A)->allocs;
3302 valb = (*B)->allocs;
3304 if (valb == vala)
3305 return 0;
3306 if (valb < vala)
3307 return -1;
3308 return 1;
3311 static void
3312 dump_header (ProfContext *ctx)
3314 time_t st = ctx->startup_time / 1000;
3315 char *t = ctime (&st);
3316 fprintf (outfile, "\nMono log profiler data\n");
3317 fprintf (outfile, "\tProfiler version: %d.%d\n", ctx->version_major, ctx->version_minor);
3318 fprintf (outfile, "\tData version: %d\n", ctx->data_version);
3319 if (ctx->version_major >= 1) {
3320 fprintf (outfile, "\tArguments: %s\n", ctx->args);
3321 fprintf (outfile, "\tArchitecture: %s\n", ctx->arch);
3322 fprintf (outfile, "\tOperating system: %s\n", ctx->os);
3324 fprintf (outfile, "\tMean timer overhead: %d nanoseconds\n", ctx->timer_overhead);
3325 fprintf (outfile, "\tProgram startup: %s", t);
3326 if (ctx->pid)
3327 fprintf (outfile, "\tProgram ID: %d\n", ctx->pid);
3328 if (ctx->port)
3329 fprintf (outfile, "\tServer listening on: %d\n", ctx->port);
3332 static void
3333 dump_traces (TraceDesc *traces, const char *desc)
3335 int j;
3336 if (!show_traces)
3337 return;
3338 if (!traces->count)
3339 return;
3340 sort_context_array (traces);
3341 for (j = 0; j < traces->count; ++j) {
3342 int k;
3343 BackTrace *bt;
3344 bt = traces->traces [j].bt;
3345 if (!bt->count)
3346 continue;
3347 fprintf (outfile, "\t%llu %s from:\n", (unsigned long long) traces->traces [j].count, desc);
3348 for (k = 0; k < bt->count; ++k)
3349 fprintf (outfile, "\t\t%s\n", bt->methods [k]->name);
3353 static void
3354 dump_threads (ProfContext *ctx)
3356 ThreadContext *thread;
3357 fprintf (outfile, "\nThread summary\n");
3358 for (thread = ctx->threads; thread; thread = thread->next) {
3359 if (thread->thread_id) {
3360 fprintf (outfile, "\tThread: %p, name: \"%s\"\n", (void*)thread->thread_id, thread->name? thread->name: "");
3365 static void
3366 dump_domains (ProfContext *ctx)
3368 fprintf (outfile, "\nDomain summary\n");
3370 for (DomainContext *domain = ctx->domains; domain; domain = domain->next)
3371 fprintf (outfile, "\tDomain: %p, friendly name: \"%s\"\n", (void *) domain->domain_id, domain->friendly_name);
3374 static void
3375 dump_remctxs (ProfContext *ctx)
3377 fprintf (outfile, "\nContext summary\n");
3379 for (RemCtxContext *remctx = ctx->remctxs; remctx; remctx = remctx->next)
3380 fprintf (outfile, "\tContext: %p, domain: %p\n", (void *) remctx->remctx_id, (void *) remctx->domain_id);
3383 static void
3384 dump_exceptions (void)
3386 int i;
3387 fprintf (outfile, "\nException summary\n");
3388 fprintf (outfile, "\tThrows: %llu\n", (unsigned long long) throw_count);
3389 dump_traces (&exc_traces, "throws");
3390 for (i = 0; i <= MONO_EXCEPTION_CLAUSE_FAULT; ++i) {
3391 if (!clause_summary [i])
3392 continue;
3393 fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), (unsigned long long) clause_summary [i]);
3397 static int
3398 compare_monitor (const void *a, const void *b)
3400 MonitorDesc *const *A = (MonitorDesc *const *)a;
3401 MonitorDesc *const *B = (MonitorDesc *const *)b;
3402 if ((*B)->wait_time == (*A)->wait_time)
3403 return 0;
3404 if ((*B)->wait_time < (*A)->wait_time)
3405 return -1;
3406 return 1;
3409 static void
3410 dump_monitors (void)
3412 MonitorDesc **monitors;
3413 int i, j;
3414 if (!num_monitors)
3415 return;
3416 monitors = (MonitorDesc **) g_malloc (sizeof (void*) * num_monitors);
3417 for (i = 0, j = 0; i < SMALL_HASH_SIZE; ++i) {
3418 MonitorDesc *mdesc = monitor_hash [i];
3419 while (mdesc) {
3420 monitors [j++] = mdesc;
3421 mdesc = mdesc->next;
3424 mono_qsort (monitors, num_monitors, sizeof (void*), compare_monitor);
3425 fprintf (outfile, "\nMonitor lock summary\n");
3426 for (i = 0; i < num_monitors; ++i) {
3427 MonitorDesc *mdesc = monitors [i];
3428 fprintf (outfile, "\tLock object %p: %d contentions\n", (void*)mdesc->objid, (int)mdesc->contentions);
3429 fprintf (outfile, "\t\t%.6f secs total wait time, %.6f max, %.6f average\n",
3430 mdesc->wait_time/1000000000.0, mdesc->max_wait_time/1000000000.0, mdesc->wait_time/1000000000.0/mdesc->contentions);
3431 dump_traces (&mdesc->traces, "contentions");
3433 fprintf (outfile, "\tLock contentions: %llu\n", (unsigned long long) monitor_contention);
3434 fprintf (outfile, "\tLock acquired: %llu\n", (unsigned long long) monitor_acquired);
3435 fprintf (outfile, "\tLock failures: %llu\n", (unsigned long long) monitor_failed);
3438 static void
3439 dump_gcs (void)
3441 int i;
3442 fprintf (outfile, "\nGC summary\n");
3443 fprintf (outfile, "\tGC resizes: %d\n", gc_resizes);
3444 fprintf (outfile, "\tMax heap size: %llu\n", (unsigned long long) max_heap_size);
3445 fprintf (outfile, "\tObject moves: %llu\n", (unsigned long long) gc_object_moves);
3446 for (i = 0; i < 3; ++i) {
3447 if (!gc_info [i].count)
3448 continue;
3449 fprintf (outfile, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
3450 i, gc_info [i].count,
3451 (unsigned long long) (gc_info [i].max_time / 1000),
3452 (unsigned long long) (gc_info [i].total_time / 1000),
3453 (unsigned long long) (gc_info [i].total_time / gc_info [i].count / 1000));
3455 for (i = 0; i < 3; ++i) {
3456 if (!handle_info [i].max_live)
3457 continue;
3458 fprintf (outfile, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
3459 get_handle_name (i),
3460 (unsigned long long) (handle_info [i].created),
3461 (unsigned long long) (handle_info [i].destroyed),
3462 (unsigned long long) (handle_info [i].max_live));
3463 dump_traces (&handle_info [i].traces, "created");
3464 dump_traces (&handle_info [i].destroy_traces, "destroyed");
3468 static void
3469 dump_jit (void)
3471 int i;
3472 int code_size = 0;
3473 int compiled_methods = 0;
3474 MethodDesc* m;
3475 fprintf (outfile, "\nJIT summary\n");
3476 for (i = 0; i < HASH_SIZE; ++i) {
3477 m = method_hash [i];
3478 for (m = method_hash [i]; m; m = m->next) {
3479 if (!m->code || m->ignore_jit)
3480 continue;
3481 compiled_methods++;
3482 code_size += m->len;
3485 fprintf (outfile, "\tCompiled methods: %d\n", compiled_methods);
3486 fprintf (outfile, "\tGenerated code size: %d\n", code_size);
3487 fprintf (outfile, "\tJIT helpers: %d\n", num_jit_helpers);
3488 fprintf (outfile, "\tJIT helpers code size: %d\n", jit_helpers_code_size);
3491 static void
3492 dump_allocations (void)
3494 int i, c;
3495 intptr_t allocs = 0;
3496 uint64_t size = 0;
3497 int header_done = 0;
3498 ClassDesc **classes = (ClassDesc **) g_malloc (num_classes * sizeof (void*));
3499 ClassDesc *cd;
3500 c = 0;
3501 for (i = 0; i < HASH_SIZE; ++i) {
3502 cd = class_hash [i];
3503 while (cd) {
3504 classes [c++] = cd;
3505 cd = cd->next;
3508 mono_qsort (classes, num_classes, sizeof (void*), compare_class);
3509 for (i = 0; i < num_classes; ++i) {
3510 cd = classes [i];
3511 if (!cd->allocs)
3512 continue;
3513 allocs += cd->allocs;
3514 size += cd->alloc_size;
3515 if (!header_done++) {
3516 fprintf (outfile, "\nAllocation summary\n");
3517 fprintf (outfile, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
3519 fprintf (outfile, "%10llu %10zd %8llu %s\n",
3520 (unsigned long long) (cd->alloc_size),
3521 cd->allocs,
3522 (unsigned long long) (cd->alloc_size / cd->allocs),
3523 cd->name);
3524 dump_traces (&cd->traces, "bytes");
3526 if (allocs)
3527 fprintf (outfile, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size, allocs);
3530 enum {
3531 METHOD_SORT_TOTAL,
3532 METHOD_SORT_SELF,
3533 METHOD_SORT_CALLS
3536 static int method_sort_mode = METHOD_SORT_TOTAL;
3538 static int
3539 compare_method (const void *a, const void *b)
3541 MethodDesc *const *A = (MethodDesc *const *)a;
3542 MethodDesc *const *B = (MethodDesc *const *)b;
3543 uint64_t vala, valb;
3544 if (method_sort_mode == METHOD_SORT_SELF) {
3545 vala = (*A)->self_time;
3546 valb = (*B)->self_time;
3547 } else if (method_sort_mode == METHOD_SORT_CALLS) {
3548 vala = (*A)->calls;
3549 valb = (*B)->calls;
3550 } else {
3551 vala = (*A)->total_time;
3552 valb = (*B)->total_time;
3554 if (vala == valb)
3555 return 0;
3556 if (valb < vala)
3557 return -1;
3558 return 1;
3561 static void
3562 dump_metadata (void)
3564 fprintf (outfile, "\nMetadata summary\n");
3565 fprintf (outfile, "\tLoaded images: %d\n", num_images);
3566 if (verbose) {
3567 ImageDesc *image;
3568 int i;
3569 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
3570 image = image_hash [i];
3571 while (image) {
3572 fprintf (outfile, "\t\t%s\n", image->filename);
3573 image = image->next;
3577 fprintf (outfile, "\tLoaded assemblies: %d\n", num_assemblies);
3578 if (verbose) {
3579 AssemblyDesc *assembly;
3580 int i;
3581 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
3582 assembly = assembly_hash [i];
3583 while (assembly) {
3584 fprintf (outfile, "\t\t%s\n", assembly->asmname);
3585 assembly = assembly->next;
3591 static void
3592 dump_methods (void)
3594 int i, c;
3595 uint64_t calls = 0;
3596 int header_done = 0;
3597 MethodDesc **methods = (MethodDesc **) g_malloc (num_methods * sizeof (void*));
3598 MethodDesc *cd;
3599 c = 0;
3600 for (i = 0; i < HASH_SIZE; ++i) {
3601 cd = method_hash [i];
3602 while (cd) {
3603 cd->total_time = cd->self_time + cd->callee_time;
3604 methods [c++] = cd;
3605 cd = cd->next;
3608 mono_qsort (methods, num_methods, sizeof (void*), compare_method);
3609 for (i = 0; i < num_methods; ++i) {
3610 uint64_t msecs;
3611 uint64_t smsecs;
3612 cd = methods [i];
3613 if (!cd->calls)
3614 continue;
3615 calls += cd->calls;
3616 msecs = cd->total_time / 1000000;
3617 smsecs = (cd->total_time - cd->callee_time) / 1000000;
3618 if (!msecs && !verbose)
3619 continue;
3620 if (!header_done++) {
3621 fprintf (outfile, "\nMethod call summary\n");
3622 fprintf (outfile, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
3624 fprintf (outfile, "%8llu %8llu %10llu %s\n",
3625 (unsigned long long) (msecs),
3626 (unsigned long long) (smsecs),
3627 (unsigned long long) (cd->calls),
3628 cd->name);
3629 dump_traces (&cd->traces, "calls");
3631 if (calls)
3632 fprintf (outfile, "Total calls: %llu\n", (unsigned long long) calls);
3635 static int
3636 compare_heap_class (const void *a, const void *b)
3638 HeapClassDesc *const *A = (HeapClassDesc *const *)a;
3639 HeapClassDesc *const *B = (HeapClassDesc *const *)b;
3640 uint64_t vala, valb;
3641 if (alloc_sort_mode == ALLOC_SORT_BYTES) {
3642 vala = (*A)->total_size;
3643 valb = (*B)->total_size;
3644 } else {
3645 vala = (*A)->count;
3646 valb = (*B)->count;
3648 if (valb == vala)
3649 return 0;
3650 if (valb < vala)
3651 return -1;
3652 return 1;
3655 static int
3656 compare_rev_class (const void *a, const void *b)
3658 const HeapClassRevRef *A = (const HeapClassRevRef *)a;
3659 const HeapClassRevRef *B = (const HeapClassRevRef *)b;
3660 if (B->count == A->count)
3661 return 0;
3662 if (B->count < A->count)
3663 return -1;
3664 return 1;
3667 static void
3668 dump_rev_claases (HeapClassRevRef *revs, int count)
3670 int j;
3671 if (!show_traces)
3672 return;
3673 if (!count)
3674 return;
3675 for (j = 0; j < count; ++j) {
3676 HeapClassDesc *cd = revs [j].klass;
3677 fprintf (outfile, "\t\t%llu references from: %s\n",
3678 (unsigned long long) (revs [j].count),
3679 cd->klass->name);
3683 static void
3684 heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
3686 uint64_t size = 0;
3687 uint64_t count = 0;
3688 int ccount = 0;
3689 int i;
3690 HeapClassDesc *cd;
3691 HeapClassDesc **sorted;
3692 sorted = (HeapClassDesc **) g_malloc (sizeof (void*) * hs->class_count);
3693 for (i = 0; i < hs->hash_size; ++i) {
3694 cd = hs->class_hash [i];
3695 if (!cd)
3696 continue;
3697 count += cd->count;
3698 size += cd->total_size;
3699 sorted [ccount++] = cd;
3701 hs->sorted = sorted;
3702 mono_qsort (sorted, ccount, sizeof (void*), compare_heap_class);
3703 fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %zd\n",
3704 hs_num,
3705 (hs->timestamp - startup_time)/1000000000.0,
3706 (unsigned long long) (size),
3707 (unsigned long long) (count),
3708 ccount, hs->num_roots);
3709 if (!verbose && ccount > 30)
3710 ccount = 30;
3711 fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
3712 for (i = 0; i < ccount; ++i) {
3713 HeapClassRevRef *rev_sorted;
3714 int j, k;
3715 HeapClassDesc *ocd = NULL;
3716 cd = sorted [i];
3717 if (last_hs)
3718 ocd = heap_class_lookup (last_hs, cd->klass);
3719 fprintf (outfile, "\t%10llu %10llu %8llu %s",
3720 (unsigned long long) (cd->total_size),
3721 (unsigned long long) (cd->count),
3722 (unsigned long long) (cd->total_size / cd->count),
3723 cd->klass->name);
3724 if (ocd) {
3725 int64_t bdiff = cd->total_size - ocd->total_size;
3726 int64_t cdiff = cd->count - ocd->count;
3727 fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", (long long) bdiff, (long long) cdiff);
3728 } else {
3729 fprintf (outfile, "\n");
3731 if (!collect_traces)
3732 continue;
3733 rev_sorted = (HeapClassRevRef *) g_malloc (cd->rev_count * sizeof (HeapClassRevRef));
3734 k = 0;
3735 for (j = 0; j < cd->rev_hash_size; ++j) {
3736 if (cd->rev_hash [j].klass)
3737 rev_sorted [k++] = cd->rev_hash [j];
3739 assert (cd->rev_count == k);
3740 mono_qsort (rev_sorted, cd->rev_count, sizeof (HeapClassRevRef), compare_rev_class);
3741 if (cd->root_references)
3742 fprintf (outfile, "\t\t%zd root references (%zd pinning)\n", cd->root_references, cd->pinned_references);
3743 dump_rev_claases (rev_sorted, cd->rev_count);
3744 g_free (rev_sorted);
3746 g_free (sorted);
3749 static int
3750 compare_heap_shots (const void *a, const void *b)
3752 HeapShot *const *A = (HeapShot *const *)a;
3753 HeapShot *const *B = (HeapShot *const *)b;
3754 if ((*B)->timestamp == (*A)->timestamp)
3755 return 0;
3756 if ((*B)->timestamp > (*A)->timestamp)
3757 return -1;
3758 return 1;
3761 static void
3762 dump_heap_shots (void)
3764 HeapShot **hs_sorted;
3765 HeapShot *hs;
3766 HeapShot *last_hs = NULL;
3767 int i;
3768 if (!heap_shots)
3769 return;
3770 hs_sorted = (HeapShot **) g_malloc (num_heap_shots * sizeof (void*));
3771 fprintf (outfile, "\nHeap shot summary\n");
3772 i = 0;
3773 for (hs = heap_shots; hs; hs = hs->next)
3774 hs_sorted [i++] = hs;
3775 mono_qsort (hs_sorted, num_heap_shots, sizeof (void*), compare_heap_shots);
3776 for (i = 0; i < num_heap_shots; ++i) {
3777 hs = hs_sorted [i];
3778 heap_shot_summary (hs, i, last_hs);
3779 last_hs = hs;
3783 #define DUMP_EVENT_STAT(EVENT,SUBTYPE) dump_event (#EVENT, #SUBTYPE, EVENT, SUBTYPE);
3785 static void
3786 dump_event (const char *event_name, const char *subtype_name, int event, int subtype)
3788 int idx = event | subtype;
3789 EventStat evt = stats [idx];
3790 if (!evt.count)
3791 return;
3793 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);
3796 static void
3797 dump_stats (void)
3799 fprintf (outfile, "\nMlpd statistics\n");
3800 fprintf (outfile, "\tBuffer count %d\toverhead %d (%d bytes per header)\n", buffer_count, buffer_count * BUFFER_HEADER_SIZE, BUFFER_HEADER_SIZE);
3801 fprintf (outfile, "\nEvent details:\n");
3803 DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_NO_BT);
3804 DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_BT);
3806 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_EVENT);
3807 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_RESIZE);
3808 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_MOVE);
3809 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED);
3810 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED);
3811 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED_BT);
3812 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED_BT);
3813 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_FINALIZE_START);
3814 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_FINALIZE_END);
3815 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_FINALIZE_OBJECT_START);
3816 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_FINALIZE_OBJECT_END);
3818 DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_LOAD);
3819 DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_UNLOAD);
3821 DUMP_EVENT_STAT (TYPE_METHOD, TYPE_LEAVE);
3822 DUMP_EVENT_STAT (TYPE_METHOD, TYPE_ENTER);
3823 DUMP_EVENT_STAT (TYPE_METHOD, TYPE_EXC_LEAVE);
3824 DUMP_EVENT_STAT (TYPE_METHOD, TYPE_JIT);
3826 DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_THROW_NO_BT);
3827 DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_THROW_BT);
3828 DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_CLAUSE);
3830 DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_NO_BT);
3831 DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_BT);
3833 DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_START);
3834 DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_END);
3835 DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_OBJECT);
3836 DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_ROOT);
3838 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_HIT);
3839 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_USYM);
3840 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_UBIN);
3841 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS_DESC);
3842 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS);
3844 DUMP_EVENT_STAT (TYPE_RUNTIME, TYPE_JITHELPER);
3846 DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_ASSEMBLY);
3847 DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_METHOD);
3848 DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_STATEMENT);
3849 DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_CLASS);
3851 DUMP_EVENT_STAT (TYPE_META, TYPE_SYNC_POINT);
3852 DUMP_EVENT_STAT (TYPE_META, TYPE_AOT_ID);
3857 static void
3858 flush_context (ProfContext *ctx)
3860 ThreadContext *thread;
3861 /* FIXME: sometimes there are leftovers: indagate */
3862 for (thread = ctx->threads; thread; thread = thread->next) {
3863 while (thread->stack_id) {
3864 if (debug)
3865 fprintf (outfile, "thread %p has %d items on stack\n", (void*)thread->thread_id, thread->stack_id);
3866 pop_method (thread, thread->stack [thread->stack_id - 1], thread->last_time);
3871 static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,domain,context,heapshot,counters";
3873 static const char*
3874 match_option (const char *p, const char *opt)
3876 int len = strlen (opt);
3877 if (strncmp (p, opt, len) == 0) {
3878 if (p [len] == ',')
3879 len++;
3880 return p + len;
3882 return p;
3885 static int
3886 print_reports (ProfContext *ctx, const char *reps, int parse_only)
3888 const char *opt;
3889 const char *p;
3890 for (p = reps; *p; p = opt) {
3891 if ((opt = match_option (p, "header")) != p) {
3892 if (!parse_only)
3893 dump_header (ctx);
3894 continue;
3896 if ((opt = match_option (p, "thread")) != p) {
3897 if (!parse_only)
3898 dump_threads (ctx);
3899 continue;
3901 if ((opt = match_option (p, "domain")) != p) {
3902 if (!parse_only)
3903 dump_domains (ctx);
3904 continue;
3906 if ((opt = match_option (p, "context")) != p) {
3907 if (!parse_only)
3908 dump_remctxs (ctx);
3909 continue;
3911 if ((opt = match_option (p, "gc")) != p) {
3912 if (!parse_only)
3913 dump_gcs ();
3914 continue;
3916 if ((opt = match_option (p, "jit")) != p) {
3917 if (!parse_only)
3918 dump_jit ();
3919 continue;
3921 if ((opt = match_option (p, "alloc")) != p) {
3922 if (!parse_only)
3923 dump_allocations ();
3924 continue;
3926 if ((opt = match_option (p, "call")) != p) {
3927 if (!parse_only)
3928 dump_methods ();
3929 continue;
3931 if ((opt = match_option (p, "metadata")) != p) {
3932 if (!parse_only)
3933 dump_metadata ();
3934 continue;
3936 if ((opt = match_option (p, "exception")) != p) {
3937 if (!parse_only)
3938 dump_exceptions ();
3939 continue;
3941 if ((opt = match_option (p, "monitor")) != p) {
3942 if (!parse_only)
3943 dump_monitors ();
3944 continue;
3946 if ((opt = match_option (p, "heapshot")) != p) {
3947 if (!parse_only)
3948 dump_heap_shots ();
3949 continue;
3951 if ((opt = match_option (p, "sample")) != p) {
3952 if (!parse_only)
3953 dump_samples ();
3954 continue;
3956 if ((opt = match_option (p, "counters")) != p) {
3957 if (!parse_only)
3958 dump_counters ();
3959 continue;
3961 if ((opt = match_option (p, "coverage")) != p) {
3962 printf ("The log profiler no longer supports code coverage. Please use the dedicated coverage profiler instead. See mono-profilers(1) for more information.\n");
3963 continue;
3965 if ((opt = match_option (p, "stats")) != p) {
3966 if (!parse_only)
3967 dump_stats ();
3968 continue;
3970 return 0;
3972 return 1;
3975 static int
3976 add_find_spec (const char *p)
3978 if (p [0] == 'S' && p [1] == ':') {
3979 char *vale;
3980 find_size = strtoul (p + 2, &vale, 10);
3981 return 1;
3982 } else if (p [0] == 'T' && p [1] == ':') {
3983 find_name = p + 2;
3984 return 1;
3986 return 0;
3989 static void
3990 usage (void)
3992 printf ("Mono log profiler report version %d.%d\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR);
3993 printf ("Usage: mprof-report [OPTIONS] FILENAME\n");
3994 printf ("FILENAME can be '-' to read from standard input.\n");
3995 printf ("Options:\n");
3996 printf ("\t--help display this help\n");
3997 printf ("\t--out=FILE write to FILE instead of stdout\n");
3998 printf ("\t--traces collect and show backtraces\n");
3999 printf ("\t--maxframes=NUM limit backtraces to NUM entries\n");
4000 printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
4001 printf ("\t %s\n", reports);
4002 printf ("\t--method-sort=MODE sort methods according to MODE: total, self, calls\n");
4003 printf ("\t--alloc-sort=MODE sort allocations according to MODE: bytes, count\n");
4004 printf ("\t--counters-sort=MODE sort counters according to MODE: time, category\n");
4005 printf ("\t only accessible in verbose mode\n");
4006 printf ("\t--track=OB1[,OB2...] track what happens to objects OBJ1, O2 etc.\n");
4007 printf ("\t--find=FINDSPEC find and track objects matching FINFSPEC, where FINDSPEC is:\n");
4008 printf ("\t S:minimum_size or T:partial_name\n");
4009 printf ("\t--thread=THREADID consider just the data for thread THREADID\n");
4010 printf ("\t--time=FROM-TO consider data FROM seconds from startup up to TO seconds\n");
4011 printf ("\t--verbose increase verbosity level\n");
4012 printf ("\t--debug display decoding debug info for mprof-report devs\n");
4016 main (int argc, char *argv[])
4018 ProfContext *ctx;
4019 int i;
4020 outfile = stdout;
4021 for (i = 1; i < argc; ++i) {
4022 if (strcmp ("--debug", argv [i]) == 0) {
4023 debug++;
4024 } else if (strcmp ("--help", argv [i]) == 0) {
4025 usage ();
4026 return 0;
4027 } else if (strncmp ("--alloc-sort=", argv [i], 13) == 0) {
4028 const char *val = argv [i] + 13;
4029 if (strcmp (val, "bytes") == 0) {
4030 alloc_sort_mode = ALLOC_SORT_BYTES;
4031 } else if (strcmp (val, "count") == 0) {
4032 alloc_sort_mode = ALLOC_SORT_COUNT;
4033 } else {
4034 usage ();
4035 return 1;
4037 } else if (strncmp ("--method-sort=", argv [i], 14) == 0) {
4038 const char *val = argv [i] + 14;
4039 if (strcmp (val, "total") == 0) {
4040 method_sort_mode = METHOD_SORT_TOTAL;
4041 } else if (strcmp (val, "self") == 0) {
4042 method_sort_mode = METHOD_SORT_SELF;
4043 } else if (strcmp (val, "calls") == 0) {
4044 method_sort_mode = METHOD_SORT_CALLS;
4045 } else {
4046 usage ();
4047 return 1;
4049 } else if (strncmp ("--counters-sort=", argv [i], 16) == 0) {
4050 const char *val = argv [i] + 16;
4051 if (strcmp (val, "time") == 0) {
4052 counters_sort_mode = COUNTERS_SORT_TIME;
4053 } else if (strcmp (val, "category") == 0) {
4054 counters_sort_mode = COUNTERS_SORT_CATEGORY;
4055 } else {
4056 usage ();
4057 return 1;
4059 } else if (strncmp ("--reports=", argv [i], 10) == 0) {
4060 const char *val = argv [i] + 10;
4061 if (!print_reports (NULL, val, 1)) {
4062 usage ();
4063 return 1;
4065 reports = val;
4066 } else if (strncmp ("--out=", argv [i], 6) == 0) {
4067 const char *val = argv [i] + 6;
4068 outfile = fopen (val, "w");
4069 if (!outfile) {
4070 printf ("Cannot open output file: %s\n", val);
4071 return 1;
4073 } else if (strncmp ("--maxframes=", argv [i], 12) == 0) {
4074 const char *val = argv [i] + 12;
4075 char *vale;
4076 trace_max = strtoul (val, &vale, 10);
4077 } else if (strncmp ("--find=", argv [i], 7) == 0) {
4078 const char *val = argv [i] + 7;
4079 if (!add_find_spec (val)) {
4080 usage ();
4081 return 1;
4083 } else if (strncmp ("--track=", argv [i], 8) == 0) {
4084 const char *val = argv [i] + 8;
4085 char *vale;
4086 while (*val) {
4087 uintptr_t tracked_obj;
4088 if (*val == ',') {
4089 val++;
4090 continue;
4092 tracked_obj = strtoul (val, &vale, 0);
4093 found_object (tracked_obj);
4094 val = vale;
4096 } else if (strncmp ("--thread=", argv [i], 9) == 0) {
4097 const char *val = argv [i] + 9;
4098 char *vale;
4099 thread_filter = strtoul (val, &vale, 0);
4100 } else if (strncmp ("--time=", argv [i], 7) == 0) {
4101 char *val = pstrdup (argv [i] + 7);
4102 double from_secs, to_secs;
4103 char *top = strchr (val, '-');
4104 if (!top) {
4105 usage ();
4106 return 1;
4108 *top++ = 0;
4109 from_secs = atof (val);
4110 to_secs = atof (top);
4111 g_free (val);
4112 if (from_secs > to_secs) {
4113 usage ();
4114 return 1;
4116 time_from = from_secs * 1000000000;
4117 time_to = to_secs * 1000000000;
4118 use_time_filter = 1;
4119 } else if (strcmp ("--verbose", argv [i]) == 0) {
4120 verbose++;
4121 } else if (strcmp ("--traces", argv [i]) == 0) {
4122 show_traces = 1;
4123 collect_traces = 1;
4124 } else if (strncmp ("--coverage-out=", argv [i], 15) == 0) {
4125 // For backwards compatibility.
4126 } else {
4127 break;
4130 if (i >= argc) {
4131 usage ();
4132 return 2;
4134 ctx = load_file (argv [i]);
4135 if (!ctx) {
4136 printf ("Not a log profiler data file (or unsupported version).\n");
4137 return 1;
4139 while (decode_buffer (ctx));
4140 flush_context (ctx);
4141 if (num_tracked_objects)
4142 return 0;
4143 print_reports (ctx, reports, 0);
4144 return 0;