[System] Tweak socket test
[mono-project.git] / mono / profiler / mprof-report.c
blob1017d1f8e6d4fb9e09b8f08fc4c7c8a976f35db6
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.
13 * The Coverage XML output schema
14 * <coverage>
15 * <assembly/>
16 * <class/>
17 * <method>
18 * <statement/>
19 * </method>
20 * </coverage>
22 * Elements:
23 * <coverage> - The root element of the documentation. It can contain any number of
24 * <assembly>, <class> or <method> elements.
25 * Attributes:
26 * - version: The version number for the file format - (eg: "0.3")
27 * <assembly> - Contains data about assemblies. Has no child elements
28 * Attributes:
29 * - name: The name of the assembly - (eg: "System.Xml")
30 * - guid: The GUID of the assembly
31 * - filename: The filename of the assembly
32 * - method-count: The number of methods in the assembly
33 * - full: The number of fully covered methods
34 * - partial: The number of partially covered methods
35 * <class> - Contains data about classes. Has no child elements
36 * Attributes:
37 * - name: The name of the class
38 * - method-count: The number of methods in the class
39 * - full: The number of fully covered methods
40 * - partial: The number of partially covered methods
41 * <method> - Contains data about methods. Can contain any number of <statement> elements
42 * Attributes:
43 * - assembly: The name of the parent assembly
44 * - class: The name of the parent class
45 * - name: The name of the method, with all it's parameters
46 * - filename: The name of the source file containing this method
47 * - token
48 * <statement> - Contains data about IL statements. Has no child elements
49 * Attributes:
50 * - offset: The offset of the statement in the IL code after the previous
51 * statement's offset
52 * - counter: 1 if the line was covered, 0 if it was not
53 * - line: The line number in the parent method's file
54 * - column: The column on the line
56 #include <config.h>
57 #include "mono-profiler-log.h"
58 #include <string.h>
59 #include <assert.h>
60 #include <stdio.h>
61 #include <time.h>
62 #if !defined(__APPLE__) && !defined(__FreeBSD__)
63 #include <malloc.h>
64 #endif
65 #include <unistd.h>
66 #include <stdlib.h>
67 #if defined (HAVE_SYS_ZLIB)
68 #include <zlib.h>
69 #endif
70 #include <glib.h>
71 #include <mono/metadata/profiler.h>
72 #include <mono/metadata/object.h>
73 #include <mono/metadata/debug-helpers.h>
74 #include <mono/utils/mono-counters.h>
76 #define HASH_SIZE 9371
77 #define SMALL_HASH_SIZE 31
79 #if defined(__native_client__) || defined(__native_client_codegen__)
80 volatile int __nacl_thread_suspension_needed = 0;
81 void __nacl_suspend_thread_if_needed() {}
82 #endif
84 static int debug = 0;
85 static int collect_traces = 0;
86 static int show_traces = 0;
87 static int trace_max = 6;
88 static int verbose = 0;
89 static uintptr_t *tracked_objects = 0;
90 static int num_tracked_objects = 0;
91 static uintptr_t thread_filter = 0;
92 static uint64_t find_size = 0;
93 static const char* find_name = NULL;
94 static uint64_t time_from = 0;
95 static uint64_t time_to = 0xffffffffffffffffULL;
96 static int use_time_filter = 0;
97 static uint64_t startup_time = 0;
98 static FILE* outfile = NULL;
99 static FILE* coverage_outfile = NULL;
101 static int32_t
102 read_int16 (unsigned char *p)
104 int32_t value = *p++;
105 value |= (*p++) << 8;
106 return value;
109 static int32_t
110 read_int32 (unsigned char *p)
112 int32_t value = *p++;
113 value |= (*p++) << 8;
114 value |= (*p++) << 16;
115 value |= (uint32_t)(*p++) << 24;
116 return value;
119 static int64_t
120 read_int64 (unsigned char *p)
122 uint64_t value = *p++;
123 value |= (*p++) << 8;
124 value |= (*p++) << 16;
125 value |= (uint64_t)(*p++) << 24;
126 value |= (uint64_t)(*p++) << 32;
127 value |= (uint64_t)(*p++) << 40;
128 value |= (uint64_t)(*p++) << 48;
129 value |= (uint64_t)(*p++) << 54;
130 return value;
133 static char*
134 pstrdup (const char *s)
136 int len = strlen (s) + 1;
137 char *p = (char *) g_malloc (len);
138 memcpy (p, s, len);
139 return p;
142 typedef struct _CounterValue CounterValue;
143 struct _CounterValue {
144 uint64_t timestamp;
145 unsigned char *buffer;
146 CounterValue *next;
149 typedef struct _Counter Counter;
150 struct _Counter {
151 int index;
152 const char *section;
153 const char *name;
154 int type;
155 int unit;
156 int variance;
157 CounterValue *values;
158 CounterValue *values_last;
161 typedef struct _CounterList CounterList;
162 struct _CounterList {
163 Counter *counter;
164 CounterList *next;
167 typedef struct _CounterSection CounterSection;
168 struct _CounterSection {
169 const char *value;
170 CounterList *counters;
171 CounterList *counters_last;
172 CounterSection *next;
175 typedef struct _CounterTimestamp CounterTimestamp;
176 struct _CounterTimestamp {
177 uint64_t value;
178 CounterSection *sections;
179 CounterSection *sections_last;
180 CounterTimestamp *next;
183 static CounterList *counters = NULL;
184 static CounterSection *counters_sections = NULL;
185 static CounterTimestamp *counters_timestamps = NULL;
187 enum {
188 COUNTERS_SORT_TIME,
189 COUNTERS_SORT_CATEGORY
192 static int counters_sort_mode = COUNTERS_SORT_TIME;
194 static void
195 add_counter_to_section (Counter *counter)
197 CounterSection *csection, *s;
198 CounterList *clist;
200 clist = (CounterList *) g_calloc (1, sizeof (CounterList));
201 clist->counter = counter;
203 for (csection = counters_sections; csection; csection = csection->next) {
204 if (strcmp (csection->value, counter->section) == 0) {
205 /* If section exist */
206 if (!csection->counters)
207 csection->counters = clist;
208 else
209 csection->counters_last->next = clist;
210 csection->counters_last = clist;
211 return;
215 /* If section does not exist */
216 csection = (CounterSection *) g_calloc (1, sizeof (CounterSection));
217 csection->value = counter->section;
218 csection->counters = clist;
219 csection->counters_last = clist;
221 if (!counters_sections) {
222 counters_sections = csection;
223 } else {
224 s = counters_sections;
225 while (s->next)
226 s = s->next;
227 s->next = csection;
231 static void
232 add_counter (const char *section, const char *name, int type, int unit, int variance, int index)
234 CounterList *list, *l;
235 Counter *counter;
237 for (list = counters; list; list = list->next)
238 if (list->counter->index == index)
239 return;
241 counter = (Counter *) g_calloc (1, sizeof (Counter));
242 counter->section = section;
243 counter->name = name;
244 counter->type = type;
245 counter->unit = unit;
246 counter->variance = variance;
247 counter->index = index;
249 list = (CounterList *) g_calloc (1, sizeof (CounterList));
250 list->counter = counter;
252 if (!counters) {
253 counters = list;
254 } else {
255 l = counters;
256 while (l->next)
257 l = l->next;
258 l->next = list;
261 if (counters_sort_mode == COUNTERS_SORT_CATEGORY || !verbose)
262 add_counter_to_section (counter);
265 static void
266 add_counter_to_timestamp (uint64_t timestamp, Counter *counter)
268 CounterTimestamp *ctimestamp, *t;
269 CounterSection *csection;
270 CounterList *clist;
272 clist = (CounterList *) g_calloc (1, sizeof (CounterList));
273 clist->counter = counter;
275 for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
276 if (ctimestamp->value == timestamp) {
277 for (csection = ctimestamp->sections; csection; csection = csection->next) {
278 if (strcmp (csection->value, counter->section) == 0) {
279 /* if timestamp exist and section exist */
280 if (!csection->counters)
281 csection->counters = clist;
282 else
283 csection->counters_last->next = clist;
284 csection->counters_last = clist;
285 return;
289 /* if timestamp exist and section does not exist */
290 csection = (CounterSection *) g_calloc (1, sizeof (CounterSection));
291 csection->value = counter->section;
292 csection->counters = clist;
293 csection->counters_last = clist;
295 if (!ctimestamp->sections)
296 ctimestamp->sections = csection;
297 else
298 ctimestamp->sections_last->next = csection;
299 ctimestamp->sections_last = csection;
300 return;
304 /* If timestamp do not exist and section does not exist */
305 csection = (CounterSection *) g_calloc (1, sizeof (CounterSection));
306 csection->value = counter->section;
307 csection->counters = clist;
308 csection->counters_last = clist;
310 ctimestamp = (CounterTimestamp *) g_calloc (1, sizeof (CounterTimestamp));
311 ctimestamp->value = timestamp;
312 ctimestamp->sections = csection;
313 ctimestamp->sections_last = csection;
315 if (!counters_timestamps) {
316 counters_timestamps = ctimestamp;
317 } else {
318 t = counters_timestamps;
319 while (t->next)
320 t = t->next;
321 t->next = ctimestamp;
325 static void
326 add_counter_value (int index, CounterValue *value)
328 CounterList *list;
330 for (list = counters; list; list = list->next) {
331 if (list->counter->index == index) {
332 if (!list->counter->values)
333 list->counter->values = value;
334 else
335 list->counter->values_last->next = value;
336 list->counter->values_last = value;
338 if (counters_sort_mode == COUNTERS_SORT_TIME)
339 add_counter_to_timestamp (value->timestamp, list->counter);
341 return;
346 static const char*
347 section_name (int section)
349 switch (section) {
350 case MONO_COUNTER_JIT: return "Mono JIT";
351 case MONO_COUNTER_GC: return "Mono GC";
352 case MONO_COUNTER_METADATA: return "Mono Metadata";
353 case MONO_COUNTER_GENERICS: return "Mono Generics";
354 case MONO_COUNTER_SECURITY: return "Mono Security";
355 case MONO_COUNTER_RUNTIME: return "Mono Runtime";
356 case MONO_COUNTER_SYSTEM: return "Mono System";
357 case MONO_COUNTER_PROFILER: return "Mono Profiler";
358 default: return "<unknown>";
362 static const char*
363 type_name (int type)
365 switch (type) {
366 case MONO_COUNTER_INT: return "Int";
367 case MONO_COUNTER_UINT: return "UInt";
368 case MONO_COUNTER_WORD: return "Word";
369 case MONO_COUNTER_LONG: return "Long";
370 case MONO_COUNTER_ULONG: return "ULong";
371 case MONO_COUNTER_DOUBLE: return "Double";
372 case MONO_COUNTER_STRING: return "String";
373 case MONO_COUNTER_TIME_INTERVAL: return "Time Interval";
374 default: return "<unknown>";
378 static const char*
379 unit_name (int unit)
381 switch (unit) {
382 case MONO_COUNTER_RAW: return "Raw";
383 case MONO_COUNTER_BYTES: return "Bytes";
384 case MONO_COUNTER_TIME: return "Time";
385 case MONO_COUNTER_COUNT: return "Count";
386 case MONO_COUNTER_PERCENTAGE: return "Percentage";
387 default: return "<unknown>";
391 static const char*
392 variance_name (int variance)
394 switch (variance) {
395 case MONO_COUNTER_MONOTONIC: return "Monotonic";
396 case MONO_COUNTER_CONSTANT: return "Constant";
397 case MONO_COUNTER_VARIABLE: return "Variable";
398 default: return "<unknown>";
402 static void
403 dump_counters_value (Counter *counter, const char *key_format, const char *key, void *value)
405 char format[32];
407 if (value == NULL) {
408 snprintf (format, sizeof (format), "%s : %%s\n", key_format);
409 fprintf (outfile, format, key, "<null>");
410 } else {
411 switch (counter->type) {
412 case MONO_COUNTER_INT:
413 #if SIZEOF_VOID_P == 4
414 case MONO_COUNTER_WORD:
415 #endif
416 snprintf (format, sizeof (format), "%s : %%d\n", key_format);
417 fprintf (outfile, format, key, *(int32_t*)value);
418 break;
419 case MONO_COUNTER_UINT:
420 snprintf (format, sizeof (format), "%s : %%u\n", key_format);
421 fprintf (outfile, format, key, *(uint32_t*)value);
422 break;
423 case MONO_COUNTER_LONG:
424 #if SIZEOF_VOID_P == 8
425 case MONO_COUNTER_WORD:
426 #endif
427 case MONO_COUNTER_TIME_INTERVAL:
428 if (counter->type == MONO_COUNTER_LONG && counter->unit == MONO_COUNTER_TIME) {
429 snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
430 fprintf (outfile, format, key, (double)*(int64_t*)value / 10000.0);
431 } else if (counter->type == MONO_COUNTER_TIME_INTERVAL) {
432 snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
433 fprintf (outfile, format, key, (double)*(int64_t*)value / 1000.0);
434 } else {
435 snprintf (format, sizeof (format), "%s : %%u\n", key_format);
436 fprintf (outfile, format, key, *(int64_t*)value);
438 break;
439 case MONO_COUNTER_ULONG:
440 snprintf (format, sizeof (format), "%s : %%llu\n", key_format);
441 fprintf (outfile, format, key, *(uint64_t*)value);
442 break;
443 case MONO_COUNTER_DOUBLE:
444 snprintf (format, sizeof (format), "%s : %%f\n", key_format);
445 fprintf (outfile, format, key, *(double*)value);
446 break;
447 case MONO_COUNTER_STRING:
448 snprintf (format, sizeof (format), "%s : %%s\n", key_format);
449 fprintf (outfile, format, key, *(char*)value);
450 break;
455 static void
456 dump_counters (void)
458 Counter *counter;
459 CounterValue *cvalue;
460 CounterTimestamp *ctimestamp;
461 CounterSection *csection;
462 CounterList *clist;
463 char strtimestamp[17];
464 int i, section_printed;
466 fprintf (outfile, "\nCounters:\n");
468 if (!verbose) {
469 char counters_to_print[][64] = {
470 "Methods from AOT",
471 "Methods JITted using mono JIT",
472 "Methods JITted using LLVM",
473 "Total time spent JITting (sec)",
474 "User Time",
475 "System Time",
476 "Total Time",
477 "Working Set",
478 "Private Bytes",
479 "Virtual Bytes",
480 "Page Faults",
481 "CPU Load Average - 1min",
482 "CPU Load Average - 5min",
483 "CPU Load Average - 15min",
487 for (csection = counters_sections; csection; csection = csection->next) {
488 section_printed = 0;
490 for (clist = csection->counters; clist; clist = clist->next) {
491 counter = clist->counter;
492 if (!counter->values_last)
493 continue;
495 for (i = 0; counters_to_print [i][0] != 0; i++) {
496 if (strcmp (counters_to_print [i], counter->name) == 0) {
497 if (!section_printed) {
498 fprintf (outfile, "\t%s:\n", csection->value);
499 section_printed = 1;
502 dump_counters_value (counter, "\t\t%-30s", counter->name, counter->values_last->buffer);
503 break;
508 } else if (counters_sort_mode == COUNTERS_SORT_TIME) {
509 for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
510 fprintf (outfile, "\t%llu:%02llu:%02llu:%02llu.%03llu:\n",
511 (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 / 24 % 1000),
512 (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 % 24),
513 (unsigned long long) (ctimestamp->value / 1000 / 60 % 60),
514 (unsigned long long) (ctimestamp->value / 1000 % 60),
515 (unsigned long long) (ctimestamp->value % 1000));
517 for (csection = ctimestamp->sections; csection; csection = csection->next) {
518 fprintf (outfile, "\t\t%s:\n", csection->value);
520 for (clist = csection->counters; clist; clist = clist->next) {
521 counter = clist->counter;
522 for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
523 if (cvalue->timestamp != ctimestamp->value)
524 continue;
526 dump_counters_value (counter, "\t\t\t%-30s", counter->name, cvalue->buffer);
531 } else if (counters_sort_mode == COUNTERS_SORT_CATEGORY) {
532 for (csection = counters_sections; csection; csection = csection->next) {
533 fprintf (outfile, "\t%s:\n", csection->value);
535 for (clist = csection->counters; clist; clist = clist->next) {
536 counter = clist->counter;
537 fprintf (outfile, "\t\t%s: [type: %s, unit: %s, variance: %s]\n",
538 counter->name, type_name (counter->type), unit_name (counter->unit), variance_name (counter->variance));
540 for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
541 snprintf (strtimestamp, sizeof (strtimestamp), "%llu:%02llu:%02llu:%02llu.%03llu",
542 (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 / 24 % 1000),
543 (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 % 24),
544 (unsigned long long) (cvalue->timestamp / 1000 / 60 % 60),
545 (unsigned long long) (cvalue->timestamp / 1000 % 60),
546 (unsigned long long) (cvalue->timestamp % 1000));
548 dump_counters_value (counter, "\t\t\t%s", strtimestamp, cvalue->buffer);
555 static int num_images;
556 typedef struct _ImageDesc ImageDesc;
557 struct _ImageDesc {
558 ImageDesc *next;
559 intptr_t image;
560 char *filename;
563 static ImageDesc* image_hash [SMALL_HASH_SIZE] = {0};
565 static void
566 add_image (intptr_t image, char *name)
568 int slot = ((image >> 2) & 0xffff) % SMALL_HASH_SIZE;
569 ImageDesc *cd = (ImageDesc *) g_malloc (sizeof (ImageDesc));
570 cd->image = image;
571 cd->filename = pstrdup (name);
572 cd->next = image_hash [slot];
573 image_hash [slot] = cd;
574 num_images++;
577 static int num_assemblies;
579 typedef struct _AssemblyDesc AssemblyDesc;
580 struct _AssemblyDesc {
581 AssemblyDesc *next;
582 intptr_t assembly;
583 char *asmname;
586 static AssemblyDesc* assembly_hash [SMALL_HASH_SIZE] = {0};
588 static void
589 add_assembly (intptr_t assembly, char *name)
591 int slot = ((assembly >> 2) & 0xffff) % SMALL_HASH_SIZE;
592 AssemblyDesc *cd = (AssemblyDesc *) g_malloc (sizeof (AssemblyDesc));
593 cd->assembly = assembly;
594 cd->asmname = pstrdup (name);
595 cd->next = assembly_hash [slot];
596 assembly_hash [slot] = cd;
597 num_assemblies++;
600 typedef struct _BackTrace BackTrace;
601 typedef struct {
602 uint64_t count;
603 BackTrace *bt;
604 } CallContext;
606 typedef struct {
607 int count;
608 int size;
609 CallContext *traces;
610 } TraceDesc;
612 typedef struct _ClassDesc ClassDesc;
613 struct _ClassDesc {
614 ClassDesc *next;
615 intptr_t klass;
616 char *name;
617 intptr_t allocs;
618 uint64_t alloc_size;
619 TraceDesc traces;
622 static ClassDesc* class_hash [HASH_SIZE] = {0};
623 static int num_classes = 0;
625 static ClassDesc*
626 add_class (intptr_t klass, const char *name)
628 int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
629 ClassDesc *cd;
630 cd = class_hash [slot];
631 while (cd && cd->klass != klass)
632 cd = cd->next;
633 /* we resolved an unknown class (unless we had the code unloaded) */
634 if (cd) {
635 /*printf ("resolved unknown: %s\n", name);*/
636 g_free (cd->name);
637 cd->name = pstrdup (name);
638 return cd;
640 cd = (ClassDesc *) g_calloc (sizeof (ClassDesc), 1);
641 cd->klass = klass;
642 cd->name = pstrdup (name);
643 cd->next = class_hash [slot];
644 cd->allocs = 0;
645 cd->alloc_size = 0;
646 cd->traces.count = 0;
647 cd->traces.size = 0;
648 cd->traces.traces = NULL;
649 class_hash [slot] = cd;
650 num_classes++;
651 return cd;
654 static ClassDesc *
655 lookup_class (intptr_t klass)
657 int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
658 ClassDesc *cd = class_hash [slot];
659 while (cd && cd->klass != klass)
660 cd = cd->next;
661 if (!cd) {
662 char buf [128];
663 snprintf (buf, sizeof (buf), "unresolved class %p", (void*)klass);
664 return add_class (klass, buf);
666 return cd;
669 typedef struct _MethodDesc MethodDesc;
670 struct _MethodDesc {
671 MethodDesc *next;
672 intptr_t method;
673 char *name;
674 intptr_t code;
675 int len;
676 int recurse_count;
677 int sample_hits;
678 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 */
679 uint64_t calls;
680 uint64_t total_time;
681 uint64_t callee_time;
682 uint64_t self_time;
683 TraceDesc traces;
686 static MethodDesc* method_hash [HASH_SIZE] = {0};
687 static int num_methods = 0;
689 static MethodDesc*
690 add_method (intptr_t method, const char *name, intptr_t code, int len)
692 int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
693 MethodDesc *cd;
694 cd = method_hash [slot];
695 while (cd && cd->method != method)
696 cd = cd->next;
697 /* we resolved an unknown method (unless we had the code unloaded) */
698 if (cd) {
699 cd->code = code;
700 cd->len = len;
701 /*printf ("resolved unknown: %s\n", name);*/
702 g_free (cd->name);
703 cd->name = pstrdup (name);
704 return cd;
706 cd = (MethodDesc *) g_calloc (sizeof (MethodDesc), 1);
707 cd->method = method;
708 cd->name = pstrdup (name);
709 cd->code = code;
710 cd->len = len;
711 cd->calls = 0;
712 cd->total_time = 0;
713 cd->traces.count = 0;
714 cd->traces.size = 0;
715 cd->traces.traces = NULL;
716 cd->next = method_hash [slot];
717 method_hash [slot] = cd;
718 num_methods++;
719 return cd;
722 static MethodDesc *
723 lookup_method (intptr_t method)
725 int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
726 MethodDesc *cd = method_hash [slot];
727 while (cd && cd->method != method)
728 cd = cd->next;
729 if (!cd) {
730 char buf [128];
731 snprintf (buf, sizeof (buf), "unknown method %p", (void*)method);
732 return add_method (method, buf, 0, 0);
734 return cd;
737 static int num_stat_samples = 0;
738 static int size_stat_samples = 0;
739 uintptr_t *stat_samples = NULL;
740 int *stat_sample_desc = NULL;
742 static void
743 add_stat_sample (int type, uintptr_t ip) {
744 if (num_stat_samples == size_stat_samples) {
745 size_stat_samples *= 2;
746 if (!size_stat_samples)
747 size_stat_samples = 32;
748 stat_samples = (uintptr_t *) g_realloc (stat_samples, size_stat_samples * sizeof (uintptr_t));
749 stat_sample_desc = (int *) g_realloc (stat_sample_desc, size_stat_samples * sizeof (int));
751 stat_samples [num_stat_samples] = ip;
752 stat_sample_desc [num_stat_samples++] = type;
755 static MethodDesc*
756 lookup_method_by_ip (uintptr_t ip)
758 int i;
759 MethodDesc* m;
760 /* dumb */
761 for (i = 0; i < HASH_SIZE; ++i) {
762 m = method_hash [i];
763 while (m) {
764 //printf ("checking %p against %p-%p\n", (void*)ip, (void*)(m->code), (void*)(m->code + m->len));
765 if (ip >= (uintptr_t)m->code && ip < (uintptr_t)m->code + m->len) {
766 return m;
768 m = m->next;
771 return NULL;
774 static int
775 compare_method_samples (const void *a, const void *b)
777 MethodDesc *const *A = (MethodDesc *const *)a;
778 MethodDesc *const *B = (MethodDesc *const *)b;
779 if ((*A)->sample_hits == (*B)->sample_hits)
780 return 0;
781 if ((*B)->sample_hits < (*A)->sample_hits)
782 return -1;
783 return 1;
786 typedef struct _UnmanagedSymbol UnmanagedSymbol;
787 struct _UnmanagedSymbol {
788 UnmanagedSymbol *parent;
789 char *name;
790 int is_binary;
791 uintptr_t addr;
792 uintptr_t size;
793 uintptr_t sample_hits;
796 static UnmanagedSymbol **usymbols = NULL;
797 static int usymbols_size = 0;
798 static int usymbols_num = 0;
800 static int
801 compare_usymbol_addr (const void *a, const void *b)
803 UnmanagedSymbol *const *A = (UnmanagedSymbol *const *)a;
804 UnmanagedSymbol *const *B = (UnmanagedSymbol *const *)b;
805 if ((*B)->addr == (*A)->addr)
806 return 0;
807 if ((*B)->addr > (*A)->addr)
808 return -1;
809 return 1;
812 static int
813 compare_usymbol_samples (const void *a, const void *b)
815 UnmanagedSymbol *const *A = (UnmanagedSymbol *const *)a;
816 UnmanagedSymbol *const *B = (UnmanagedSymbol *const *)b;
817 if ((*B)->sample_hits == (*A)->sample_hits)
818 return 0;
819 if ((*B)->sample_hits < (*A)->sample_hits)
820 return -1;
821 return 1;
824 static void
825 add_unmanaged_symbol (uintptr_t addr, char *name, uintptr_t size)
827 UnmanagedSymbol *sym;
828 if (usymbols_num == usymbols_size) {
829 int new_size = usymbols_size * 2;
830 if (!new_size)
831 new_size = 16;
832 usymbols = (UnmanagedSymbol **) g_realloc (usymbols, sizeof (void*) * new_size);
833 usymbols_size = new_size;
835 sym = (UnmanagedSymbol *) g_calloc (sizeof (UnmanagedSymbol), 1);
836 sym->addr = addr;
837 sym->name = name;
838 sym->size = size;
839 usymbols [usymbols_num++] = sym;
842 /* only valid after the symbols are sorted */
843 static UnmanagedSymbol*
844 lookup_unmanaged_symbol (uintptr_t addr)
846 int r = usymbols_num - 1;
847 int l = 0;
848 UnmanagedSymbol *sym;
849 int last_best = -1;
850 while (r >= l) {
851 int m = (l + r) / 2;
852 sym = usymbols [m];
853 if (addr == sym->addr)
854 return sym;
855 if (addr < sym->addr) {
856 r = m - 1;
857 } else if (addr > sym->addr) {
858 l = m + 1;
859 last_best = m;
862 if (last_best >= 0 && (addr - usymbols [last_best]->addr) < 4096)
863 return usymbols [last_best];
864 return NULL;
867 /* we use the same structure for binaries */
868 static UnmanagedSymbol **ubinaries = NULL;
869 static int ubinaries_size = 0;
870 static int ubinaries_num = 0;
872 static void
873 add_unmanaged_binary (uintptr_t addr, char *name, uintptr_t size)
875 UnmanagedSymbol *sym;
876 if (ubinaries_num == ubinaries_size) {
877 int new_size = ubinaries_size * 2;
878 if (!new_size)
879 new_size = 16;
880 ubinaries = (UnmanagedSymbol **) g_realloc (ubinaries, sizeof (void*) * new_size);
881 ubinaries_size = new_size;
883 sym = (UnmanagedSymbol *) g_calloc (sizeof (UnmanagedSymbol), 1);
884 sym->addr = addr;
885 sym->name = name;
886 sym->size = size;
887 sym->is_binary = 1;
888 ubinaries [ubinaries_num++] = sym;
891 static UnmanagedSymbol*
892 lookup_unmanaged_binary (uintptr_t addr)
894 int i;
895 for (i = 0; i < ubinaries_num; ++i) {
896 UnmanagedSymbol *ubin = ubinaries [i];
897 if (addr >= ubin->addr && addr < ubin->addr + ubin->size) {
898 return ubin;
901 return NULL;
904 static const char*
905 sample_type_name (int type)
907 switch (type) {
908 case SAMPLE_CYCLES: return "cycles";
909 case SAMPLE_INSTRUCTIONS: return "instructions retired";
910 case SAMPLE_CACHE_MISSES: return "cache misses";
911 case SAMPLE_CACHE_REFS: return "cache references";
912 case SAMPLE_BRANCHES: return "executed branches";
913 case SAMPLE_BRANCH_MISSES: return "unpredicted branches";
915 return "unknown";
918 static void
919 set_usym_parent (UnmanagedSymbol** cachedus, int count)
921 int i;
922 for (i = 0; i < count; ++i) {
923 UnmanagedSymbol *ubin = lookup_unmanaged_binary (cachedus [i]->addr);
924 if (ubin == cachedus [i])
925 continue;
926 cachedus [i]->parent = ubin;
930 static void
931 print_usym (UnmanagedSymbol* um)
933 if (um->parent)
934 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);
935 else
936 fprintf (outfile, "\t%6zd %6.2f %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name);
939 static int
940 sym_percent (uintptr_t sample_hits)
942 double pc;
943 if (verbose)
944 return 1;
945 pc = sample_hits*100.0/num_stat_samples;
946 return pc >= 0.1;
949 static void
950 dump_samples (void)
952 int i, u;
953 int count = 0, msize = 0;
954 int unmanaged_hits = 0;
955 int unresolved_hits = 0;
956 MethodDesc** cachedm = NULL;
957 int ucount = 0, usize = 0;
958 UnmanagedSymbol** cachedus = NULL;
959 if (!num_stat_samples)
960 return;
961 qsort (usymbols, usymbols_num, sizeof (UnmanagedSymbol*), compare_usymbol_addr);
962 for (i = 0; i < num_stat_samples; ++i) {
963 MethodDesc *m = lookup_method_by_ip (stat_samples [i]);
964 if (m) {
965 if (!m->sample_hits) {
966 if (count == msize) {
967 msize *= 2;
968 if (!msize)
969 msize = 4;
970 cachedm = (MethodDesc **) g_realloc (cachedm, sizeof (void*) * msize);
972 cachedm [count++] = m;
974 m->sample_hits++;
975 } else {
976 UnmanagedSymbol *usym = lookup_unmanaged_symbol (stat_samples [i]);
977 if (!usym) {
978 unresolved_hits++;
979 //printf ("unmanaged hit at %p\n", (void*)stat_samples [i]);
980 usym = lookup_unmanaged_binary (stat_samples [i]);
982 if (usym) {
983 if (!usym->sample_hits) {
984 if (ucount == usize) {
985 usize *= 2;
986 if (!usize)
987 usize = 4;
988 cachedus = (UnmanagedSymbol **) g_realloc (cachedus, sizeof (void*) * usize);
990 cachedus [ucount++] = usym;
992 usym->sample_hits++;
994 unmanaged_hits++;
997 qsort (cachedm, count, sizeof (MethodDesc*), compare_method_samples);
998 qsort (cachedus, ucount, sizeof (UnmanagedSymbol*), compare_usymbol_samples);
999 set_usym_parent (cachedus, ucount);
1000 fprintf (outfile, "\nStatistical samples summary\n");
1001 fprintf (outfile, "\tSample type: %s\n", sample_type_name (stat_sample_desc [0]));
1002 fprintf (outfile, "\tUnmanaged hits: %6d (%4.1f%%)\n", unmanaged_hits, (100.0*unmanaged_hits)/num_stat_samples);
1003 fprintf (outfile, "\tManaged hits: %6d (%4.1f%%)\n", num_stat_samples - unmanaged_hits, (100.0*(num_stat_samples-unmanaged_hits))/num_stat_samples);
1004 fprintf (outfile, "\tUnresolved hits: %6d (%4.1f%%)\n", unresolved_hits, (100.0*unresolved_hits)/num_stat_samples);
1005 fprintf (outfile, "\t%6s %6s %s\n", "Hits", "%", "Method name");
1006 i = 0;
1007 u = 0;
1008 while (i < count || u < ucount) {
1009 if (i < count) {
1010 MethodDesc *m = cachedm [i];
1011 if (u < ucount) {
1012 UnmanagedSymbol *um = cachedus [u];
1013 if (um->sample_hits > m->sample_hits) {
1014 if (!sym_percent (um->sample_hits))
1015 break;
1016 print_usym (um);
1017 u++;
1018 continue;
1021 if (!sym_percent (m->sample_hits))
1022 break;
1023 fprintf (outfile, "\t%6d %6.2f %s\n", m->sample_hits, m->sample_hits*100.0/num_stat_samples, m->name);
1024 i++;
1025 continue;
1027 if (u < ucount) {
1028 UnmanagedSymbol *um = cachedus [u];
1029 if (!sym_percent (um->sample_hits))
1030 break;
1031 print_usym (um);
1032 u++;
1033 continue;
1038 typedef struct _HeapClassDesc HeapClassDesc;
1039 typedef struct {
1040 HeapClassDesc *klass;
1041 uint64_t count;
1042 } HeapClassRevRef;
1044 struct _HeapClassDesc {
1045 ClassDesc *klass;
1046 int64_t count;
1047 int64_t total_size;
1048 HeapClassRevRef *rev_hash;
1049 int rev_hash_size;
1050 int rev_count;
1051 uintptr_t pinned_references;
1052 uintptr_t root_references;
1055 static int
1056 add_rev_class_hashed (HeapClassRevRef *rev_hash, uintptr_t size, HeapClassDesc *hklass, uint64_t value)
1058 uintptr_t i;
1059 uintptr_t start_pos;
1060 start_pos = (hklass->klass->klass >> 2) % size;
1061 assert (start_pos < size);
1062 i = start_pos;
1063 do {
1064 if (rev_hash [i].klass == hklass) {
1065 rev_hash [i].count += value;
1066 return 0;
1067 } else if (!rev_hash [i].klass) {
1068 rev_hash [i].klass = hklass;
1069 rev_hash [i].count += value;
1070 start_pos = 0;
1071 for (i = 0; i < size; ++i)
1072 if (rev_hash [i].klass && rev_hash [i].klass->klass == hklass->klass)
1073 start_pos ++;
1074 assert (start_pos == 1);
1075 return 1;
1077 /* wrap around */
1078 if (++i == size)
1079 i = 0;
1080 } while (i != start_pos);
1081 /* should not happen */
1082 printf ("failed revref store\n");
1083 return 0;
1086 static void
1087 add_heap_class_rev (HeapClassDesc *from, HeapClassDesc *to)
1089 uintptr_t i;
1090 if (to->rev_count * 2 >= to->rev_hash_size) {
1091 HeapClassRevRef *n;
1092 uintptr_t old_size = to->rev_hash_size;
1093 to->rev_hash_size *= 2;
1094 if (to->rev_hash_size == 0)
1095 to->rev_hash_size = 4;
1096 n = (HeapClassRevRef *) g_calloc (sizeof (HeapClassRevRef) * to->rev_hash_size, 1);
1097 for (i = 0; i < old_size; ++i) {
1098 if (to->rev_hash [i].klass)
1099 add_rev_class_hashed (n, to->rev_hash_size, to->rev_hash [i].klass, to->rev_hash [i].count);
1101 if (to->rev_hash)
1102 g_free (to->rev_hash);
1103 to->rev_hash = n;
1105 to->rev_count += add_rev_class_hashed (to->rev_hash, to->rev_hash_size, from, 1);
1108 typedef struct {
1109 uintptr_t objaddr;
1110 HeapClassDesc *hklass;
1111 uintptr_t num_refs;
1112 uintptr_t refs [0];
1113 } HeapObjectDesc;
1115 typedef struct _HeapShot HeapShot;
1116 struct _HeapShot {
1117 HeapShot *next;
1118 uint64_t timestamp;
1119 int class_count;
1120 int hash_size;
1121 HeapClassDesc **class_hash;
1122 HeapClassDesc **sorted;
1123 HeapObjectDesc **objects_hash;
1124 uintptr_t objects_count;
1125 uintptr_t objects_hash_size;
1126 uintptr_t num_roots;
1127 uintptr_t *roots;
1128 uintptr_t *roots_extra;
1129 int *roots_types;
1132 static HeapShot *heap_shots = NULL;
1133 static int num_heap_shots = 0;
1135 static HeapShot*
1136 new_heap_shot (uint64_t timestamp)
1138 HeapShot *hs = (HeapShot *) g_calloc (sizeof (HeapShot), 1);
1139 hs->hash_size = 4;
1140 hs->class_hash = (HeapClassDesc **) g_calloc (sizeof (void*), hs->hash_size);
1141 hs->timestamp = timestamp;
1142 num_heap_shots++;
1143 hs->next = heap_shots;
1144 heap_shots = hs;
1145 return hs;
1148 static HeapClassDesc*
1149 heap_class_lookup (HeapShot *hs, ClassDesc *klass)
1151 int i;
1152 unsigned int start_pos;
1153 start_pos = ((uintptr_t)klass->klass >> 2) % hs->hash_size;
1154 i = start_pos;
1155 do {
1156 HeapClassDesc* cd = hs->class_hash [i];
1157 if (!cd)
1158 return NULL;
1159 if (cd->klass == klass)
1160 return cd;
1161 /* wrap around */
1162 if (++i == hs->hash_size)
1163 i = 0;
1164 } while (i != start_pos);
1165 return NULL;
1168 static int
1169 add_heap_hashed (HeapClassDesc **hash, HeapClassDesc **retv, uintptr_t hsize, ClassDesc *klass, uint64_t size, uint64_t count)
1171 uintptr_t i;
1172 uintptr_t start_pos;
1173 start_pos = ((uintptr_t)klass->klass >> 2) % hsize;
1174 i = start_pos;
1175 do {
1176 if (hash [i] && hash [i]->klass == klass) {
1177 hash [i]->total_size += size;
1178 hash [i]->count += count;
1179 *retv = hash [i];
1180 return 0;
1181 } else if (!hash [i]) {
1182 if (*retv) {
1183 hash [i] = *retv;
1184 return 1;
1186 hash [i] = (HeapClassDesc *) g_calloc (sizeof (HeapClassDesc), 1);
1187 hash [i]->klass = klass;
1188 hash [i]->total_size += size;
1189 hash [i]->count += count;
1190 *retv = hash [i];
1191 return 1;
1193 /* wrap around */
1194 if (++i == hsize)
1195 i = 0;
1196 } while (i != start_pos);
1197 /* should not happen */
1198 printf ("failed heap class store\n");
1199 return 0;
1202 static HeapClassDesc*
1203 add_heap_shot_class (HeapShot *hs, ClassDesc *klass, uint64_t size)
1205 HeapClassDesc *res;
1206 int i;
1207 if (hs->class_count * 2 >= hs->hash_size) {
1208 HeapClassDesc **n;
1209 int old_size = hs->hash_size;
1210 hs->hash_size *= 2;
1211 if (hs->hash_size == 0)
1212 hs->hash_size = 4;
1213 n = (HeapClassDesc **) g_calloc (sizeof (void*) * hs->hash_size, 1);
1214 for (i = 0; i < old_size; ++i) {
1215 res = hs->class_hash [i];
1216 if (hs->class_hash [i])
1217 add_heap_hashed (n, &res, hs->hash_size, hs->class_hash [i]->klass, hs->class_hash [i]->total_size, hs->class_hash [i]->count);
1219 if (hs->class_hash)
1220 g_free (hs->class_hash);
1221 hs->class_hash = n;
1223 res = NULL;
1224 hs->class_count += add_heap_hashed (hs->class_hash, &res, hs->hash_size, klass, size, 1);
1225 //if (res->count == 1)
1226 // printf ("added heap class: %s\n", res->klass->name);
1227 return res;
1230 static HeapObjectDesc*
1231 alloc_heap_obj (uintptr_t objaddr, HeapClassDesc *hklass, uintptr_t num_refs)
1233 HeapObjectDesc* ho = (HeapObjectDesc *) g_calloc (sizeof (HeapObjectDesc) + num_refs * sizeof (uintptr_t), 1);
1234 ho->objaddr = objaddr;
1235 ho->hklass = hklass;
1236 ho->num_refs = num_refs;
1237 return ho;
1240 static uintptr_t
1241 heap_shot_find_obj_slot (HeapShot *hs, uintptr_t objaddr)
1243 uintptr_t i;
1244 uintptr_t start_pos;
1245 HeapObjectDesc **hash = hs->objects_hash;
1246 start_pos = ((uintptr_t)objaddr >> 3) % hs->objects_hash_size;
1247 i = start_pos;
1248 do {
1249 if (hash [i] && hash [i]->objaddr == objaddr) {
1250 return i;
1251 } else if (!hash [i]) {
1252 break; /* fail */
1254 /* wrap around */
1255 if (++i == hs->objects_hash_size)
1256 i = 0;
1257 } while (i != start_pos);
1258 /* should not happen */
1259 //printf ("failed heap obj slot\n");
1260 return -1;
1263 static HeapObjectDesc*
1264 heap_shot_obj_add_refs (HeapShot *hs, uintptr_t objaddr, uintptr_t num, uintptr_t *ref_offset)
1266 HeapObjectDesc **hash = hs->objects_hash;
1267 uintptr_t i = heap_shot_find_obj_slot (hs, objaddr);
1268 if (i >= 0) {
1269 HeapObjectDesc* ho = alloc_heap_obj (objaddr, hash [i]->hklass, hash [i]->num_refs + num);
1270 *ref_offset = hash [i]->num_refs;
1271 memcpy (ho->refs, hash [i]->refs, hash [i]->num_refs * sizeof (uintptr_t));
1272 g_free (hash [i]);
1273 hash [i] = ho;
1274 return ho;
1276 /* should not happen */
1277 printf ("failed heap obj update\n");
1278 return NULL;
1282 static uintptr_t
1283 add_heap_hashed_obj (HeapObjectDesc **hash, uintptr_t hsize, HeapObjectDesc *obj)
1285 uintptr_t i;
1286 uintptr_t start_pos;
1287 start_pos = ((uintptr_t)obj->objaddr >> 3) % hsize;
1288 i = start_pos;
1289 do {
1290 if (hash [i] && hash [i]->objaddr == obj->objaddr) {
1291 printf ("duplicate object!\n");
1292 return 0;
1293 } else if (!hash [i]) {
1294 hash [i] = obj;
1295 return 1;
1297 /* wrap around */
1298 if (++i == hsize)
1299 i = 0;
1300 } while (i != start_pos);
1301 /* should not happen */
1302 printf ("failed heap obj store\n");
1303 return 0;
1306 static void
1307 add_heap_shot_obj (HeapShot *hs, HeapObjectDesc *obj)
1309 uintptr_t i;
1310 if (hs->objects_count * 2 >= hs->objects_hash_size) {
1311 HeapObjectDesc **n;
1312 uintptr_t old_size = hs->objects_hash_size;
1313 hs->objects_hash_size *= 2;
1314 if (hs->objects_hash_size == 0)
1315 hs->objects_hash_size = 4;
1316 n = (HeapObjectDesc **) g_calloc (sizeof (void*) * hs->objects_hash_size, 1);
1317 for (i = 0; i < old_size; ++i) {
1318 if (hs->objects_hash [i])
1319 add_heap_hashed_obj (n, hs->objects_hash_size, hs->objects_hash [i]);
1321 if (hs->objects_hash)
1322 g_free (hs->objects_hash);
1323 hs->objects_hash = n;
1325 hs->objects_count += add_heap_hashed_obj (hs->objects_hash, hs->objects_hash_size, obj);
1328 static void
1329 heap_shot_resolve_reverse_refs (HeapShot *hs)
1331 uintptr_t i;
1332 for (i = 0; i < hs->objects_hash_size; ++i) {
1333 uintptr_t r;
1334 HeapObjectDesc *ho = hs->objects_hash [i];
1335 if (!ho)
1336 continue;
1337 for (r = 0; r < ho->num_refs; ++r) {
1338 uintptr_t oi = heap_shot_find_obj_slot (hs, ho->refs [r]);
1339 add_heap_class_rev (ho->hklass, hs->objects_hash [oi]->hklass);
1344 #define MARK_GRAY 1
1345 #define MARK_BLACK 2
1347 static void
1348 heap_shot_mark_objects (HeapShot *hs)
1350 uintptr_t i, oi, r;
1351 unsigned char *marks;
1352 HeapObjectDesc *obj, *ref;
1353 int marked_some;
1354 uintptr_t num_marked = 0, num_unmarked;
1355 for (i = 0; i < hs->num_roots; ++i) {
1356 HeapClassDesc *cd;
1357 oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
1358 if (oi == -1) {
1359 continue;
1361 obj = hs->objects_hash [oi];
1362 cd = obj->hklass;
1363 if (hs->roots_types [i] & MONO_PROFILE_GC_ROOT_PINNING)
1364 cd->pinned_references++;
1365 cd->root_references++;
1367 if (!debug)
1368 return;
1369 /* consistency checks: it seems not all the objects are walked in the heap in some cases */
1370 marks = (unsigned char *) g_calloc (hs->objects_hash_size, 1);
1371 if (!marks)
1372 return;
1373 for (i = 0; i < hs->num_roots; ++i) {
1374 oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
1375 if (oi == -1) {
1376 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);
1377 continue;
1379 obj = hs->objects_hash [oi];
1380 if (!marks [oi]) {
1381 marks [oi] = obj->num_refs? MARK_GRAY: MARK_BLACK;
1382 num_marked++;
1385 marked_some = 1;
1386 while (marked_some) {
1387 marked_some = 0;
1388 for (i = 0; i < hs->objects_hash_size; ++i) {
1389 if (marks [i] != MARK_GRAY)
1390 continue;
1391 marks [i] = MARK_BLACK;
1392 obj = hs->objects_hash [i];
1393 for (r = 0; r < obj->num_refs; ++r) {
1394 oi = heap_shot_find_obj_slot (hs, obj->refs [r]);
1395 if (oi == -1) {
1396 fprintf (outfile, "referenced obj %p not found in heap\n", (void*)obj->refs [r]);
1397 continue;
1399 ref = hs->objects_hash [oi];
1400 if (!marks [oi]) {
1401 marks [oi] = ref->num_refs? MARK_GRAY: MARK_BLACK;
1404 marked_some++;
1408 num_unmarked = 0;
1409 for (i = 0; i < hs->objects_hash_size; ++i) {
1410 if (hs->objects_hash [i] && !marks [i]) {
1411 num_unmarked++;
1412 fprintf (outfile, "object %p (%s) unmarked\n", (void*)hs->objects_hash [i], hs->objects_hash [i]->hklass->klass->name);
1415 fprintf (outfile, "Total unmarked: %zd/%zd\n", num_unmarked, hs->objects_count);
1416 g_free (marks);
1419 static void
1420 heap_shot_free_objects (HeapShot *hs)
1422 uintptr_t i;
1423 for (i = 0; i < hs->objects_hash_size; ++i) {
1424 HeapObjectDesc *ho = hs->objects_hash [i];
1425 if (ho)
1426 g_free (ho);
1428 if (hs->objects_hash)
1429 g_free (hs->objects_hash);
1430 hs->objects_hash = NULL;
1431 hs->objects_hash_size = 0;
1432 hs->objects_count = 0;
1436 struct _BackTrace {
1437 BackTrace *next;
1438 unsigned int hash;
1439 int count;
1440 int id;
1441 MethodDesc *methods [1];
1444 static BackTrace *backtrace_hash [HASH_SIZE];
1445 static BackTrace **backtraces = NULL;
1446 static int num_backtraces = 0;
1447 static int next_backtrace = 0;
1449 static int
1450 hash_backtrace (int count, MethodDesc **methods)
1452 int hash = count;
1453 int i;
1454 for (i = 0; i < count; ++i) {
1455 hash = (hash << 5) - hash + methods [i]->method;
1457 return hash;
1460 static int
1461 compare_backtrace (BackTrace *bt, int count, MethodDesc **methods)
1463 int i;
1464 if (bt->count != count)
1465 return 0;
1466 for (i = 0; i < count; ++i)
1467 if (methods [i] != bt->methods [i])
1468 return 0;
1469 return 1;
1472 static BackTrace*
1473 add_backtrace (int count, MethodDesc **methods)
1475 int hash = hash_backtrace (count, methods);
1476 int slot = (hash & 0xffff) % HASH_SIZE;
1477 BackTrace *bt = backtrace_hash [slot];
1478 while (bt) {
1479 if (bt->hash == hash && compare_backtrace (bt, count, methods))
1480 return bt;
1481 bt = bt->next;
1483 bt = (BackTrace *) g_malloc (sizeof (BackTrace) + ((count - 1) * sizeof (void*)));
1484 bt->next = backtrace_hash [slot];
1485 backtrace_hash [slot] = bt;
1486 if (next_backtrace == num_backtraces) {
1487 num_backtraces *= 2;
1488 if (!num_backtraces)
1489 num_backtraces = 16;
1490 backtraces = (BackTrace **) g_realloc (backtraces, sizeof (void*) * num_backtraces);
1492 bt->id = next_backtrace++;
1493 backtraces [bt->id] = bt;
1494 bt->count = count;
1495 bt->hash = hash;
1496 for (slot = 0; slot < count; ++slot)
1497 bt->methods [slot] = methods [slot];
1499 return bt;
1502 typedef struct _MonitorDesc MonitorDesc;
1503 typedef struct _ThreadContext ThreadContext;
1504 typedef struct _DomainContext DomainContext;
1505 typedef struct _RemCtxContext RemCtxContext;
1507 typedef struct {
1508 FILE *file;
1509 #if defined (HAVE_SYS_ZLIB)
1510 gzFile gzfile;
1511 #endif
1512 unsigned char *buf;
1513 int size;
1514 int data_version;
1515 int version_major;
1516 int version_minor;
1517 int timer_overhead;
1518 int pid;
1519 int port;
1520 char *args;
1521 char *arch;
1522 char *os;
1523 uint64_t startup_time;
1524 ThreadContext *threads;
1525 ThreadContext *current_thread;
1526 DomainContext *domains;
1527 DomainContext *current_domain;
1528 RemCtxContext *remctxs;
1529 RemCtxContext *current_remctx;
1530 } ProfContext;
1532 struct _ThreadContext {
1533 ThreadContext *next;
1534 intptr_t thread_id;
1535 char *name;
1536 /* emulated stack */
1537 MethodDesc **stack;
1538 uint64_t *time_stack;
1539 uint64_t *callee_time_stack;
1540 uint64_t last_time;
1541 uint64_t contention_start;
1542 MonitorDesc *monitor;
1543 int stack_size;
1544 int stack_id;
1545 HeapShot *current_heap_shot;
1546 uintptr_t num_roots;
1547 uintptr_t size_roots;
1548 uintptr_t *roots;
1549 uintptr_t *roots_extra;
1550 int *roots_types;
1551 uint64_t gc_start_times [3];
1554 struct _DomainContext {
1555 DomainContext *next;
1556 intptr_t domain_id;
1557 const char *friendly_name;
1560 struct _RemCtxContext {
1561 RemCtxContext *next;
1562 intptr_t remctx_id;
1563 intptr_t domain_id;
1566 static void
1567 ensure_buffer (ProfContext *ctx, int size)
1569 if (ctx->size < size) {
1570 ctx->buf = (unsigned char *) g_realloc (ctx->buf, size);
1571 ctx->size = size;
1575 static int
1576 load_data (ProfContext *ctx, int size)
1578 ensure_buffer (ctx, size);
1579 #if defined (HAVE_SYS_ZLIB)
1580 if (ctx->gzfile) {
1581 int r = gzread (ctx->gzfile, ctx->buf, size);
1582 if (r == 0)
1583 return size == 0? 1: 0;
1584 return r == size;
1585 } else
1586 #endif
1588 int r = fread (ctx->buf, size, 1, ctx->file);
1589 if (r == 0)
1590 return size == 0? 1: 0;
1591 return r;
1595 static ThreadContext*
1596 get_thread (ProfContext *ctx, intptr_t thread_id)
1598 ThreadContext *thread;
1599 if (ctx->current_thread && ctx->current_thread->thread_id == thread_id)
1600 return ctx->current_thread;
1601 thread = ctx->threads;
1602 while (thread) {
1603 if (thread->thread_id == thread_id) {
1604 return thread;
1606 thread = thread->next;
1608 thread = (ThreadContext *) g_calloc (sizeof (ThreadContext), 1);
1609 thread->next = ctx->threads;
1610 ctx->threads = thread;
1611 thread->thread_id = thread_id;
1612 thread->last_time = 0;
1613 thread->stack_id = 0;
1614 thread->stack_size = 32;
1615 thread->stack = (MethodDesc **) g_malloc (thread->stack_size * sizeof (void*));
1616 thread->time_stack = (uint64_t *) g_malloc (thread->stack_size * sizeof (uint64_t));
1617 thread->callee_time_stack = (uint64_t *) g_malloc (thread->stack_size * sizeof (uint64_t));
1618 return thread;
1621 static DomainContext *
1622 get_domain (ProfContext *ctx, intptr_t domain_id)
1624 if (ctx->current_domain && ctx->current_domain->domain_id == domain_id)
1625 return ctx->current_domain;
1627 DomainContext *domain = ctx->domains;
1629 while (domain) {
1630 if (domain->domain_id == domain_id)
1631 return domain;
1633 domain = domain->next;
1636 domain = (DomainContext *) g_calloc (sizeof (DomainContext), 1);
1637 domain->next = ctx->domains;
1638 ctx->domains = domain;
1639 domain->domain_id = domain_id;
1641 return domain;
1644 static RemCtxContext *
1645 get_remctx (ProfContext *ctx, intptr_t remctx_id)
1647 if (ctx->current_remctx && ctx->current_remctx->remctx_id == remctx_id)
1648 return ctx->current_remctx;
1650 RemCtxContext *remctx = ctx->remctxs;
1652 while (remctx) {
1653 if (remctx->remctx_id == remctx_id)
1654 return remctx;
1656 remctx = remctx->next;
1659 remctx = (RemCtxContext *) g_calloc (sizeof (RemCtxContext), 1);
1660 remctx->next = ctx->remctxs;
1661 ctx->remctxs = remctx;
1662 remctx->remctx_id = remctx_id;
1664 return remctx;
1667 static ThreadContext*
1668 load_thread (ProfContext *ctx, intptr_t thread_id)
1670 ThreadContext *thread = get_thread (ctx, thread_id);
1671 ctx->current_thread = thread;
1672 return thread;
1675 static void
1676 ensure_thread_stack (ThreadContext *thread)
1678 if (thread->stack_id == thread->stack_size) {
1679 thread->stack_size *= 2;
1680 thread->stack = (MethodDesc **) g_realloc (thread->stack, thread->stack_size * sizeof (void*));
1681 thread->time_stack = (uint64_t *) g_realloc (thread->time_stack, thread->stack_size * sizeof (uint64_t));
1682 thread->callee_time_stack = (uint64_t *) g_realloc (thread->callee_time_stack, thread->stack_size * sizeof (uint64_t));
1686 static int
1687 add_trace_hashed (CallContext *traces, int size, BackTrace *bt, uint64_t value)
1689 int i;
1690 unsigned int start_pos;
1691 start_pos = bt->hash % size;
1692 i = start_pos;
1693 do {
1694 if (traces [i].bt == bt) {
1695 traces [i].count += value;
1696 return 0;
1697 } else if (!traces [i].bt) {
1698 traces [i].bt = bt;
1699 traces [i].count += value;
1700 return 1;
1702 /* wrap around */
1703 if (++i == size)
1704 i = 0;
1705 } while (i != start_pos);
1706 /* should not happen */
1707 printf ("failed trace store\n");
1708 return 0;
1711 static void
1712 add_trace_bt (BackTrace *bt, TraceDesc *trace, uint64_t value)
1714 int i;
1715 if (!collect_traces)
1716 return;
1717 if (trace->count * 2 >= trace->size) {
1718 CallContext *n;
1719 int old_size = trace->size;
1720 trace->size *= 2;
1721 if (trace->size == 0)
1722 trace->size = 4;
1723 n = (CallContext *) g_calloc (sizeof (CallContext) * trace->size, 1);
1724 for (i = 0; i < old_size; ++i) {
1725 if (trace->traces [i].bt)
1726 add_trace_hashed (n, trace->size, trace->traces [i].bt, trace->traces [i].count);
1728 if (trace->traces)
1729 g_free (trace->traces);
1730 trace->traces = n;
1732 trace->count += add_trace_hashed (trace->traces, trace->size, bt, value);
1735 static BackTrace*
1736 add_trace_thread (ThreadContext *thread, TraceDesc *trace, uint64_t value)
1738 BackTrace *bt;
1739 int count = thread->stack_id;
1740 if (!collect_traces)
1741 return NULL;
1742 if (count > trace_max)
1743 count = trace_max;
1744 bt = add_backtrace (count, thread->stack + thread->stack_id - count);
1745 add_trace_bt (bt, trace, value);
1746 return bt;
1749 static BackTrace*
1750 add_trace_methods (MethodDesc **methods, int count, TraceDesc *trace, uint64_t value)
1752 BackTrace *bt;
1753 if (!collect_traces)
1754 return NULL;
1755 if (count > trace_max)
1756 count = trace_max;
1757 bt = add_backtrace (count, methods);
1758 add_trace_bt (bt, trace, value);
1759 return bt;
1762 static void
1763 thread_add_root (ThreadContext *ctx, uintptr_t obj, int root_type, uintptr_t extra_info)
1765 if (ctx->num_roots == ctx->size_roots) {
1766 int new_size = ctx->size_roots * 2;
1767 if (!new_size)
1768 new_size = 4;
1769 ctx->roots = (uintptr_t *) g_realloc (ctx->roots, new_size * sizeof (uintptr_t));
1770 ctx->roots_extra = (uintptr_t *) g_realloc (ctx->roots_extra, new_size * sizeof (uintptr_t));
1771 ctx->roots_types = (int *) g_realloc (ctx->roots_types, new_size * sizeof (int));
1772 ctx->size_roots = new_size;
1774 ctx->roots_types [ctx->num_roots] = root_type;
1775 ctx->roots_extra [ctx->num_roots] = extra_info;
1776 ctx->roots [ctx->num_roots++] = obj;
1779 static int
1780 compare_callc (const void *a, const void *b)
1782 const CallContext *A = (const CallContext *)a;
1783 const CallContext *B = (const CallContext *)b;
1784 if (B->count == A->count)
1785 return 0;
1786 if (B->count < A->count)
1787 return -1;
1788 return 1;
1791 static void
1792 sort_context_array (TraceDesc* traces)
1794 int i, j;
1795 for (i = 0, j = 0; i < traces->size; ++i) {
1796 if (traces->traces [i].bt) {
1797 traces->traces [j].bt = traces->traces [i].bt;
1798 traces->traces [j].count = traces->traces [i].count;
1799 j++;
1802 qsort (traces->traces, traces->count, sizeof (CallContext), compare_callc);
1805 static void
1806 push_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
1808 ensure_thread_stack (thread);
1809 thread->time_stack [thread->stack_id] = timestamp;
1810 thread->callee_time_stack [thread->stack_id] = 0;
1811 thread->stack [thread->stack_id++] = method;
1812 method->recurse_count++;
1815 static void
1816 pop_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
1818 method->recurse_count--;
1819 if (thread->stack_id > 0 && thread->stack [thread->stack_id - 1] == method) {
1820 uint64_t tdiff;
1821 thread->stack_id--;
1822 method->calls++;
1823 if (timestamp < thread->time_stack [thread->stack_id])
1824 fprintf (outfile, "time went backwards for %s\n", method->name);
1825 tdiff = timestamp - thread->time_stack [thread->stack_id];
1826 if (thread->callee_time_stack [thread->stack_id] > tdiff)
1827 fprintf (outfile, "callee time bigger for %s\n", method->name);
1828 method->self_time += tdiff - thread->callee_time_stack [thread->stack_id];
1829 method->callee_time += thread->callee_time_stack [thread->stack_id];
1830 if (thread->stack_id)
1831 thread->callee_time_stack [thread->stack_id - 1] += tdiff;
1832 //fprintf (outfile, "method %s took %d\n", method->name, (int)(tdiff/1000));
1833 } else {
1834 fprintf (outfile, "unmatched leave at stack pos: %d for method %s\n", thread->stack_id, method->name);
1838 typedef struct {
1839 uint64_t total_time;
1840 uint64_t max_time;
1841 int count;
1842 } GCDesc;
1843 static GCDesc gc_info [3];
1844 static uint64_t max_heap_size;
1845 static uint64_t gc_object_moves;
1846 static int gc_resizes;
1847 typedef struct {
1848 uint64_t created;
1849 uint64_t destroyed;
1850 uint64_t live;
1851 uint64_t max_live;
1852 TraceDesc traces;
1853 TraceDesc destroy_traces;
1854 } HandleInfo;
1855 static HandleInfo handle_info [4];
1857 static const char*
1858 gc_event_name (int ev)
1860 switch (ev) {
1861 case MONO_GC_EVENT_START: return "start";
1862 case MONO_GC_EVENT_MARK_START: return "mark start";
1863 case MONO_GC_EVENT_MARK_END: return "mark end";
1864 case MONO_GC_EVENT_RECLAIM_START: return "reclaim start";
1865 case MONO_GC_EVENT_RECLAIM_END: return "reclaim end";
1866 case MONO_GC_EVENT_END: return "end";
1867 case MONO_GC_EVENT_PRE_STOP_WORLD: return "pre stop";
1868 case MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED: return "pre stop lock";
1869 case MONO_GC_EVENT_POST_STOP_WORLD: return "post stop";
1870 case MONO_GC_EVENT_PRE_START_WORLD: return "pre start";
1871 case MONO_GC_EVENT_POST_START_WORLD: return "post start";
1872 case MONO_GC_EVENT_POST_START_WORLD_UNLOCKED: return "post start unlock";
1873 default:
1874 return "unknown";
1878 static const char*
1879 sync_point_name (int type)
1881 switch (type) {
1882 case SYNC_POINT_PERIODIC: return "periodic";
1883 case SYNC_POINT_WORLD_STOP: return "world stop";
1884 case SYNC_POINT_WORLD_START: return "world start";
1885 default:
1886 return "unknown";
1890 static uint64_t clause_summary [MONO_EXCEPTION_CLAUSE_FAULT + 1];
1891 static uint64_t throw_count = 0;
1892 static TraceDesc exc_traces;
1894 static const char*
1895 clause_name (int type)
1897 switch (type) {
1898 case MONO_EXCEPTION_CLAUSE_NONE: return "catch";
1899 case MONO_EXCEPTION_CLAUSE_FILTER: return "filter";
1900 case MONO_EXCEPTION_CLAUSE_FINALLY: return "finally";
1901 case MONO_EXCEPTION_CLAUSE_FAULT: return "fault";
1902 default: return "invalid";
1906 static uint64_t monitor_contention;
1907 static uint64_t monitor_failed;
1908 static uint64_t monitor_acquired;
1910 struct _MonitorDesc {
1911 MonitorDesc *next;
1912 uintptr_t objid;
1913 uintptr_t contentions;
1914 uint64_t wait_time;
1915 uint64_t max_wait_time;
1916 TraceDesc traces;
1919 static MonitorDesc* monitor_hash [SMALL_HASH_SIZE] = {0};
1920 static int num_monitors = 0;
1922 static MonitorDesc*
1923 lookup_monitor (uintptr_t objid)
1925 int slot = ((objid >> 3) & 0xffff) % SMALL_HASH_SIZE;
1926 MonitorDesc *cd = monitor_hash [slot];
1927 while (cd && cd->objid != objid)
1928 cd = cd->next;
1929 if (!cd) {
1930 cd = (MonitorDesc *) g_calloc (sizeof (MonitorDesc), 1);
1931 cd->objid = objid;
1932 cd->next = monitor_hash [slot];
1933 monitor_hash [slot] = cd;
1934 num_monitors++;
1936 return cd;
1939 static const char*
1940 monitor_ev_name (int ev)
1942 switch (ev) {
1943 case MONO_PROFILER_MONITOR_CONTENTION: return "contended";
1944 case MONO_PROFILER_MONITOR_DONE: return "acquired";
1945 case MONO_PROFILER_MONITOR_FAIL: return "not taken";
1946 default: return "invalid";
1950 static const char*
1951 get_handle_name (int htype)
1953 switch (htype) {
1954 case 0: return "weak";
1955 case 1: return "weaktrack";
1956 case 2: return "normal";
1957 case 3: return "pinned";
1958 default: return "unknown";
1962 static const char*
1963 get_root_name (int rtype)
1965 switch (rtype & MONO_PROFILE_GC_ROOT_TYPEMASK) {
1966 case MONO_PROFILE_GC_ROOT_STACK: return "stack";
1967 case MONO_PROFILE_GC_ROOT_FINALIZER: return "finalizer";
1968 case MONO_PROFILE_GC_ROOT_HANDLE: return "handle";
1969 case MONO_PROFILE_GC_ROOT_OTHER: return "other";
1970 case MONO_PROFILE_GC_ROOT_MISC: return "misc";
1971 default: return "unknown";
1975 static uint64_t
1976 decode_uleb128 (uint8_t *buf, uint8_t **endbuf)
1978 uint64_t res = 0;
1979 int shift = 0;
1981 while (1) {
1982 uint8_t b = *buf++;
1983 res |= (((uint64_t) (b & 0x7f)) << shift);
1985 if (!(b & 0x80))
1986 break;
1988 shift += 7;
1991 *endbuf = buf;
1993 return res;
1996 static intptr_t
1997 decode_sleb128 (uint8_t *buf, uint8_t **endbuf)
1999 uint8_t *p = buf;
2000 intptr_t res = 0;
2001 int shift = 0;
2003 while (1) {
2004 uint8_t b = *p;
2005 p++;
2007 res = res | (((intptr_t) (b & 0x7f)) << shift);
2008 shift += 7;
2010 if (!(b & 0x80)) {
2011 if (shift < sizeof (intptr_t) * 8 && (b & 0x40))
2012 res |= - ((intptr_t) 1 << shift);
2014 break;
2018 *endbuf = p;
2020 return res;
2023 static MethodDesc**
2024 decode_bt (ProfContext *ctx, MethodDesc** sframes, int *size, unsigned char *p, unsigned char **endp, intptr_t ptr_base, intptr_t *method_base)
2026 MethodDesc **frames;
2027 int i;
2028 if (ctx->data_version < 13)
2029 decode_uleb128 (p, &p); /* flags */
2030 int count = decode_uleb128 (p, &p);
2031 if (count > *size)
2032 frames = (MethodDesc **) g_malloc (count * sizeof (void*));
2033 else
2034 frames = sframes;
2035 for (i = 0; i < count; ++i) {
2036 intptr_t ptrdiff = decode_sleb128 (p, &p);
2037 if (ctx->data_version > 12) {
2038 *method_base += ptrdiff;
2039 frames [i] = lookup_method (*method_base);
2040 } else {
2041 frames [i] = lookup_method (ptr_base + ptrdiff);
2044 *size = count;
2045 *endp = p;
2046 return frames;
2049 static void
2050 tracked_creation (uintptr_t obj, ClassDesc *cd, uint64_t size, BackTrace *bt, uint64_t timestamp)
2052 int i;
2053 for (i = 0; i < num_tracked_objects; ++i) {
2054 if (tracked_objects [i] != obj)
2055 continue;
2056 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);
2057 if (bt && bt->count) {
2058 int k;
2059 for (k = 0; k < bt->count; ++k)
2060 fprintf (outfile, "\t%s\n", bt->methods [k]->name);
2065 static void
2066 track_handle (uintptr_t obj, int htype, uint32_t handle, BackTrace *bt, uint64_t timestamp)
2068 int i;
2069 for (i = 0; i < num_tracked_objects; ++i) {
2070 if (tracked_objects [i] != obj)
2071 continue;
2072 fprintf (outfile, "Object %p referenced from handle %u at %.3f secs.\n", (void*)obj, handle, (timestamp - startup_time) / 1000000000.0);
2073 if (bt && bt->count) {
2074 int k;
2075 for (k = 0; k < bt->count; ++k)
2076 fprintf (outfile, "\t%s\n", bt->methods [k]->name);
2081 static void
2082 track_move (uintptr_t src, uintptr_t dst)
2084 int i;
2085 for (i = 0; i < num_tracked_objects; ++i) {
2086 if (tracked_objects [i] == src)
2087 fprintf (outfile, "Object %p moved to %p\n", (void*)src, (void*)dst);
2088 else if (tracked_objects [i] == dst)
2089 fprintf (outfile, "Object %p moved from %p\n", (void*)dst, (void*)src);
2093 static void
2094 track_obj_reference (uintptr_t obj, uintptr_t parent, ClassDesc *cd)
2096 int i;
2097 for (i = 0; i < num_tracked_objects; ++i) {
2098 if (tracked_objects [i] == obj)
2099 fprintf (outfile, "Object %p referenced from %p (%s).\n", (void*)obj, (void*)parent, cd->name);
2103 static void
2104 found_object (uintptr_t obj)
2106 num_tracked_objects ++;
2107 tracked_objects = (uintptr_t *) g_realloc (tracked_objects, num_tracked_objects * sizeof (tracked_objects [0]));
2108 tracked_objects [num_tracked_objects - 1] = obj;
2111 static int num_jit_helpers = 0;
2112 static int jit_helpers_code_size = 0;
2114 static const char*
2115 code_buffer_desc (int type)
2117 switch (type) {
2118 case MONO_PROFILER_CODE_BUFFER_METHOD:
2119 return "method";
2120 case MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE:
2121 return "method trampoline";
2122 case MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE:
2123 return "unbox trampoline";
2124 case MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE:
2125 return "imt trampoline";
2126 case MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE:
2127 return "generics trampoline";
2128 case MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE:
2129 return "specific trampoline";
2130 case MONO_PROFILER_CODE_BUFFER_HELPER:
2131 return "misc helper";
2132 case MONO_PROFILER_CODE_BUFFER_MONITOR:
2133 return "monitor/lock";
2134 case MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE:
2135 return "delegate invoke";
2136 case MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING:
2137 return "exception handling";
2138 default:
2139 return "unspecified";
2143 typedef struct _CoverageAssembly CoverageAssembly;
2144 struct _CoverageAssembly {
2145 char *name;
2146 char *guid;
2147 char *filename;
2148 int number_of_methods;
2149 int fully_covered;
2150 int partially_covered;
2153 typedef struct _CoverageClass CoverageClass;
2154 struct _CoverageClass {
2155 char *assembly_name;
2156 char *class_name;
2157 int number_of_methods;
2158 int fully_covered;
2159 int partially_covered;
2162 typedef struct _CoverageCoverage CoverageCoverage;
2163 struct _CoverageCoverage {
2164 int method_id;
2165 int offset;
2166 int count;
2167 int line;
2168 int column;
2171 typedef struct _CoverageMethod CoverageMethod;
2172 struct _CoverageMethod {
2173 char *assembly_name;
2174 char *class_name;
2175 char *method_name;
2176 char *method_signature;
2177 char *filename;
2178 int token;
2179 int n_statements;
2180 int method_id;
2181 GPtrArray *coverage;
2183 static GPtrArray *coverage_assemblies = NULL;
2184 static GPtrArray *coverage_methods = NULL;
2185 static GPtrArray *coverage_statements = NULL;
2186 static GHashTable *coverage_methods_hash = NULL;
2187 static GPtrArray *coverage_classes = NULL;
2188 static GHashTable *coverage_assembly_classes = NULL;
2190 static void
2191 gather_coverage_statements (void)
2193 for (guint i = 0; i < coverage_statements->len; i++) {
2194 CoverageCoverage *coverage = (CoverageCoverage *)coverage_statements->pdata[i];
2195 CoverageMethod *method = (CoverageMethod *)g_hash_table_lookup (coverage_methods_hash, GINT_TO_POINTER (coverage->method_id));
2196 if (method == NULL) {
2197 fprintf (outfile, "Cannot find method with ID: %d\n", coverage->method_id);
2198 continue;
2201 g_ptr_array_add (method->coverage, coverage);
2205 static void
2206 coverage_add_assembly (CoverageAssembly *assembly)
2208 if (coverage_assemblies == NULL)
2209 coverage_assemblies = g_ptr_array_new ();
2211 g_ptr_array_add (coverage_assemblies, assembly);
2214 static void
2215 coverage_add_method (CoverageMethod *method)
2217 if (coverage_methods == NULL) {
2218 coverage_methods = g_ptr_array_new ();
2219 coverage_methods_hash = g_hash_table_new (NULL, NULL);
2222 g_ptr_array_add (coverage_methods, method);
2223 g_hash_table_insert (coverage_methods_hash, GINT_TO_POINTER (method->method_id), method);
2226 static void
2227 coverage_add_class (CoverageClass *klass)
2229 GPtrArray *classes = NULL;
2231 if (coverage_classes == NULL) {
2232 coverage_classes = g_ptr_array_new ();
2233 coverage_assembly_classes = g_hash_table_new (g_str_hash, g_str_equal);
2236 g_ptr_array_add (coverage_classes, klass);
2237 classes = (GPtrArray *)g_hash_table_lookup (coverage_assembly_classes, klass->assembly_name);
2238 if (classes == NULL) {
2239 classes = g_ptr_array_new ();
2240 g_hash_table_insert (coverage_assembly_classes, klass->assembly_name, classes);
2242 g_ptr_array_add (classes, klass);
2245 static void
2246 coverage_add_coverage (CoverageCoverage *coverage)
2248 if (coverage_statements == NULL)
2249 coverage_statements = g_ptr_array_new ();
2251 g_ptr_array_add (coverage_statements, coverage);
2254 #define OBJ_ADDR(diff) ((obj_base + diff) << 3)
2255 #define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
2258 /* Stats */
2259 #define BUFFER_HEADER_SIZE 48
2261 typedef struct {
2262 int count, min_size, max_size, bytes;
2263 } EventStat;
2265 static int buffer_count;
2266 static EventStat stats [256];
2268 static void
2269 record_event_stats (int type, int size)
2271 ++stats [type].count;
2272 if (!stats [type].min_size)
2273 stats [type].min_size = size;
2274 stats [type].min_size = MIN (stats [type].min_size, size);
2275 stats [type].max_size = MAX (stats [type].max_size, size);
2276 stats [type].bytes += size;
2279 static int
2280 decode_buffer (ProfContext *ctx)
2282 unsigned char *p;
2283 unsigned char *end;
2284 intptr_t thread_id;
2285 intptr_t ptr_base;
2286 intptr_t obj_base;
2287 intptr_t method_base;
2288 uint64_t time_base;
2289 uint64_t file_offset;
2290 int len, i;
2291 ThreadContext *thread;
2293 #ifdef HAVE_SYS_ZLIB
2294 if (ctx->gzfile)
2295 file_offset = gztell (ctx->gzfile);
2296 else
2297 #endif
2298 file_offset = ftell (ctx->file);
2299 if (!load_data (ctx, 48))
2300 return 0;
2301 p = ctx->buf;
2302 if (read_int32 (p) != BUF_ID) {
2303 fprintf (outfile, "Incorrect buffer id: 0x%x\n", read_int32 (p));
2304 for (i = 0; i < 48; ++i) {
2305 fprintf (outfile, "0x%x%s", p [i], i % 8?" ":"\n");
2307 return 0;
2309 len = read_int32 (p + 4);
2310 time_base = read_int64 (p + 8);
2311 ptr_base = read_int64 (p + 16);
2312 obj_base = read_int64 (p + 24);
2313 thread_id = read_int64 (p + 32);
2314 method_base = read_int64 (p + 40);
2315 if (debug)
2316 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);
2317 thread = load_thread (ctx, thread_id);
2318 if (!load_data (ctx, len))
2319 return 0;
2321 ++buffer_count;
2323 if (!startup_time) {
2324 startup_time = time_base;
2325 if (use_time_filter) {
2326 time_from += startup_time;
2327 time_to += startup_time;
2330 for (i = 0; i < thread->stack_id; ++i)
2331 thread->stack [i]->recurse_count++;
2332 p = ctx->buf;
2333 end = p + len;
2334 while (p < end) {
2335 unsigned char *start = p;
2336 unsigned char event = *p;
2337 switch (*p & 0xf) {
2338 case TYPE_GC: {
2339 int subtype = *p & 0xf0;
2340 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2341 LOG_TIME (time_base, tdiff);
2342 time_base += tdiff;
2343 if (subtype == TYPE_GC_RESIZE) {
2344 uint64_t new_size = decode_uleb128 (p, &p);
2345 if (debug)
2346 fprintf (outfile, "gc heap resized to %llu\n", (unsigned long long) new_size);
2347 gc_resizes++;
2348 if (new_size > max_heap_size)
2349 max_heap_size = new_size;
2350 } else if (subtype == TYPE_GC_EVENT) {
2351 uint64_t ev;
2352 if (ctx->data_version > 12)
2353 ev = *p++;
2354 else
2355 ev = decode_uleb128 (p, &p);
2356 int gen;
2357 if (ctx->data_version > 12)
2358 gen = *p++;
2359 else
2360 gen = decode_uleb128 (p, &p);
2361 if (debug)
2362 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);
2363 if (gen > 2) {
2364 fprintf (outfile, "incorrect gc gen: %d\n", gen);
2365 break;
2367 if (ev == MONO_GC_EVENT_START) {
2368 thread->gc_start_times [gen] = time_base;
2369 gc_info [gen].count++;
2370 } else if (ev == MONO_GC_EVENT_END) {
2371 tdiff = time_base - thread->gc_start_times [gen];
2372 gc_info [gen].total_time += tdiff;
2373 if (tdiff > gc_info [gen].max_time)
2374 gc_info [gen].max_time = tdiff;
2376 } else if (subtype == TYPE_GC_MOVE) {
2377 int j, num = decode_uleb128 (p, &p);
2378 gc_object_moves += num / 2;
2379 for (j = 0; j < num; j += 2) {
2380 intptr_t obj1diff = decode_sleb128 (p, &p);
2381 intptr_t obj2diff = decode_sleb128 (p, &p);
2382 if (num_tracked_objects)
2383 track_move (OBJ_ADDR (obj1diff), OBJ_ADDR (obj2diff));
2384 if (debug) {
2385 fprintf (outfile, "moved obj %p to %p\n", (void*)OBJ_ADDR (obj1diff), (void*)OBJ_ADDR (obj2diff));
2388 } else if (subtype == TYPE_GC_HANDLE_CREATED || subtype == TYPE_GC_HANDLE_CREATED_BT) {
2389 int has_bt = subtype == TYPE_GC_HANDLE_CREATED_BT;
2390 int num_bt = 0;
2391 MethodDesc *sframes [8];
2392 MethodDesc **frames = sframes;
2393 int htype = decode_uleb128 (p, &p);
2394 uint32_t handle = decode_uleb128 (p, &p);
2395 intptr_t objdiff = decode_sleb128 (p, &p);
2396 if (has_bt) {
2397 num_bt = 8;
2398 frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2399 if (!frames) {
2400 fprintf (outfile, "Cannot load backtrace\n");
2401 return 0;
2404 if (htype > 3)
2405 return 0;
2406 if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2407 handle_info [htype].created++;
2408 handle_info [htype].live++;
2409 if (handle_info [htype].live > handle_info [htype].max_live)
2410 handle_info [htype].max_live = handle_info [htype].live;
2411 BackTrace *bt;
2412 if (has_bt)
2413 bt = add_trace_methods (frames, num_bt, &handle_info [htype].traces, 1);
2414 else
2415 bt = add_trace_thread (thread, &handle_info [htype].traces, 1);
2416 if (num_tracked_objects)
2417 track_handle (OBJ_ADDR (objdiff), htype, handle, bt, time_base);
2419 if (debug)
2420 fprintf (outfile, "handle (%s) %u created for object %p\n", get_handle_name (htype), handle, (void*)OBJ_ADDR (objdiff));
2421 if (frames != sframes)
2422 g_free (frames);
2423 } else if (subtype == TYPE_GC_HANDLE_DESTROYED || subtype == TYPE_GC_HANDLE_DESTROYED_BT) {
2424 int has_bt = subtype == TYPE_GC_HANDLE_DESTROYED_BT;
2425 int num_bt = 0;
2426 MethodDesc *sframes [8];
2427 MethodDesc **frames = sframes;
2428 int htype = decode_uleb128 (p, &p);
2429 uint32_t handle = decode_uleb128 (p, &p);
2430 if (has_bt) {
2431 num_bt = 8;
2432 frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2433 if (!frames) {
2434 fprintf (outfile, "Cannot load backtrace\n");
2435 return 0;
2438 if (htype > 3)
2439 return 0;
2440 if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2441 handle_info [htype].destroyed ++;
2442 handle_info [htype].live--;
2443 BackTrace *bt;
2444 if (has_bt)
2445 bt = add_trace_methods (frames, num_bt, &handle_info [htype].destroy_traces, 1);
2446 else
2447 bt = add_trace_thread (thread, &handle_info [htype].destroy_traces, 1);
2448 /* TODO: track_handle_free () - would need to record and keep track of the associated object address... */
2450 if (debug)
2451 fprintf (outfile, "handle (%s) %u destroyed\n", get_handle_name (htype), handle);
2452 if (frames != sframes)
2453 g_free (frames);
2454 } else if (subtype == TYPE_GC_FINALIZE_START) {
2455 // TODO: Generate a finalizer report based on these events.
2456 if (debug)
2457 fprintf (outfile, "gc finalizer queue being processed at %llu\n", (unsigned long long) time_base);
2458 } else if (subtype == TYPE_GC_FINALIZE_END) {
2459 if (debug)
2460 fprintf (outfile, "gc finalizer queue finished processing at %llu\n", (unsigned long long) time_base);
2461 } else if (subtype == TYPE_GC_FINALIZE_OBJECT_START) {
2462 intptr_t objdiff = decode_sleb128 (p, &p);
2463 if (debug)
2464 fprintf (outfile, "gc finalizing object %p at %llu\n", (void *) OBJ_ADDR (objdiff), (unsigned long long) time_base);
2465 } else if (subtype == TYPE_GC_FINALIZE_OBJECT_END) {
2466 intptr_t objdiff = decode_sleb128 (p, &p);
2467 if (debug)
2468 fprintf (outfile, "gc finalized object %p at %llu\n", (void *) OBJ_ADDR (objdiff), (unsigned long long) time_base);
2470 break;
2472 case TYPE_METADATA: {
2473 int subtype = *p & 0xf0;
2474 const char *load_str = subtype == TYPE_END_LOAD ? "loaded" : "unloaded";
2475 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2476 int mtype = *p++;
2477 intptr_t ptrdiff = decode_sleb128 (p, &p);
2478 LOG_TIME (time_base, tdiff);
2479 time_base += tdiff;
2480 if (mtype == TYPE_CLASS) {
2481 intptr_t imptrdiff = decode_sleb128 (p, &p);
2482 if (ctx->data_version < 13)
2483 decode_uleb128 (p, &p); /* flags */
2484 if (debug)
2485 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);
2486 if (subtype == TYPE_END_LOAD)
2487 add_class (ptr_base + ptrdiff, (char*)p);
2488 while (*p) p++;
2489 p++;
2490 } else if (mtype == TYPE_IMAGE) {
2491 if (ctx->data_version < 13)
2492 decode_uleb128 (p, &p); /* flags */
2493 if (debug)
2494 fprintf (outfile, "%s image %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
2495 if (subtype == TYPE_END_LOAD)
2496 add_image (ptr_base + ptrdiff, (char*)p);
2497 while (*p) p++;
2498 p++;
2499 } else if (mtype == TYPE_ASSEMBLY) {
2500 if (ctx->data_version < 13)
2501 decode_uleb128 (p, &p); /* flags */
2502 if (debug)
2503 fprintf (outfile, "%s assembly %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
2504 if (subtype == TYPE_END_LOAD)
2505 add_assembly (ptr_base + ptrdiff, (char*)p);
2506 while (*p) p++;
2507 p++;
2508 } else if (mtype == TYPE_DOMAIN) {
2509 if (ctx->data_version < 13)
2510 decode_uleb128 (p, &p); /* flags */
2511 DomainContext *nd = get_domain (ctx, ptr_base + ptrdiff);
2512 /* no subtype means it's a name event, rather than start/stop */
2513 if (subtype == 0)
2514 nd->friendly_name = pstrdup ((char *) p);
2515 if (debug) {
2516 if (subtype == 0)
2517 fprintf (outfile, "domain %p named at %llu: %s\n", (void *) (ptr_base + ptrdiff), (unsigned long long) time_base, p);
2518 else
2519 fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
2521 if (subtype == 0) {
2522 while (*p) p++;
2523 p++;
2525 } else if (mtype == TYPE_CONTEXT) {
2526 if (ctx->data_version < 13)
2527 decode_uleb128 (p, &p); /* flags */
2528 intptr_t domaindiff = decode_sleb128 (p, &p);
2529 if (debug)
2530 fprintf (outfile, "%s context %p (%p) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), (void *) (ptr_base + domaindiff), (unsigned long long) time_base);
2531 if (subtype == TYPE_END_LOAD)
2532 get_remctx (ctx, ptr_base + ptrdiff)->domain_id = ptr_base + domaindiff;
2533 } else if (mtype == TYPE_THREAD) {
2534 if (ctx->data_version < 13)
2535 decode_uleb128 (p, &p); /* flags */
2536 ThreadContext *nt = get_thread (ctx, ptr_base + ptrdiff);
2537 /* no subtype means it's a name event, rather than start/stop */
2538 if (subtype == 0)
2539 nt->name = pstrdup ((char*)p);
2540 if (debug) {
2541 if (subtype == 0)
2542 fprintf (outfile, "thread %p named at %llu: %s\n", (void*)(ptr_base + ptrdiff), (unsigned long long) time_base, p);
2543 else
2544 fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
2546 if (subtype == 0) {
2547 while (*p) p++;
2548 p++;
2551 break;
2553 case TYPE_ALLOC: {
2554 int has_bt = *p & TYPE_ALLOC_BT;
2555 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2556 intptr_t ptrdiff = decode_sleb128 (p, &p);
2557 intptr_t objdiff = decode_sleb128 (p, &p);
2558 uint64_t len;
2559 int num_bt = 0;
2560 MethodDesc* sframes [8];
2561 MethodDesc** frames = sframes;
2562 ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
2563 len = decode_uleb128 (p, &p);
2564 LOG_TIME (time_base, tdiff);
2565 time_base += tdiff;
2566 if (debug)
2567 fprintf (outfile, "alloced object %p, size %llu (%s) at %llu\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) len, lookup_class (ptr_base + ptrdiff)->name, (unsigned long long) time_base);
2568 if (has_bt) {
2569 num_bt = 8;
2570 frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2571 if (!frames) {
2572 fprintf (outfile, "Cannot load backtrace\n");
2573 return 0;
2576 if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2577 BackTrace *bt;
2578 cd->allocs++;
2579 cd->alloc_size += len;
2580 if (has_bt)
2581 bt = add_trace_methods (frames, num_bt, &cd->traces, len);
2582 else
2583 bt = add_trace_thread (thread, &cd->traces, len);
2584 if (find_size && len >= find_size) {
2585 if (!find_name || strstr (cd->name, find_name))
2586 found_object (OBJ_ADDR (objdiff));
2587 } else if (!find_size && find_name && strstr (cd->name, find_name)) {
2588 found_object (OBJ_ADDR (objdiff));
2590 if (num_tracked_objects)
2591 tracked_creation (OBJ_ADDR (objdiff), cd, len, bt, time_base);
2593 if (frames != sframes)
2594 g_free (frames);
2595 break;
2597 case TYPE_METHOD: {
2598 int subtype = *p & 0xf0;
2599 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2600 int64_t ptrdiff = decode_sleb128 (p, &p);
2601 LOG_TIME (time_base, tdiff);
2602 time_base += tdiff;
2603 method_base += ptrdiff;
2604 if (subtype == TYPE_JIT) {
2605 intptr_t codediff = decode_sleb128 (p, &p);
2606 int codelen = decode_uleb128 (p, &p);
2607 MethodDesc *jitted_method;
2608 if (debug)
2609 fprintf (outfile, "jitted method %p (%s), size: %d, code: %p\n", (void*)(method_base), p, codelen, (void*)(ptr_base + codediff));
2610 jitted_method = add_method (method_base, (char*)p, ptr_base + codediff, codelen);
2611 if (!(time_base >= time_from && time_base < time_to))
2612 jitted_method->ignore_jit = 1;
2613 while (*p) p++;
2614 p++;
2615 } else {
2616 MethodDesc *method;
2617 if ((thread_filter && thread_filter != thread->thread_id))
2618 break;
2619 if (!(time_base >= time_from && time_base < time_to))
2620 break;
2621 method = lookup_method (method_base);
2622 if (subtype == TYPE_ENTER) {
2623 add_trace_thread (thread, &method->traces, 1);
2624 push_method (thread, method, time_base);
2625 } else {
2626 pop_method (thread, method, time_base);
2628 if (debug)
2629 fprintf (outfile, "%s method %s\n", subtype == TYPE_ENTER? "enter": subtype == TYPE_EXC_LEAVE? "exleave": "leave", method->name);
2631 break;
2633 case TYPE_HEAP: {
2634 int subtype = *p & 0xf0;
2635 if (subtype == TYPE_HEAP_OBJECT) {
2636 HeapObjectDesc *ho = NULL;
2637 int i;
2638 intptr_t objdiff;
2639 if (ctx->data_version > 12) {
2640 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2641 LOG_TIME (time_base, tdiff);
2642 time_base += tdiff;
2643 objdiff = decode_sleb128 (p, &p);
2644 } else
2645 objdiff = decode_sleb128 (p + 1, &p);
2646 intptr_t ptrdiff = decode_sleb128 (p, &p);
2647 uint64_t size = decode_uleb128 (p, &p);
2648 uintptr_t num = decode_uleb128 (p, &p);
2649 uintptr_t ref_offset = 0;
2650 uintptr_t last_obj_offset = 0;
2651 ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
2652 if (size) {
2653 HeapClassDesc *hcd = add_heap_shot_class (thread->current_heap_shot, cd, size);
2654 if (collect_traces) {
2655 ho = alloc_heap_obj (OBJ_ADDR (objdiff), hcd, num);
2656 add_heap_shot_obj (thread->current_heap_shot, ho);
2657 ref_offset = 0;
2659 } else {
2660 if (collect_traces)
2661 ho = heap_shot_obj_add_refs (thread->current_heap_shot, OBJ_ADDR (objdiff), num, &ref_offset);
2663 for (i = 0; i < num; ++i) {
2664 /* FIXME: use object distance to measure how good
2665 * the GC is at keeping related objects close
2667 uintptr_t offset = ctx->data_version > 1? last_obj_offset + decode_uleb128 (p, &p): -1;
2668 intptr_t obj1diff = decode_sleb128 (p, &p);
2669 last_obj_offset = offset;
2670 if (collect_traces)
2671 ho->refs [ref_offset + i] = OBJ_ADDR (obj1diff);
2672 if (num_tracked_objects)
2673 track_obj_reference (OBJ_ADDR (obj1diff), OBJ_ADDR (objdiff), cd);
2675 if (debug && size)
2676 fprintf (outfile, "traced object %p, size %llu (%s), refs: %zd\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) size, cd->name, num);
2677 } else if (subtype == TYPE_HEAP_ROOT) {
2678 uintptr_t num;
2679 if (ctx->data_version > 12) {
2680 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2681 LOG_TIME (time_base, tdiff);
2682 time_base += tdiff;
2683 num = decode_uleb128 (p, &p);
2684 } else
2685 num = decode_uleb128 (p + 1, &p);
2686 uintptr_t gc_num G_GNUC_UNUSED = decode_uleb128 (p, &p);
2687 int i;
2688 for (i = 0; i < num; ++i) {
2689 intptr_t objdiff = decode_sleb128 (p, &p);
2690 int root_type;
2691 if (ctx->data_version > 12)
2692 root_type = *p++;
2693 else
2694 root_type = decode_uleb128 (p, &p);
2695 /* we just discard the extra info for now */
2696 uintptr_t extra_info = decode_uleb128 (p, &p);
2697 if (debug)
2698 fprintf (outfile, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff), get_root_name (root_type));
2699 if (collect_traces)
2700 thread_add_root (thread, OBJ_ADDR (objdiff), root_type, extra_info);
2702 } else if (subtype == TYPE_HEAP_END) {
2703 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2704 LOG_TIME (time_base, tdiff);
2705 time_base += tdiff;
2706 if (debug)
2707 fprintf (outfile, "heap shot end\n");
2708 if (collect_traces) {
2709 HeapShot *hs = thread->current_heap_shot;
2710 if (hs && thread->num_roots) {
2711 /* transfer the root ownershipt to the heapshot */
2712 hs->num_roots = thread->num_roots;
2713 hs->roots = thread->roots;
2714 hs->roots_extra = thread->roots_extra;
2715 hs->roots_types = thread->roots_types;
2716 } else {
2717 g_free (thread->roots);
2718 g_free (thread->roots_extra);
2719 g_free (thread->roots_types);
2721 thread->num_roots = 0;
2722 thread->size_roots = 0;
2723 thread->roots = NULL;
2724 thread->roots_extra = NULL;
2725 thread->roots_types = NULL;
2726 heap_shot_resolve_reverse_refs (hs);
2727 heap_shot_mark_objects (hs);
2728 heap_shot_free_objects (hs);
2730 thread->current_heap_shot = NULL;
2731 } else if (subtype == TYPE_HEAP_START) {
2732 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2733 LOG_TIME (time_base, tdiff);
2734 time_base += tdiff;
2735 if (debug)
2736 fprintf (outfile, "heap shot start\n");
2737 thread->current_heap_shot = new_heap_shot (time_base);
2739 break;
2741 case TYPE_MONITOR: {
2742 int event = (*p >> 4) & 0x3;
2743 int has_bt = *p & TYPE_MONITOR_BT;
2744 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2745 intptr_t objdiff = decode_sleb128 (p, &p);
2746 MethodDesc* sframes [8];
2747 MethodDesc** frames = sframes;
2748 int record;
2749 int num_bt = 0;
2750 LOG_TIME (time_base, tdiff);
2751 time_base += tdiff;
2752 record = (!thread_filter || thread_filter == thread->thread_id);
2753 if (!(time_base >= time_from && time_base < time_to))
2754 record = 0;
2755 if (event == MONO_PROFILER_MONITOR_CONTENTION) {
2756 MonitorDesc *mdesc = lookup_monitor (OBJ_ADDR (objdiff));
2757 if (record) {
2758 monitor_contention++;
2759 mdesc->contentions++;
2760 thread->monitor = mdesc;
2761 thread->contention_start = time_base;
2763 if (has_bt) {
2764 num_bt = 8;
2765 frames = decode_bt (ctx, sframes, &num_bt, p, &p, ptr_base, &method_base);
2766 if (!frames) {
2767 fprintf (outfile, "Cannot load backtrace\n");
2768 return 0;
2770 if (record)
2771 add_trace_methods (frames, num_bt, &mdesc->traces, 1);
2772 } else {
2773 if (record)
2774 add_trace_thread (thread, &mdesc->traces, 1);
2776 } else if (event == MONO_PROFILER_MONITOR_FAIL) {
2777 if (record) {
2778 monitor_failed++;
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;
2788 } else if (event == MONO_PROFILER_MONITOR_DONE) {
2789 if (record) {
2790 monitor_acquired++;
2791 if (thread->monitor && thread->contention_start) {
2792 uint64_t wait_time = time_base - thread->contention_start;
2793 if (wait_time > thread->monitor->max_wait_time)
2794 thread->monitor->max_wait_time = wait_time;
2795 thread->monitor->wait_time += wait_time;
2796 thread->monitor = NULL;
2797 thread->contention_start = 0;
2801 if (debug)
2802 fprintf (outfile, "monitor %s for object %p\n", monitor_ev_name (event), (void*)OBJ_ADDR (objdiff));
2803 if (frames != sframes)
2804 g_free (frames);
2805 break;
2807 case TYPE_EXCEPTION: {
2808 int subtype = *p & 0x70;
2809 int has_bt = *p & TYPE_THROW_BT;
2810 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2811 MethodDesc* sframes [8];
2812 MethodDesc** frames = sframes;
2813 int record;
2814 LOG_TIME (time_base, tdiff);
2815 time_base += tdiff;
2816 record = (!thread_filter || thread_filter == thread->thread_id);
2817 if (!(time_base >= time_from && time_base < time_to))
2818 record = 0;
2819 if (subtype == TYPE_CLAUSE) {
2820 int clause_type;
2821 if (ctx->data_version > 12)
2822 clause_type = *p++;
2823 else
2824 clause_type = decode_uleb128 (p, &p);
2825 int clause_num = decode_uleb128 (p, &p);
2826 int64_t ptrdiff = decode_sleb128 (p, &p);
2827 method_base += ptrdiff;
2828 if (record)
2829 clause_summary [clause_type]++;
2830 if (debug)
2831 fprintf (outfile, "clause %s (%d) in method %s\n", clause_name (clause_type), clause_num, lookup_method (method_base)->name);
2832 } else {
2833 intptr_t objdiff = decode_sleb128 (p, &p);
2834 if (record)
2835 throw_count++;
2836 if (has_bt) {
2837 has_bt = 8;
2838 frames = decode_bt (ctx, sframes, &has_bt, p, &p, ptr_base, &method_base);
2839 if (!frames) {
2840 fprintf (outfile, "Cannot load backtrace\n");
2841 return 0;
2843 if (record)
2844 add_trace_methods (frames, has_bt, &exc_traces, 1);
2845 } else {
2846 if (record)
2847 add_trace_thread (thread, &exc_traces, 1);
2849 if (frames != sframes)
2850 g_free (frames);
2851 if (debug)
2852 fprintf (outfile, "throw %p\n", (void*)OBJ_ADDR (objdiff));
2854 break;
2856 case TYPE_RUNTIME: {
2857 int subtype = *p & 0xf0;
2858 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2859 LOG_TIME (time_base, tdiff);
2860 time_base += tdiff;
2861 if (subtype == TYPE_JITHELPER) {
2862 int type;
2863 if (ctx->data_version > 12)
2864 type = *p++;
2865 else
2866 type = decode_uleb128 (p, &p);
2867 intptr_t codediff = decode_sleb128 (p, &p);
2868 int codelen = decode_uleb128 (p, &p);
2869 const char *name;
2870 if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
2871 name = (const char *)p;
2872 while (*p) p++;
2873 p++;
2874 } else {
2875 name = code_buffer_desc (type);
2877 num_jit_helpers++;
2878 jit_helpers_code_size += codelen;
2879 if (debug)
2880 fprintf (outfile, "jit helper %s, size: %d, code: %p\n", name, codelen, (void*)(ptr_base + codediff));
2882 break;
2884 case TYPE_SAMPLE: {
2885 int subtype = *p & 0xf0;
2886 if (subtype == TYPE_SAMPLE_HIT) {
2887 int i;
2888 int sample_type;
2889 uint64_t tstamp;
2890 if (ctx->data_version > 12) {
2891 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2892 LOG_TIME (time_base, tdiff);
2893 time_base += tdiff;
2894 sample_type = *p++;
2895 tstamp = time_base;
2896 } else {
2897 sample_type = decode_uleb128 (p + 1, &p);
2898 tstamp = decode_uleb128 (p, &p);
2900 void *tid = (void *) thread_id;
2901 if (ctx->data_version > 10)
2902 tid = (void *) (ptr_base + decode_sleb128 (p, &p));
2903 int count = decode_uleb128 (p, &p);
2904 for (i = 0; i < count; ++i) {
2905 uintptr_t ip = ptr_base + decode_sleb128 (p, &p);
2906 if ((tstamp >= time_from && tstamp < time_to))
2907 add_stat_sample (sample_type, ip);
2908 if (debug)
2909 fprintf (outfile, "sample hit, type: %d at %p for thread %p\n", sample_type, (void*)ip, tid);
2911 if (ctx->data_version > 5) {
2912 count = decode_uleb128 (p, &p);
2913 for (i = 0; i < count; ++i) {
2914 MethodDesc *method;
2915 int64_t ptrdiff = decode_sleb128 (p, &p);
2916 method_base += ptrdiff;
2917 method = lookup_method (method_base);
2918 if (debug)
2919 fprintf (outfile, "sample hit bt %d: %s\n", i, method->name);
2920 if (ctx->data_version < 13) {
2921 decode_sleb128 (p, &p); /* il offset */
2922 decode_sleb128 (p, &p); /* native offset */
2926 } else if (subtype == TYPE_SAMPLE_USYM) {
2927 /* un unmanaged symbol description */
2928 uintptr_t addr;
2929 if (ctx->data_version > 12) {
2930 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2931 LOG_TIME (time_base, tdiff);
2932 time_base += tdiff;
2933 addr = ptr_base + decode_sleb128 (p, &p);
2934 } else
2935 addr = ptr_base + decode_sleb128 (p + 1, &p);
2936 uintptr_t size = decode_uleb128 (p, &p);
2937 char *name;
2938 name = pstrdup ((char*)p);
2939 add_unmanaged_symbol (addr, name, size);
2940 if (debug)
2941 fprintf (outfile, "unmanaged symbol %s at %p\n", name, (void*)addr);
2942 while (*p) p++;
2943 p++;
2944 } else if (subtype == TYPE_SAMPLE_UBIN) {
2945 /* un unmanaged binary loaded in memory */
2946 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2947 uintptr_t addr = decode_sleb128 (p, &p);
2948 uint64_t offset G_GNUC_UNUSED = decode_uleb128 (p, &p);
2949 uintptr_t size = decode_uleb128 (p, &p);
2950 char *name;
2951 LOG_TIME (time_base, tdiff);
2952 time_base += tdiff;
2953 name = pstrdup ((char*)p);
2954 add_unmanaged_binary (addr, name, size);
2955 if (debug)
2956 fprintf (outfile, "unmanaged binary %s at %p\n", name, (void*)addr);
2957 while (*p) p++;
2958 p++;
2959 } else if (subtype == TYPE_SAMPLE_COUNTERS_DESC) {
2960 uint64_t i, len;
2961 if (ctx->data_version > 12) {
2962 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2963 LOG_TIME (time_base, tdiff);
2964 time_base += tdiff;
2965 len = decode_uleb128 (p, &p);
2966 } else
2967 len = decode_uleb128 (p + 1, &p);
2968 for (i = 0; i < len; i++) {
2969 uint64_t type, unit, variance, index;
2970 uint64_t section = decode_uleb128 (p, &p);
2971 char *section_str, *name;
2972 if (section != MONO_COUNTER_PERFCOUNTERS) {
2973 section_str = (char*) section_name (section);
2974 } else {
2975 section_str = pstrdup ((char*)p);
2976 while (*p++);
2978 name = pstrdup ((char*)p);
2979 while (*p++);
2980 if (ctx->data_version > 12) {
2981 type = *p++;
2982 unit = *p++;
2983 variance = *p++;
2984 } else {
2985 type = decode_uleb128 (p, &p);
2986 unit = decode_uleb128 (p, &p);
2987 variance = decode_uleb128 (p, &p);
2989 index = decode_uleb128 (p, &p);
2990 add_counter (section_str, name, (int)type, (int)unit, (int)variance, (int)index);
2992 } else if (subtype == TYPE_SAMPLE_COUNTERS) {
2993 int i;
2994 CounterValue *value, *previous = NULL;
2995 CounterList *list;
2996 uint64_t timestamp; // milliseconds since startup
2997 if (ctx->data_version > 12) {
2998 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2999 LOG_TIME (time_base, tdiff);
3000 time_base += tdiff;
3001 timestamp = (time_base - startup_time) / 1000 / 1000;
3002 } else
3003 timestamp = decode_uleb128 (p + 1, &p);
3004 uint64_t time_between = timestamp / 1000 * 1000 * 1000 * 1000 + startup_time;
3005 while (1) {
3006 uint64_t type, index = decode_uleb128 (p, &p);
3007 if (index == 0)
3008 break;
3010 for (list = counters; list; list = list->next) {
3011 if (list->counter->index == (int)index) {
3012 previous = list->counter->values_last;
3013 break;
3017 if (ctx->data_version > 12)
3018 type = *p++;
3019 else
3020 type = decode_uleb128 (p, &p);
3022 value = (CounterValue *) g_calloc (1, sizeof (CounterValue));
3023 value->timestamp = timestamp;
3025 switch (type) {
3026 case MONO_COUNTER_INT:
3027 #if SIZEOF_VOID_P == 4
3028 case MONO_COUNTER_WORD:
3029 #endif
3030 value->buffer = (unsigned char *)g_malloc (sizeof (int32_t));
3031 *(int32_t*)value->buffer = (int32_t)decode_sleb128 (p, &p) + (previous ? (*(int32_t*)previous->buffer) : 0);
3032 break;
3033 case MONO_COUNTER_UINT:
3034 value->buffer = (unsigned char *) g_malloc (sizeof (uint32_t));
3035 *(uint32_t*)value->buffer = (uint32_t)decode_uleb128 (p, &p) + (previous ? (*(uint32_t*)previous->buffer) : 0);
3036 break;
3037 case MONO_COUNTER_LONG:
3038 #if SIZEOF_VOID_P == 8
3039 case MONO_COUNTER_WORD:
3040 #endif
3041 case MONO_COUNTER_TIME_INTERVAL:
3042 value->buffer = (unsigned char *) g_malloc (sizeof (int64_t));
3043 *(int64_t*)value->buffer = (int64_t)decode_sleb128 (p, &p) + (previous ? (*(int64_t*)previous->buffer) : 0);
3044 break;
3045 case MONO_COUNTER_ULONG:
3046 value->buffer = (unsigned char *) g_malloc (sizeof (uint64_t));
3047 *(uint64_t*)value->buffer = (uint64_t)decode_uleb128 (p, &p) + (previous ? (*(uint64_t*)previous->buffer) : 0);
3048 break;
3049 case MONO_COUNTER_DOUBLE:
3050 value->buffer = (unsigned char *) g_malloc (sizeof (double));
3051 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
3052 for (i = 0; i < sizeof (double); i++)
3053 #else
3054 for (i = sizeof (double) - 1; i >= 0; i--)
3055 #endif
3056 value->buffer[i] = *p++;
3057 break;
3058 case MONO_COUNTER_STRING:
3059 if (*p++ == 0) {
3060 value->buffer = NULL;
3061 } else {
3062 value->buffer = (unsigned char*) pstrdup ((char*)p);
3063 while (*p++);
3065 break;
3067 if (time_between >= time_from && time_between <= time_to)
3068 add_counter_value (index, value);
3070 } else {
3071 return 0;
3073 break;
3075 case TYPE_COVERAGE:{
3076 int subtype = *p & 0xf0;
3077 switch (subtype) {
3078 case TYPE_COVERAGE_METHOD: {
3079 CoverageMethod *method = g_new0 (CoverageMethod, 1);
3080 const char *assembly, *klass, *name, *sig, *filename;
3081 int token, n_offsets, method_id;
3083 p++;
3085 if (ctx->data_version > 12) {
3086 uint64_t tdiff = decode_uleb128 (p, &p);
3087 LOG_TIME (time_base, tdiff);
3088 time_base += tdiff;
3091 assembly = (const char *)p; while (*p) p++; p++;
3092 klass = (const char *)p; while (*p) p++; p++;
3093 name = (const char *)p; while (*p) p++; p++;
3094 sig = (const char *)p; while (*p) p++; p++;
3095 filename = (const char *)p; while (*p) p++; p++;
3097 token = decode_uleb128 (p, &p);
3098 method_id = decode_uleb128 (p, &p);
3099 n_offsets = decode_uleb128 (p, &p);
3101 method->assembly_name = g_strdup (assembly);
3102 method->class_name = g_strdup (klass);
3103 method->method_name = g_strdup (name);
3104 method->method_signature = g_strdup (sig);
3105 method->filename = g_strdup (filename);
3106 method->token = token;
3107 method->n_statements = n_offsets;
3108 method->coverage = g_ptr_array_new ();
3109 method->method_id = method_id;
3111 coverage_add_method (method);
3113 break;
3115 case TYPE_COVERAGE_STATEMENT: {
3116 CoverageCoverage *coverage = g_new0 (CoverageCoverage, 1);
3117 int offset, count, line, column, method_id;
3119 p++;
3121 if (ctx->data_version > 12) {
3122 uint64_t tdiff = decode_uleb128 (p, &p);
3123 LOG_TIME (time_base, tdiff);
3124 time_base += tdiff;
3127 method_id = decode_uleb128 (p, &p);
3128 offset = decode_uleb128 (p, &p);
3129 count = decode_uleb128 (p, &p);
3130 line = decode_uleb128 (p, &p);
3131 column = decode_uleb128 (p, &p);
3133 coverage->method_id = method_id;
3134 coverage->offset = offset;
3135 coverage->count = count;
3136 coverage->line = line;
3137 coverage->column = column;
3139 coverage_add_coverage (coverage);
3140 break;
3142 case TYPE_COVERAGE_ASSEMBLY: {
3143 CoverageAssembly *assembly = g_new0 (CoverageAssembly, 1);
3144 char *name, *guid, *filename;
3145 int number_of_methods, fully_covered, partially_covered;
3146 p++;
3148 if (ctx->data_version > 12) {
3149 uint64_t tdiff = decode_uleb128 (p, &p);
3150 LOG_TIME (time_base, tdiff);
3151 time_base += tdiff;
3154 name = (char *)p; while (*p) p++; p++;
3155 guid = (char *)p; while (*p) p++; p++;
3156 filename = (char *)p; while (*p) p++; p++;
3157 number_of_methods = decode_uleb128 (p, &p);
3158 fully_covered = decode_uleb128 (p, &p);
3159 partially_covered = decode_uleb128 (p, &p);
3161 assembly->name = g_strdup (name);
3162 assembly->guid = g_strdup (guid);
3163 assembly->filename = g_strdup (filename);
3164 assembly->number_of_methods = number_of_methods;
3165 assembly->fully_covered = fully_covered;
3166 assembly->partially_covered = partially_covered;
3168 coverage_add_assembly (assembly);
3169 break;
3171 case TYPE_COVERAGE_CLASS: {
3172 CoverageClass *klass = g_new0 (CoverageClass, 1);
3173 char *assembly_name, *class_name;
3174 int number_of_methods, fully_covered, partially_covered;
3175 p++;
3177 if (ctx->data_version > 12) {
3178 uint64_t tdiff = decode_uleb128 (p, &p);
3179 LOG_TIME (time_base, tdiff);
3180 time_base += tdiff;
3183 assembly_name = (char *)p; while (*p) p++; p++;
3184 class_name = (char *)p; while (*p) p++; p++;
3185 number_of_methods = decode_uleb128 (p, &p);
3186 fully_covered = decode_uleb128 (p, &p);
3187 partially_covered = decode_uleb128 (p, &p);
3189 klass->assembly_name = g_strdup (assembly_name);
3190 klass->class_name = g_strdup (class_name);
3191 klass->number_of_methods = number_of_methods;
3192 klass->fully_covered = fully_covered;
3193 klass->partially_covered = partially_covered;
3195 coverage_add_class (klass);
3196 break;
3199 default:
3200 break;
3202 break;
3204 case TYPE_META: {
3205 int subtype = *p & 0xf0;
3206 uint64_t tdiff = decode_uleb128 (p + 1, &p);
3207 LOG_TIME (time_base, tdiff);
3208 time_base += tdiff;
3209 if (subtype == TYPE_SYNC_POINT) {
3210 int type = *p++;
3211 if (debug)
3212 fprintf (outfile, "sync point %i (%s)\n", type, sync_point_name (type));
3214 break;
3216 default:
3217 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);
3218 exit (1);
3220 record_event_stats (event, p - start);
3222 thread->last_time = time_base;
3223 for (i = 0; i < thread->stack_id; ++i)
3224 thread->stack [i]->recurse_count = 0;
3225 return 1;
3228 static int
3229 read_header_string (ProfContext *ctx, char **field)
3231 if (!load_data (ctx, 4))
3232 return 0;
3234 if (!load_data (ctx, read_int32 (ctx->buf)))
3235 return 0;
3237 *field = pstrdup ((const char *) ctx->buf);
3239 return 1;
3242 static ProfContext*
3243 load_file (char *name)
3245 unsigned char *p;
3246 ProfContext *ctx = (ProfContext *) g_calloc (sizeof (ProfContext), 1);
3247 if (strcmp (name, "-") == 0)
3248 ctx->file = stdin;
3249 else
3250 ctx->file = fopen (name, "rb");
3251 if (!ctx->file) {
3252 printf ("Cannot open file: %s\n", name);
3253 exit (1);
3255 #if defined (HAVE_SYS_ZLIB)
3256 if (ctx->file != stdin)
3257 ctx->gzfile = gzdopen (fileno (ctx->file), "rb");
3258 #endif
3259 if (!load_data (ctx, 30))
3260 return NULL;
3261 p = ctx->buf;
3262 if (read_int32 (p) != LOG_HEADER_ID || p [6] > LOG_DATA_VERSION)
3263 return NULL;
3264 ctx->version_major = p [4];
3265 ctx->version_minor = p [5];
3266 ctx->data_version = p [6];
3267 /* reading 64 bit files on 32 bit systems not supported yet */
3268 if (p [7] > sizeof (void*))
3269 return NULL;
3270 if (read_int32 (p + 20)) /* flags must be 0 */
3271 return NULL;
3272 ctx->startup_time = read_int64 (p + 8);
3273 ctx->timer_overhead = read_int32 (p + 16);
3274 ctx->pid = read_int32 (p + 24);
3275 ctx->port = read_int16 (p + 28);
3276 if (ctx->version_major >= 1) {
3277 if (!read_header_string (ctx, &ctx->args))
3278 return NULL;
3279 if (!read_header_string (ctx, &ctx->arch))
3280 return NULL;
3281 if (!read_header_string (ctx, &ctx->os))
3282 return NULL;
3283 } else {
3284 if (!load_data (ctx, 2)) /* old opsys field, was never used */
3285 return NULL;
3287 return ctx;
3290 enum {
3291 ALLOC_SORT_BYTES,
3292 ALLOC_SORT_COUNT
3294 static int alloc_sort_mode = ALLOC_SORT_BYTES;
3296 static int
3297 compare_class (const void *a, const void *b)
3299 ClassDesc *const *A = (ClassDesc *const *)a;
3300 ClassDesc *const *B = (ClassDesc *const *)b;
3301 uint64_t vala, valb;
3302 if (alloc_sort_mode == ALLOC_SORT_BYTES) {
3303 vala = (*A)->alloc_size;
3304 valb = (*B)->alloc_size;
3305 } else {
3306 vala = (*A)->allocs;
3307 valb = (*B)->allocs;
3309 if (valb == vala)
3310 return 0;
3311 if (valb < vala)
3312 return -1;
3313 return 1;
3316 static void
3317 dump_header (ProfContext *ctx)
3319 time_t st = ctx->startup_time / 1000;
3320 char *t = ctime (&st);
3321 fprintf (outfile, "\nMono log profiler data\n");
3322 fprintf (outfile, "\tProfiler version: %d.%d\n", ctx->version_major, ctx->version_minor);
3323 fprintf (outfile, "\tData version: %d\n", ctx->data_version);
3324 if (ctx->version_major >= 1) {
3325 fprintf (outfile, "\tArguments: %s\n", ctx->args);
3326 fprintf (outfile, "\tArchitecture: %s\n", ctx->arch);
3327 fprintf (outfile, "\tOperating system: %s\n", ctx->os);
3329 fprintf (outfile, "\tMean timer overhead: %d nanoseconds\n", ctx->timer_overhead);
3330 fprintf (outfile, "\tProgram startup: %s", t);
3331 if (ctx->pid)
3332 fprintf (outfile, "\tProgram ID: %d\n", ctx->pid);
3333 if (ctx->port)
3334 fprintf (outfile, "\tServer listening on: %d\n", ctx->port);
3337 static void
3338 dump_traces (TraceDesc *traces, const char *desc)
3340 int j;
3341 if (!show_traces)
3342 return;
3343 if (!traces->count)
3344 return;
3345 sort_context_array (traces);
3346 for (j = 0; j < traces->count; ++j) {
3347 int k;
3348 BackTrace *bt;
3349 bt = traces->traces [j].bt;
3350 if (!bt->count)
3351 continue;
3352 fprintf (outfile, "\t%llu %s from:\n", (unsigned long long) traces->traces [j].count, desc);
3353 for (k = 0; k < bt->count; ++k)
3354 fprintf (outfile, "\t\t%s\n", bt->methods [k]->name);
3358 static void
3359 dump_threads (ProfContext *ctx)
3361 ThreadContext *thread;
3362 fprintf (outfile, "\nThread summary\n");
3363 for (thread = ctx->threads; thread; thread = thread->next) {
3364 if (thread->thread_id) {
3365 fprintf (outfile, "\tThread: %p, name: \"%s\"\n", (void*)thread->thread_id, thread->name? thread->name: "");
3370 static void
3371 dump_domains (ProfContext *ctx)
3373 fprintf (outfile, "\nDomain summary\n");
3375 for (DomainContext *domain = ctx->domains; domain; domain = domain->next)
3376 fprintf (outfile, "\tDomain: %p, friendly name: \"%s\"\n", (void *) domain->domain_id, domain->friendly_name);
3379 static void
3380 dump_remctxs (ProfContext *ctx)
3382 fprintf (outfile, "\nContext summary\n");
3384 for (RemCtxContext *remctx = ctx->remctxs; remctx; remctx = remctx->next)
3385 fprintf (outfile, "\tContext: %p, domain: %p\n", (void *) remctx->remctx_id, (void *) remctx->domain_id);
3388 static void
3389 dump_exceptions (void)
3391 int i;
3392 fprintf (outfile, "\nException summary\n");
3393 fprintf (outfile, "\tThrows: %llu\n", (unsigned long long) throw_count);
3394 dump_traces (&exc_traces, "throws");
3395 for (i = 0; i <= MONO_EXCEPTION_CLAUSE_FAULT; ++i) {
3396 if (!clause_summary [i])
3397 continue;
3398 fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), (unsigned long long) clause_summary [i]);
3402 static int
3403 compare_monitor (const void *a, const void *b)
3405 MonitorDesc *const *A = (MonitorDesc *const *)a;
3406 MonitorDesc *const *B = (MonitorDesc *const *)b;
3407 if ((*B)->wait_time == (*A)->wait_time)
3408 return 0;
3409 if ((*B)->wait_time < (*A)->wait_time)
3410 return -1;
3411 return 1;
3414 static void
3415 dump_monitors (void)
3417 MonitorDesc **monitors;
3418 int i, j;
3419 if (!num_monitors)
3420 return;
3421 monitors = (MonitorDesc **) g_malloc (sizeof (void*) * num_monitors);
3422 for (i = 0, j = 0; i < SMALL_HASH_SIZE; ++i) {
3423 MonitorDesc *mdesc = monitor_hash [i];
3424 while (mdesc) {
3425 monitors [j++] = mdesc;
3426 mdesc = mdesc->next;
3429 qsort (monitors, num_monitors, sizeof (void*), compare_monitor);
3430 fprintf (outfile, "\nMonitor lock summary\n");
3431 for (i = 0; i < num_monitors; ++i) {
3432 MonitorDesc *mdesc = monitors [i];
3433 fprintf (outfile, "\tLock object %p: %d contentions\n", (void*)mdesc->objid, (int)mdesc->contentions);
3434 fprintf (outfile, "\t\t%.6f secs total wait time, %.6f max, %.6f average\n",
3435 mdesc->wait_time/1000000000.0, mdesc->max_wait_time/1000000000.0, mdesc->wait_time/1000000000.0/mdesc->contentions);
3436 dump_traces (&mdesc->traces, "contentions");
3438 fprintf (outfile, "\tLock contentions: %llu\n", (unsigned long long) monitor_contention);
3439 fprintf (outfile, "\tLock acquired: %llu\n", (unsigned long long) monitor_acquired);
3440 fprintf (outfile, "\tLock failures: %llu\n", (unsigned long long) monitor_failed);
3443 static void
3444 dump_gcs (void)
3446 int i;
3447 fprintf (outfile, "\nGC summary\n");
3448 fprintf (outfile, "\tGC resizes: %d\n", gc_resizes);
3449 fprintf (outfile, "\tMax heap size: %llu\n", (unsigned long long) max_heap_size);
3450 fprintf (outfile, "\tObject moves: %llu\n", (unsigned long long) gc_object_moves);
3451 for (i = 0; i < 3; ++i) {
3452 if (!gc_info [i].count)
3453 continue;
3454 fprintf (outfile, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
3455 i, gc_info [i].count,
3456 (unsigned long long) (gc_info [i].max_time / 1000),
3457 (unsigned long long) (gc_info [i].total_time / 1000),
3458 (unsigned long long) (gc_info [i].total_time / gc_info [i].count / 1000));
3460 for (i = 0; i < 3; ++i) {
3461 if (!handle_info [i].max_live)
3462 continue;
3463 fprintf (outfile, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
3464 get_handle_name (i),
3465 (unsigned long long) (handle_info [i].created),
3466 (unsigned long long) (handle_info [i].destroyed),
3467 (unsigned long long) (handle_info [i].max_live));
3468 dump_traces (&handle_info [i].traces, "created");
3469 dump_traces (&handle_info [i].destroy_traces, "destroyed");
3473 static void
3474 dump_jit (void)
3476 int i;
3477 int code_size = 0;
3478 int compiled_methods = 0;
3479 MethodDesc* m;
3480 fprintf (outfile, "\nJIT summary\n");
3481 for (i = 0; i < HASH_SIZE; ++i) {
3482 m = method_hash [i];
3483 for (m = method_hash [i]; m; m = m->next) {
3484 if (!m->code || m->ignore_jit)
3485 continue;
3486 compiled_methods++;
3487 code_size += m->len;
3490 fprintf (outfile, "\tCompiled methods: %d\n", compiled_methods);
3491 fprintf (outfile, "\tGenerated code size: %d\n", code_size);
3492 fprintf (outfile, "\tJIT helpers: %d\n", num_jit_helpers);
3493 fprintf (outfile, "\tJIT helpers code size: %d\n", jit_helpers_code_size);
3496 static void
3497 dump_allocations (void)
3499 int i, c;
3500 intptr_t allocs = 0;
3501 uint64_t size = 0;
3502 int header_done = 0;
3503 ClassDesc **classes = (ClassDesc **) g_malloc (num_classes * sizeof (void*));
3504 ClassDesc *cd;
3505 c = 0;
3506 for (i = 0; i < HASH_SIZE; ++i) {
3507 cd = class_hash [i];
3508 while (cd) {
3509 classes [c++] = cd;
3510 cd = cd->next;
3513 qsort (classes, num_classes, sizeof (void*), compare_class);
3514 for (i = 0; i < num_classes; ++i) {
3515 cd = classes [i];
3516 if (!cd->allocs)
3517 continue;
3518 allocs += cd->allocs;
3519 size += cd->alloc_size;
3520 if (!header_done++) {
3521 fprintf (outfile, "\nAllocation summary\n");
3522 fprintf (outfile, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
3524 fprintf (outfile, "%10llu %10zd %8llu %s\n",
3525 (unsigned long long) (cd->alloc_size),
3526 cd->allocs,
3527 (unsigned long long) (cd->alloc_size / cd->allocs),
3528 cd->name);
3529 dump_traces (&cd->traces, "bytes");
3531 if (allocs)
3532 fprintf (outfile, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size, allocs);
3535 enum {
3536 METHOD_SORT_TOTAL,
3537 METHOD_SORT_SELF,
3538 METHOD_SORT_CALLS
3541 static int method_sort_mode = METHOD_SORT_TOTAL;
3543 static int
3544 compare_method (const void *a, const void *b)
3546 MethodDesc *const *A = (MethodDesc *const *)a;
3547 MethodDesc *const *B = (MethodDesc *const *)b;
3548 uint64_t vala, valb;
3549 if (method_sort_mode == METHOD_SORT_SELF) {
3550 vala = (*A)->self_time;
3551 valb = (*B)->self_time;
3552 } else if (method_sort_mode == METHOD_SORT_CALLS) {
3553 vala = (*A)->calls;
3554 valb = (*B)->calls;
3555 } else {
3556 vala = (*A)->total_time;
3557 valb = (*B)->total_time;
3559 if (vala == valb)
3560 return 0;
3561 if (valb < vala)
3562 return -1;
3563 return 1;
3566 static void
3567 dump_metadata (void)
3569 fprintf (outfile, "\nMetadata summary\n");
3570 fprintf (outfile, "\tLoaded images: %d\n", num_images);
3571 if (verbose) {
3572 ImageDesc *image;
3573 int i;
3574 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
3575 image = image_hash [i];
3576 while (image) {
3577 fprintf (outfile, "\t\t%s\n", image->filename);
3578 image = image->next;
3582 fprintf (outfile, "\tLoaded assemblies: %d\n", num_assemblies);
3583 if (verbose) {
3584 AssemblyDesc *assembly;
3585 int i;
3586 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
3587 assembly = assembly_hash [i];
3588 while (assembly) {
3589 fprintf (outfile, "\t\t%s\n", assembly->asmname);
3590 assembly = assembly->next;
3596 static void
3597 dump_methods (void)
3599 int i, c;
3600 uint64_t calls = 0;
3601 int header_done = 0;
3602 MethodDesc **methods = (MethodDesc **) g_malloc (num_methods * sizeof (void*));
3603 MethodDesc *cd;
3604 c = 0;
3605 for (i = 0; i < HASH_SIZE; ++i) {
3606 cd = method_hash [i];
3607 while (cd) {
3608 cd->total_time = cd->self_time + cd->callee_time;
3609 methods [c++] = cd;
3610 cd = cd->next;
3613 qsort (methods, num_methods, sizeof (void*), compare_method);
3614 for (i = 0; i < num_methods; ++i) {
3615 uint64_t msecs;
3616 uint64_t smsecs;
3617 cd = methods [i];
3618 if (!cd->calls)
3619 continue;
3620 calls += cd->calls;
3621 msecs = cd->total_time / 1000000;
3622 smsecs = (cd->total_time - cd->callee_time) / 1000000;
3623 if (!msecs && !verbose)
3624 continue;
3625 if (!header_done++) {
3626 fprintf (outfile, "\nMethod call summary\n");
3627 fprintf (outfile, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
3629 fprintf (outfile, "%8llu %8llu %10llu %s\n",
3630 (unsigned long long) (msecs),
3631 (unsigned long long) (smsecs),
3632 (unsigned long long) (cd->calls),
3633 cd->name);
3634 dump_traces (&cd->traces, "calls");
3636 if (calls)
3637 fprintf (outfile, "Total calls: %llu\n", (unsigned long long) calls);
3640 static int
3641 compare_heap_class (const void *a, const void *b)
3643 HeapClassDesc *const *A = (HeapClassDesc *const *)a;
3644 HeapClassDesc *const *B = (HeapClassDesc *const *)b;
3645 uint64_t vala, valb;
3646 if (alloc_sort_mode == ALLOC_SORT_BYTES) {
3647 vala = (*A)->total_size;
3648 valb = (*B)->total_size;
3649 } else {
3650 vala = (*A)->count;
3651 valb = (*B)->count;
3653 if (valb == vala)
3654 return 0;
3655 if (valb < vala)
3656 return -1;
3657 return 1;
3660 static int
3661 compare_rev_class (const void *a, const void *b)
3663 const HeapClassRevRef *A = (const HeapClassRevRef *)a;
3664 const HeapClassRevRef *B = (const HeapClassRevRef *)b;
3665 if (B->count == A->count)
3666 return 0;
3667 if (B->count < A->count)
3668 return -1;
3669 return 1;
3672 static void
3673 dump_rev_claases (HeapClassRevRef *revs, int count)
3675 int j;
3676 if (!show_traces)
3677 return;
3678 if (!count)
3679 return;
3680 for (j = 0; j < count; ++j) {
3681 HeapClassDesc *cd = revs [j].klass;
3682 fprintf (outfile, "\t\t%llu references from: %s\n",
3683 (unsigned long long) (revs [j].count),
3684 cd->klass->name);
3688 static void
3689 heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
3691 uint64_t size = 0;
3692 uint64_t count = 0;
3693 int ccount = 0;
3694 int i;
3695 HeapClassDesc *cd;
3696 HeapClassDesc **sorted;
3697 sorted = (HeapClassDesc **) g_malloc (sizeof (void*) * hs->class_count);
3698 for (i = 0; i < hs->hash_size; ++i) {
3699 cd = hs->class_hash [i];
3700 if (!cd)
3701 continue;
3702 count += cd->count;
3703 size += cd->total_size;
3704 sorted [ccount++] = cd;
3706 hs->sorted = sorted;
3707 qsort (sorted, ccount, sizeof (void*), compare_heap_class);
3708 fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %zd\n",
3709 hs_num,
3710 (hs->timestamp - startup_time)/1000000000.0,
3711 (unsigned long long) (size),
3712 (unsigned long long) (count),
3713 ccount, hs->num_roots);
3714 if (!verbose && ccount > 30)
3715 ccount = 30;
3716 fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
3717 for (i = 0; i < ccount; ++i) {
3718 HeapClassRevRef *rev_sorted;
3719 int j, k;
3720 HeapClassDesc *ocd = NULL;
3721 cd = sorted [i];
3722 if (last_hs)
3723 ocd = heap_class_lookup (last_hs, cd->klass);
3724 fprintf (outfile, "\t%10llu %10llu %8llu %s",
3725 (unsigned long long) (cd->total_size),
3726 (unsigned long long) (cd->count),
3727 (unsigned long long) (cd->total_size / cd->count),
3728 cd->klass->name);
3729 if (ocd) {
3730 int64_t bdiff = cd->total_size - ocd->total_size;
3731 int64_t cdiff = cd->count - ocd->count;
3732 fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", (long long) bdiff, (long long) cdiff);
3733 } else {
3734 fprintf (outfile, "\n");
3736 if (!collect_traces)
3737 continue;
3738 rev_sorted = (HeapClassRevRef *) g_malloc (cd->rev_count * sizeof (HeapClassRevRef));
3739 k = 0;
3740 for (j = 0; j < cd->rev_hash_size; ++j) {
3741 if (cd->rev_hash [j].klass)
3742 rev_sorted [k++] = cd->rev_hash [j];
3744 assert (cd->rev_count == k);
3745 qsort (rev_sorted, cd->rev_count, sizeof (HeapClassRevRef), compare_rev_class);
3746 if (cd->root_references)
3747 fprintf (outfile, "\t\t%zd root references (%zd pinning)\n", cd->root_references, cd->pinned_references);
3748 dump_rev_claases (rev_sorted, cd->rev_count);
3749 g_free (rev_sorted);
3751 g_free (sorted);
3754 static int
3755 compare_heap_shots (const void *a, const void *b)
3757 HeapShot *const *A = (HeapShot *const *)a;
3758 HeapShot *const *B = (HeapShot *const *)b;
3759 if ((*B)->timestamp == (*A)->timestamp)
3760 return 0;
3761 if ((*B)->timestamp > (*A)->timestamp)
3762 return -1;
3763 return 1;
3766 static void
3767 dump_heap_shots (void)
3769 HeapShot **hs_sorted;
3770 HeapShot *hs;
3771 HeapShot *last_hs = NULL;
3772 int i;
3773 if (!heap_shots)
3774 return;
3775 hs_sorted = (HeapShot **) g_malloc (num_heap_shots * sizeof (void*));
3776 fprintf (outfile, "\nHeap shot summary\n");
3777 i = 0;
3778 for (hs = heap_shots; hs; hs = hs->next)
3779 hs_sorted [i++] = hs;
3780 qsort (hs_sorted, num_heap_shots, sizeof (void*), compare_heap_shots);
3781 for (i = 0; i < num_heap_shots; ++i) {
3782 hs = hs_sorted [i];
3783 heap_shot_summary (hs, i, last_hs);
3784 last_hs = hs;
3788 /* This is a very basic escape function that escapes < > and &
3789 Ideally we'd use g_markup_escape_string but that function isn't
3790 available in Mono's eglib. This was written without looking at the
3791 source of that function in glib. */
3792 static char *
3793 escape_string_for_xml (const char *string)
3795 GString *string_builder = g_string_new (NULL);
3796 const char *start, *p;
3798 start = p = string;
3799 while (*p) {
3800 while (*p && *p != '&' && *p != '<' && *p != '>')
3801 p++;
3803 g_string_append_len (string_builder, start, p - start);
3805 if (*p == '\0')
3806 break;
3808 switch (*p) {
3809 case '<':
3810 g_string_append (string_builder, "&lt;");
3811 break;
3813 case '>':
3814 g_string_append (string_builder, "&gt;");
3815 break;
3817 case '&':
3818 g_string_append (string_builder, "&amp;");
3819 break;
3821 default:
3822 break;
3825 p++;
3826 start = p;
3829 return g_string_free (string_builder, FALSE);
3832 static int
3833 sort_assemblies (gconstpointer a, gconstpointer b)
3835 CoverageAssembly *assembly_a = *(CoverageAssembly **)a;
3836 CoverageAssembly *assembly_b = *(CoverageAssembly **)b;
3838 if (assembly_a->name == NULL && assembly_b->name == NULL)
3839 return 0;
3840 else if (assembly_a->name == NULL)
3841 return -1;
3842 else if (assembly_b->name == NULL)
3843 return 1;
3845 return strcmp (assembly_a->name, assembly_b->name);
3848 static void
3849 dump_coverage (void)
3851 if (!coverage_methods && !coverage_assemblies)
3852 return;
3854 gather_coverage_statements ();
3855 fprintf (outfile, "\nCoverage Summary:\n");
3857 if (coverage_outfile) {
3858 fprintf (coverage_outfile, "<?xml version=\"1.0\"?>\n");
3859 fprintf (coverage_outfile, "<coverage version=\"0.3\">\n");
3862 g_ptr_array_sort (coverage_assemblies, sort_assemblies);
3864 for (guint i = 0; i < coverage_assemblies->len; i++) {
3865 CoverageAssembly *assembly = (CoverageAssembly *)coverage_assemblies->pdata[i];
3866 GPtrArray *classes;
3868 if (assembly->number_of_methods != 0) {
3869 int percentage = ((assembly->fully_covered + assembly->partially_covered) * 100) / assembly->number_of_methods;
3870 fprintf (outfile, "\t%s (%s) %d%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, percentage, assembly->number_of_methods, assembly->fully_covered);
3871 } else
3872 fprintf (outfile, "\t%s (%s) ?%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, assembly->number_of_methods, assembly->fully_covered);
3874 if (coverage_outfile) {
3875 char *escaped_name, *escaped_filename;
3876 escaped_name = escape_string_for_xml (assembly->name);
3877 escaped_filename = escape_string_for_xml (assembly->filename);
3879 fprintf (coverage_outfile, "\t<assembly name=\"%s\" guid=\"%s\" filename=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n", escaped_name, assembly->guid, escaped_filename, assembly->number_of_methods, assembly->fully_covered, assembly->partially_covered);
3881 g_free (escaped_name);
3882 g_free (escaped_filename);
3885 classes = (GPtrArray *)g_hash_table_lookup (coverage_assembly_classes, assembly->name);
3886 if (classes) {
3887 for (guint j = 0; j < classes->len; j++) {
3888 CoverageClass *klass = (CoverageClass *)classes->pdata [j];
3890 if (klass->number_of_methods > 0) {
3891 int percentage = ((klass->fully_covered + klass->partially_covered) * 100) / klass->number_of_methods;
3892 fprintf (outfile, "\t\t%s %d%% covered (%d methods - %d covered)\n", klass->class_name, percentage, klass->number_of_methods, klass->fully_covered);
3893 } else
3894 fprintf (outfile, "\t\t%s ?%% covered (%d methods - %d covered)\n", klass->class_name, klass->number_of_methods, klass->fully_covered);
3896 if (coverage_outfile) {
3897 char *escaped_name;
3898 escaped_name = escape_string_for_xml (klass->class_name);
3900 fprintf (coverage_outfile, "\t\t<class name=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n", escaped_name, klass->number_of_methods, klass->fully_covered, klass->partially_covered);
3901 g_free (escaped_name);
3907 for (guint i = 0; i < coverage_methods->len; i++) {
3908 CoverageMethod *method = (CoverageMethod *)coverage_methods->pdata [i];
3910 if (coverage_outfile) {
3911 char *escaped_assembly, *escaped_class, *escaped_method, *escaped_sig, *escaped_filename;
3913 escaped_assembly = escape_string_for_xml (method->assembly_name);
3914 escaped_class = escape_string_for_xml (method->class_name);
3915 escaped_method = escape_string_for_xml (method->method_name);
3916 escaped_sig = escape_string_for_xml (method->method_signature);
3917 escaped_filename = escape_string_for_xml (method->filename);
3919 fprintf (coverage_outfile, "\t<method assembly=\"%s\" class=\"%s\" name=\"%s (%s)\" filename=\"%s\" token=\"%d\">\n", escaped_assembly, escaped_class, escaped_method, escaped_sig, escaped_filename, method->token);
3921 g_free (escaped_assembly);
3922 g_free (escaped_class);
3923 g_free (escaped_method);
3924 g_free (escaped_sig);
3925 g_free (escaped_filename);
3927 for (guint j = 0; j < method->coverage->len; j++) {
3928 CoverageCoverage *coverage = (CoverageCoverage *)method->coverage->pdata [j];
3929 fprintf (coverage_outfile, "\t\t<statement offset=\"%d\" counter=\"%d\" line=\"%d\" column=\"%d\"/>\n", coverage->offset, coverage->count, coverage->line, coverage->column);
3931 fprintf (coverage_outfile, "\t</method>\n");
3935 if (coverage_outfile) {
3936 fprintf (coverage_outfile, "</coverage>\n");
3937 fclose (coverage_outfile);
3938 coverage_outfile = NULL;
3942 #define DUMP_EVENT_STAT(EVENT,SUBTYPE) dump_event (#EVENT, #SUBTYPE, EVENT, SUBTYPE);
3944 static void
3945 dump_event (const char *event_name, const char *subtype_name, int event, int subtype)
3947 int idx = event | subtype;
3948 EventStat evt = stats [idx];
3949 if (!evt.count)
3950 return;
3952 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);
3955 static void
3956 dump_stats (void)
3958 fprintf (outfile, "\nMlpd statistics\n");
3959 fprintf (outfile, "\tBuffer count %d\toverhead %d (%d bytes per header)\n", buffer_count, buffer_count * BUFFER_HEADER_SIZE, BUFFER_HEADER_SIZE);
3960 fprintf (outfile, "\nEvent details:\n");
3962 DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_NO_BT);
3963 DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_BT);
3965 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_EVENT);
3966 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_RESIZE);
3967 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_MOVE);
3968 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED);
3969 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED);
3970 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED_BT);
3971 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED_BT);
3973 DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_LOAD);
3974 DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_UNLOAD);
3976 DUMP_EVENT_STAT (TYPE_METHOD, TYPE_LEAVE);
3977 DUMP_EVENT_STAT (TYPE_METHOD, TYPE_ENTER);
3978 DUMP_EVENT_STAT (TYPE_METHOD, TYPE_EXC_LEAVE);
3979 DUMP_EVENT_STAT (TYPE_METHOD, TYPE_JIT);
3981 DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_THROW_NO_BT);
3982 DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_THROW_BT);
3983 DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_CLAUSE);
3985 DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_NO_BT);
3986 DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_BT);
3988 DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_START);
3989 DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_END);
3990 DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_OBJECT);
3991 DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_ROOT);
3993 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_HIT);
3994 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_USYM);
3995 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_UBIN);
3996 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS_DESC);
3997 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS);
3999 DUMP_EVENT_STAT (TYPE_RUNTIME, TYPE_JITHELPER);
4001 DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_ASSEMBLY);
4002 DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_METHOD);
4003 DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_STATEMENT);
4004 DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_CLASS);
4006 DUMP_EVENT_STAT (TYPE_META, TYPE_SYNC_POINT);
4011 static void
4012 flush_context (ProfContext *ctx)
4014 ThreadContext *thread;
4015 /* FIXME: sometimes there are leftovers: indagate */
4016 for (thread = ctx->threads; thread; thread = thread->next) {
4017 while (thread->stack_id) {
4018 if (debug)
4019 fprintf (outfile, "thread %p has %d items on stack\n", (void*)thread->thread_id, thread->stack_id);
4020 pop_method (thread, thread->stack [thread->stack_id - 1], thread->last_time);
4025 static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,heapshot,counters,coverage";
4027 static const char*
4028 match_option (const char *p, const char *opt)
4030 int len = strlen (opt);
4031 if (strncmp (p, opt, len) == 0) {
4032 if (p [len] == ',')
4033 len++;
4034 return p + len;
4036 return p;
4039 static int
4040 print_reports (ProfContext *ctx, const char *reps, int parse_only)
4042 const char *opt;
4043 const char *p;
4044 for (p = reps; *p; p = opt) {
4045 if ((opt = match_option (p, "header")) != p) {
4046 if (!parse_only)
4047 dump_header (ctx);
4048 continue;
4050 if ((opt = match_option (p, "thread")) != p) {
4051 if (!parse_only)
4052 dump_threads (ctx);
4053 continue;
4055 if ((opt = match_option (p, "domain")) != p) {
4056 if (!parse_only)
4057 dump_domains (ctx);
4058 continue;
4060 if ((opt = match_option (p, "context")) != p) {
4061 if (!parse_only)
4062 dump_remctxs (ctx);
4063 continue;
4065 if ((opt = match_option (p, "gc")) != p) {
4066 if (!parse_only)
4067 dump_gcs ();
4068 continue;
4070 if ((opt = match_option (p, "jit")) != p) {
4071 if (!parse_only)
4072 dump_jit ();
4073 continue;
4075 if ((opt = match_option (p, "alloc")) != p) {
4076 if (!parse_only)
4077 dump_allocations ();
4078 continue;
4080 if ((opt = match_option (p, "call")) != p) {
4081 if (!parse_only)
4082 dump_methods ();
4083 continue;
4085 if ((opt = match_option (p, "metadata")) != p) {
4086 if (!parse_only)
4087 dump_metadata ();
4088 continue;
4090 if ((opt = match_option (p, "exception")) != p) {
4091 if (!parse_only)
4092 dump_exceptions ();
4093 continue;
4095 if ((opt = match_option (p, "monitor")) != p) {
4096 if (!parse_only)
4097 dump_monitors ();
4098 continue;
4100 if ((opt = match_option (p, "heapshot")) != p) {
4101 if (!parse_only)
4102 dump_heap_shots ();
4103 continue;
4105 if ((opt = match_option (p, "sample")) != p) {
4106 if (!parse_only)
4107 dump_samples ();
4108 continue;
4110 if ((opt = match_option (p, "counters")) != p) {
4111 if (!parse_only)
4112 dump_counters ();
4113 continue;
4115 if ((opt = match_option (p, "coverage")) != p) {
4116 if (!parse_only)
4117 dump_coverage ();
4118 continue;
4120 if ((opt = match_option (p, "stats")) != p) {
4121 if (!parse_only)
4122 dump_stats ();
4123 continue;
4125 return 0;
4127 return 1;
4130 static int
4131 add_find_spec (const char *p)
4133 if (p [0] == 'S' && p [1] == ':') {
4134 char *vale;
4135 find_size = strtoul (p + 2, &vale, 10);
4136 return 1;
4137 } else if (p [0] == 'T' && p [1] == ':') {
4138 find_name = p + 2;
4139 return 1;
4141 return 0;
4144 static void
4145 usage (void)
4147 printf ("Mono log profiler report version %d.%d\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR);
4148 printf ("Usage: mprof-report [OPTIONS] FILENAME\n");
4149 printf ("FILENAME can be '-' to read from standard input.\n");
4150 printf ("Options:\n");
4151 printf ("\t--help display this help\n");
4152 printf ("\t--out=FILE write to FILE instead of stdout\n");
4153 printf ("\t--traces collect and show backtraces\n");
4154 printf ("\t--maxframes=NUM limit backtraces to NUM entries\n");
4155 printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
4156 printf ("\t %s\n", reports);
4157 printf ("\t--method-sort=MODE sort methods according to MODE: total, self, calls\n");
4158 printf ("\t--alloc-sort=MODE sort allocations according to MODE: bytes, count\n");
4159 printf ("\t--counters-sort=MODE sort counters according to MODE: time, category\n");
4160 printf ("\t only accessible in verbose mode\n");
4161 printf ("\t--track=OB1[,OB2...] track what happens to objects OBJ1, O2 etc.\n");
4162 printf ("\t--find=FINDSPEC find and track objects matching FINFSPEC, where FINDSPEC is:\n");
4163 printf ("\t S:minimum_size or T:partial_name\n");
4164 printf ("\t--thread=THREADID consider just the data for thread THREADID\n");
4165 printf ("\t--time=FROM-TO consider data FROM seconds from startup up to TO seconds\n");
4166 printf ("\t--verbose increase verbosity level\n");
4167 printf ("\t--debug display decoding debug info for mprof-report devs\n");
4168 printf ("\t--coverage-out=FILE write the coverage info to FILE as XML\n");
4172 main (int argc, char *argv[])
4174 ProfContext *ctx;
4175 int i;
4176 outfile = stdout;
4177 for (i = 1; i < argc; ++i) {
4178 if (strcmp ("--debug", argv [i]) == 0) {
4179 debug++;
4180 } else if (strcmp ("--help", argv [i]) == 0) {
4181 usage ();
4182 return 0;
4183 } else if (strncmp ("--alloc-sort=", argv [i], 13) == 0) {
4184 const char *val = argv [i] + 13;
4185 if (strcmp (val, "bytes") == 0) {
4186 alloc_sort_mode = ALLOC_SORT_BYTES;
4187 } else if (strcmp (val, "count") == 0) {
4188 alloc_sort_mode = ALLOC_SORT_COUNT;
4189 } else {
4190 usage ();
4191 return 1;
4193 } else if (strncmp ("--method-sort=", argv [i], 14) == 0) {
4194 const char *val = argv [i] + 14;
4195 if (strcmp (val, "total") == 0) {
4196 method_sort_mode = METHOD_SORT_TOTAL;
4197 } else if (strcmp (val, "self") == 0) {
4198 method_sort_mode = METHOD_SORT_SELF;
4199 } else if (strcmp (val, "calls") == 0) {
4200 method_sort_mode = METHOD_SORT_CALLS;
4201 } else {
4202 usage ();
4203 return 1;
4205 } else if (strncmp ("--counters-sort=", argv [i], 16) == 0) {
4206 const char *val = argv [i] + 16;
4207 if (strcmp (val, "time") == 0) {
4208 counters_sort_mode = COUNTERS_SORT_TIME;
4209 } else if (strcmp (val, "category") == 0) {
4210 counters_sort_mode = COUNTERS_SORT_CATEGORY;
4211 } else {
4212 usage ();
4213 return 1;
4215 } else if (strncmp ("--reports=", argv [i], 10) == 0) {
4216 const char *val = argv [i] + 10;
4217 if (!print_reports (NULL, val, 1)) {
4218 usage ();
4219 return 1;
4221 reports = val;
4222 } else if (strncmp ("--out=", argv [i], 6) == 0) {
4223 const char *val = argv [i] + 6;
4224 outfile = fopen (val, "w");
4225 if (!outfile) {
4226 printf ("Cannot open output file: %s\n", val);
4227 return 1;
4229 } else if (strncmp ("--maxframes=", argv [i], 12) == 0) {
4230 const char *val = argv [i] + 12;
4231 char *vale;
4232 trace_max = strtoul (val, &vale, 10);
4233 } else if (strncmp ("--find=", argv [i], 7) == 0) {
4234 const char *val = argv [i] + 7;
4235 if (!add_find_spec (val)) {
4236 usage ();
4237 return 1;
4239 } else if (strncmp ("--track=", argv [i], 8) == 0) {
4240 const char *val = argv [i] + 8;
4241 char *vale;
4242 while (*val) {
4243 uintptr_t tracked_obj;
4244 if (*val == ',') {
4245 val++;
4246 continue;
4248 tracked_obj = strtoul (val, &vale, 0);
4249 found_object (tracked_obj);
4250 val = vale;
4252 } else if (strncmp ("--thread=", argv [i], 9) == 0) {
4253 const char *val = argv [i] + 9;
4254 char *vale;
4255 thread_filter = strtoul (val, &vale, 0);
4256 } else if (strncmp ("--time=", argv [i], 7) == 0) {
4257 char *val = pstrdup (argv [i] + 7);
4258 double from_secs, to_secs;
4259 char *top = strchr (val, '-');
4260 if (!top) {
4261 usage ();
4262 return 1;
4264 *top++ = 0;
4265 from_secs = atof (val);
4266 to_secs = atof (top);
4267 g_free (val);
4268 if (from_secs > to_secs) {
4269 usage ();
4270 return 1;
4272 time_from = from_secs * 1000000000;
4273 time_to = to_secs * 1000000000;
4274 use_time_filter = 1;
4275 } else if (strcmp ("--verbose", argv [i]) == 0) {
4276 verbose++;
4277 } else if (strcmp ("--traces", argv [i]) == 0) {
4278 show_traces = 1;
4279 collect_traces = 1;
4280 } else if (strncmp ("--coverage-out=", argv [i], 15) == 0) {
4281 const char *val = argv [i] + 15;
4282 coverage_outfile = fopen (val, "w");
4283 if (!coverage_outfile) {
4284 printf ("Cannot open output file: %s\n", val);
4285 return 1;
4287 } else {
4288 break;
4291 if (i >= argc) {
4292 usage ();
4293 return 2;
4295 ctx = load_file (argv [i]);
4296 if (!ctx) {
4297 printf ("Not a log profiler data file (or unsupported version).\n");
4298 return 1;
4300 while (decode_buffer (ctx));
4301 flush_context (ctx);
4302 if (num_tracked_objects)
4303 return 0;
4304 print_reports (ctx, reports, 0);
4305 return 0;