[mono-api-info] Use XmlWriter instead of XmlDocument to make this faster.
[mono-project.git] / mono / profiler / decode.c
blobe57211635ee700b0ea1bea48015d8709333b89fd
1 /*
2 * decode.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 */
12 * The Coverage XML output schema
13 * <coverage>
14 * <assembly/>
15 * <class/>
16 * <method>
17 * <statement/>
18 * </method>
19 * </coverage>
21 * Elements:
22 * <coverage> - The root element of the documentation. It can contain any number of
23 * <assembly>, <class> or <method> elements.
24 * Attributes:
25 * - version: The version number for the file format - (eg: "0.3")
26 * <assembly> - Contains data about assemblies. Has no child elements
27 * Attributes:
28 * - name: The name of the assembly - (eg: "System.Xml")
29 * - guid: The GUID of the assembly
30 * - filename: The filename of the assembly
31 * - method-count: The number of methods in the assembly
32 * - full: The number of fully covered methods
33 * - partial: The number of partially covered methods
34 * <class> - Contains data about classes. Has no child elements
35 * Attributes:
36 * - name: The name of the class
37 * - method-count: The number of methods in the class
38 * - full: The number of fully covered methods
39 * - partial: The number of partially covered methods
40 * <method> - Contains data about methods. Can contain any number of <statement> elements
41 * Attributes:
42 * - assembly: The name of the parent assembly
43 * - class: The name of the parent class
44 * - name: The name of the method, with all it's parameters
45 * - filename: The name of the source file containing this method
46 * - token
47 * <statement> - Contains data about IL statements. Has no child elements
48 * Attributes:
49 * - offset: The offset of the statement in the IL code after the previous
50 * statement's offset
51 * - counter: 1 if the line was covered, 0 if it was not
52 * - line: The line number in the parent method's file
53 * - column: The column on the line
55 #include <config.h>
56 #include "utils.c"
57 #include "proflog.h"
58 #include <string.h>
59 #include <assert.h>
60 #include <stdio.h>
61 #if !defined(__APPLE__) && !defined(__FreeBSD__)
62 #include <malloc.h>
63 #endif
64 #include <unistd.h>
65 #include <stdlib.h>
66 #if defined (HAVE_SYS_ZLIB)
67 #include <zlib.h>
68 #endif
69 #include <glib.h>
70 #include <mono/metadata/profiler.h>
71 #include <mono/metadata/object.h>
72 #include <mono/metadata/debug-helpers.h>
73 #include <mono/utils/mono-counters.h>
75 #define HASH_SIZE 9371
76 #define SMALL_HASH_SIZE 31
78 #if defined(__native_client__) || defined(__native_client_codegen__)
79 volatile int __nacl_thread_suspension_needed = 0;
80 void __nacl_suspend_thread_if_needed() {}
81 #endif
83 static int debug = 0;
84 static int collect_traces = 0;
85 static int show_traces = 0;
86 static int trace_max = 6;
87 static int verbose = 0;
88 static uintptr_t *tracked_objects = 0;
89 static int num_tracked_objects = 0;
90 static uintptr_t thread_filter = 0;
91 static uint64_t find_size = 0;
92 static const char* find_name = NULL;
93 static uint64_t time_from = 0;
94 static uint64_t time_to = 0xffffffffffffffffULL;
95 static int use_time_filter = 0;
96 static uint64_t startup_time = 0;
97 static FILE* outfile = NULL;
98 static FILE* coverage_outfile = NULL;
100 static int32_t
101 read_int16 (unsigned char *p)
103 int32_t value = *p++;
104 value |= (*p++) << 8;
105 return value;
108 static int32_t
109 read_int32 (unsigned char *p)
111 int32_t value = *p++;
112 value |= (*p++) << 8;
113 value |= (*p++) << 16;
114 value |= (uint32_t)(*p++) << 24;
115 return value;
118 static int64_t
119 read_int64 (unsigned char *p)
121 uint64_t value = *p++;
122 value |= (*p++) << 8;
123 value |= (*p++) << 16;
124 value |= (uint64_t)(*p++) << 24;
125 value |= (uint64_t)(*p++) << 32;
126 value |= (uint64_t)(*p++) << 40;
127 value |= (uint64_t)(*p++) << 48;
128 value |= (uint64_t)(*p++) << 54;
129 return value;
132 static char*
133 pstrdup (const char *s)
135 int len = strlen (s) + 1;
136 char *p = (char *)malloc (len);
137 memcpy (p, s, len);
138 return p;
141 typedef struct _CounterValue CounterValue;
142 struct _CounterValue {
143 uint64_t timestamp;
144 unsigned char *buffer;
145 CounterValue *next;
148 typedef struct _Counter Counter;
149 struct _Counter {
150 int index;
151 const char *section;
152 const char *name;
153 int type;
154 int unit;
155 int variance;
156 CounterValue *values;
157 CounterValue *values_last;
160 typedef struct _CounterList CounterList;
161 struct _CounterList {
162 Counter *counter;
163 CounterList *next;
166 typedef struct _CounterSection CounterSection;
167 struct _CounterSection {
168 const char *value;
169 CounterList *counters;
170 CounterList *counters_last;
171 CounterSection *next;
174 typedef struct _CounterTimestamp CounterTimestamp;
175 struct _CounterTimestamp {
176 uint64_t value;
177 CounterSection *sections;
178 CounterSection *sections_last;
179 CounterTimestamp *next;
182 static CounterList *counters = NULL;
183 static CounterSection *counters_sections = NULL;
184 static CounterTimestamp *counters_timestamps = NULL;
186 enum {
187 COUNTERS_SORT_TIME,
188 COUNTERS_SORT_CATEGORY
191 static int counters_sort_mode = COUNTERS_SORT_TIME;
193 static void
194 add_counter_to_section (Counter *counter)
196 CounterSection *csection, *s;
197 CounterList *clist;
199 clist = (CounterList *)calloc (1, sizeof (CounterList));
200 clist->counter = counter;
202 for (csection = counters_sections; csection; csection = csection->next) {
203 if (strcmp (csection->value, counter->section) == 0) {
204 /* If section exist */
205 if (!csection->counters)
206 csection->counters = clist;
207 else
208 csection->counters_last->next = clist;
209 csection->counters_last = clist;
210 return;
214 /* If section does not exist */
215 csection = (CounterSection *)calloc (1, sizeof (CounterSection));
216 csection->value = counter->section;
217 csection->counters = clist;
218 csection->counters_last = clist;
220 if (!counters_sections) {
221 counters_sections = csection;
222 } else {
223 s = counters_sections;
224 while (s->next)
225 s = s->next;
226 s->next = csection;
230 static void
231 add_counter (const char *section, const char *name, int type, int unit, int variance, int index)
233 CounterList *list, *l;
234 Counter *counter;
236 for (list = counters; list; list = list->next)
237 if (list->counter->index == index)
238 return;
240 counter = (Counter *)calloc (1, sizeof (Counter));
241 counter->section = section;
242 counter->name = name;
243 counter->type = type;
244 counter->unit = unit;
245 counter->variance = variance;
246 counter->index = index;
248 list = (CounterList *)calloc (1, sizeof (CounterList));
249 list->counter = counter;
251 if (!counters) {
252 counters = list;
253 } else {
254 l = counters;
255 while (l->next)
256 l = l->next;
257 l->next = list;
260 if (counters_sort_mode == COUNTERS_SORT_CATEGORY || !verbose)
261 add_counter_to_section (counter);
264 static void
265 add_counter_to_timestamp (uint64_t timestamp, Counter *counter)
267 CounterTimestamp *ctimestamp, *t;
268 CounterSection *csection;
269 CounterList *clist;
271 clist = (CounterList *)calloc (1, sizeof (CounterList));
272 clist->counter = counter;
274 for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
275 if (ctimestamp->value == timestamp) {
276 for (csection = ctimestamp->sections; csection; csection = csection->next) {
277 if (strcmp (csection->value, counter->section) == 0) {
278 /* if timestamp exist and section exist */
279 if (!csection->counters)
280 csection->counters = clist;
281 else
282 csection->counters_last->next = clist;
283 csection->counters_last = clist;
284 return;
288 /* if timestamp exist and section does not exist */
289 csection = (CounterSection *)calloc (1, sizeof (CounterSection));
290 csection->value = counter->section;
291 csection->counters = clist;
292 csection->counters_last = clist;
294 if (!ctimestamp->sections)
295 ctimestamp->sections = csection;
296 else
297 ctimestamp->sections_last->next = csection;
298 ctimestamp->sections_last = csection;
299 return;
303 /* If timestamp do not exist and section does not exist */
304 csection = (CounterSection *)calloc (1, sizeof (CounterSection));
305 csection->value = counter->section;
306 csection->counters = clist;
307 csection->counters_last = clist;
309 ctimestamp = (CounterTimestamp *)calloc (1, sizeof (CounterTimestamp));
310 ctimestamp->value = timestamp;
311 ctimestamp->sections = csection;
312 ctimestamp->sections_last = csection;
314 if (!counters_timestamps) {
315 counters_timestamps = ctimestamp;
316 } else {
317 t = counters_timestamps;
318 while (t->next)
319 t = t->next;
320 t->next = ctimestamp;
324 static void
325 add_counter_value (int index, CounterValue *value)
327 CounterList *list;
329 for (list = counters; list; list = list->next) {
330 if (list->counter->index == index) {
331 if (!list->counter->values)
332 list->counter->values = value;
333 else
334 list->counter->values_last->next = value;
335 list->counter->values_last = value;
337 if (counters_sort_mode == COUNTERS_SORT_TIME)
338 add_counter_to_timestamp (value->timestamp, list->counter);
340 return;
345 static const char*
346 section_name (int section)
348 switch (section) {
349 case MONO_COUNTER_JIT: return "Mono JIT";
350 case MONO_COUNTER_GC: return "Mono GC";
351 case MONO_COUNTER_METADATA: return "Mono Metadata";
352 case MONO_COUNTER_GENERICS: return "Mono Generics";
353 case MONO_COUNTER_SECURITY: return "Mono Security";
354 case MONO_COUNTER_RUNTIME: return "Mono Runtime";
355 case MONO_COUNTER_SYSTEM: return "Mono System";
356 default: return "<unknown>";
360 static const char*
361 type_name (int type)
363 switch (type) {
364 case MONO_COUNTER_INT: return "Int";
365 case MONO_COUNTER_UINT: return "UInt";
366 case MONO_COUNTER_WORD: return "Word";
367 case MONO_COUNTER_LONG: return "Long";
368 case MONO_COUNTER_ULONG: return "ULong";
369 case MONO_COUNTER_DOUBLE: return "Double";
370 case MONO_COUNTER_STRING: return "String";
371 case MONO_COUNTER_TIME_INTERVAL: return "Time Interval";
372 default: return "<unknown>";
376 static const char*
377 unit_name (int unit)
379 switch (unit) {
380 case MONO_COUNTER_RAW: return "Raw";
381 case MONO_COUNTER_BYTES: return "Bytes";
382 case MONO_COUNTER_TIME: return "Time";
383 case MONO_COUNTER_COUNT: return "Count";
384 case MONO_COUNTER_PERCENTAGE: return "Percentage";
385 default: return "<unknown>";
389 static const char*
390 variance_name (int variance)
392 switch (variance) {
393 case MONO_COUNTER_MONOTONIC: return "Monotonic";
394 case MONO_COUNTER_CONSTANT: return "Constant";
395 case MONO_COUNTER_VARIABLE: return "Variable";
396 default: return "<unknown>";
400 static void
401 dump_counters_value (Counter *counter, const char *key_format, const char *key, void *value)
403 char format[32];
405 if (value == NULL) {
406 snprintf (format, sizeof (format), "%s : %%s\n", key_format);
407 fprintf (outfile, format, key, "<null>");
408 } else {
409 switch (counter->type) {
410 case MONO_COUNTER_INT:
411 #if SIZEOF_VOID_P == 4
412 case MONO_COUNTER_WORD:
413 #endif
414 snprintf (format, sizeof (format), "%s : %%d\n", key_format);
415 fprintf (outfile, format, key, *(int32_t*)value);
416 break;
417 case MONO_COUNTER_UINT:
418 snprintf (format, sizeof (format), "%s : %%u\n", key_format);
419 fprintf (outfile, format, key, *(uint32_t*)value);
420 break;
421 case MONO_COUNTER_LONG:
422 #if SIZEOF_VOID_P == 8
423 case MONO_COUNTER_WORD:
424 #endif
425 case MONO_COUNTER_TIME_INTERVAL:
426 if (counter->type == MONO_COUNTER_LONG && counter->unit == MONO_COUNTER_TIME) {
427 snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
428 fprintf (outfile, format, key, (double)*(int64_t*)value / 10000.0);
429 } else if (counter->type == MONO_COUNTER_TIME_INTERVAL) {
430 snprintf (format, sizeof (format), "%s : %%0.3fms\n", key_format);
431 fprintf (outfile, format, key, (double)*(int64_t*)value / 1000.0);
432 } else {
433 snprintf (format, sizeof (format), "%s : %%u\n", key_format);
434 fprintf (outfile, format, key, *(int64_t*)value);
436 break;
437 case MONO_COUNTER_ULONG:
438 snprintf (format, sizeof (format), "%s : %%llu\n", key_format);
439 fprintf (outfile, format, key, *(uint64_t*)value);
440 break;
441 case MONO_COUNTER_DOUBLE:
442 snprintf (format, sizeof (format), "%s : %%f\n", key_format);
443 fprintf (outfile, format, key, *(double*)value);
444 break;
445 case MONO_COUNTER_STRING:
446 snprintf (format, sizeof (format), "%s : %%s\n", key_format);
447 fprintf (outfile, format, key, *(char*)value);
448 break;
453 static void
454 dump_counters (void)
456 Counter *counter;
457 CounterValue *cvalue;
458 CounterTimestamp *ctimestamp;
459 CounterSection *csection;
460 CounterList *clist;
461 char strtimestamp[17];
462 int i, section_printed;
464 fprintf (outfile, "\nCounters:\n");
466 if (!verbose) {
467 char counters_to_print[][64] = {
468 "Methods from AOT",
469 "Methods JITted using mono JIT",
470 "Methods JITted using LLVM",
471 "Total time spent JITting (sec)",
472 "User Time",
473 "System Time",
474 "Total Time",
475 "Working Set",
476 "Private Bytes",
477 "Virtual Bytes",
478 "Page Faults",
479 "CPU Load Average - 1min",
480 "CPU Load Average - 5min",
481 "CPU Load Average - 15min",
485 for (csection = counters_sections; csection; csection = csection->next) {
486 section_printed = 0;
488 for (clist = csection->counters; clist; clist = clist->next) {
489 counter = clist->counter;
490 if (!counter->values_last)
491 continue;
493 for (i = 0; counters_to_print [i][0] != 0; i++) {
494 if (strcmp (counters_to_print [i], counter->name) == 0) {
495 if (!section_printed) {
496 fprintf (outfile, "\t%s:\n", csection->value);
497 section_printed = 1;
500 dump_counters_value (counter, "\t\t%-30s", counter->name, counter->values_last->buffer);
501 break;
506 } else if (counters_sort_mode == COUNTERS_SORT_TIME) {
507 for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
508 fprintf (outfile, "\t%llu:%02llu:%02llu:%02llu.%03llu:\n",
509 (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 / 24 % 1000),
510 (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 % 24),
511 (unsigned long long) (ctimestamp->value / 1000 / 60 % 60),
512 (unsigned long long) (ctimestamp->value / 1000 % 60),
513 (unsigned long long) (ctimestamp->value % 1000));
515 for (csection = ctimestamp->sections; csection; csection = csection->next) {
516 fprintf (outfile, "\t\t%s:\n", csection->value);
518 for (clist = csection->counters; clist; clist = clist->next) {
519 counter = clist->counter;
520 for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
521 if (cvalue->timestamp != ctimestamp->value)
522 continue;
524 dump_counters_value (counter, "\t\t\t%-30s", counter->name, cvalue->buffer);
529 } else if (counters_sort_mode == COUNTERS_SORT_CATEGORY) {
530 for (csection = counters_sections; csection; csection = csection->next) {
531 fprintf (outfile, "\t%s:\n", csection->value);
533 for (clist = csection->counters; clist; clist = clist->next) {
534 counter = clist->counter;
535 fprintf (outfile, "\t\t%s: [type: %s, unit: %s, variance: %s]\n",
536 counter->name, type_name (counter->type), unit_name (counter->unit), variance_name (counter->variance));
538 for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
539 snprintf (strtimestamp, sizeof (strtimestamp), "%llu:%02llu:%02llu:%02llu.%03llu",
540 (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 / 24 % 1000),
541 (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 % 24),
542 (unsigned long long) (cvalue->timestamp / 1000 / 60 % 60),
543 (unsigned long long) (cvalue->timestamp / 1000 % 60),
544 (unsigned long long) (cvalue->timestamp % 1000));
546 dump_counters_value (counter, "\t\t\t%s", strtimestamp, cvalue->buffer);
553 static int num_images;
554 typedef struct _ImageDesc ImageDesc;
555 struct _ImageDesc {
556 ImageDesc *next;
557 intptr_t image;
558 char *filename;
561 static ImageDesc* image_hash [SMALL_HASH_SIZE] = {0};
563 static void
564 add_image (intptr_t image, char *name)
566 int slot = ((image >> 2) & 0xffff) % SMALL_HASH_SIZE;
567 ImageDesc *cd = (ImageDesc *)malloc (sizeof (ImageDesc));
568 cd->image = image;
569 cd->filename = pstrdup (name);
570 cd->next = image_hash [slot];
571 image_hash [slot] = cd;
572 num_images++;
575 static int num_assemblies;
577 typedef struct _AssemblyDesc AssemblyDesc;
578 struct _AssemblyDesc {
579 AssemblyDesc *next;
580 intptr_t assembly;
581 char *asmname;
584 static AssemblyDesc* assembly_hash [SMALL_HASH_SIZE] = {0};
586 static void
587 add_assembly (intptr_t assembly, char *name)
589 int slot = ((assembly >> 2) & 0xffff) % SMALL_HASH_SIZE;
590 AssemblyDesc *cd = (AssemblyDesc *)malloc (sizeof (AssemblyDesc));
591 cd->assembly = assembly;
592 cd->asmname = pstrdup (name);
593 cd->next = assembly_hash [slot];
594 assembly_hash [slot] = cd;
595 num_assemblies++;
598 typedef struct _BackTrace BackTrace;
599 typedef struct {
600 uint64_t count;
601 BackTrace *bt;
602 } CallContext;
604 typedef struct {
605 int count;
606 int size;
607 CallContext *traces;
608 } TraceDesc;
610 typedef struct _ClassDesc ClassDesc;
611 struct _ClassDesc {
612 ClassDesc *next;
613 intptr_t klass;
614 char *name;
615 intptr_t allocs;
616 uint64_t alloc_size;
617 TraceDesc traces;
620 static ClassDesc* class_hash [HASH_SIZE] = {0};
621 static int num_classes = 0;
623 static ClassDesc*
624 add_class (intptr_t klass, const char *name)
626 int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
627 ClassDesc *cd;
628 cd = class_hash [slot];
629 while (cd && cd->klass != klass)
630 cd = cd->next;
631 /* we resolved an unknown class (unless we had the code unloaded) */
632 if (cd) {
633 /*printf ("resolved unknown: %s\n", name);*/
634 free (cd->name);
635 cd->name = pstrdup (name);
636 return cd;
638 cd = (ClassDesc *)calloc (sizeof (ClassDesc), 1);
639 cd->klass = klass;
640 cd->name = pstrdup (name);
641 cd->next = class_hash [slot];
642 cd->allocs = 0;
643 cd->alloc_size = 0;
644 cd->traces.count = 0;
645 cd->traces.size = 0;
646 cd->traces.traces = NULL;
647 class_hash [slot] = cd;
648 num_classes++;
649 return cd;
652 static ClassDesc *
653 lookup_class (intptr_t klass)
655 int slot = ((klass >> 2) & 0xffff) % HASH_SIZE;
656 ClassDesc *cd = class_hash [slot];
657 while (cd && cd->klass != klass)
658 cd = cd->next;
659 if (!cd) {
660 char buf [128];
661 snprintf (buf, sizeof (buf), "unresolved class %p", (void*)klass);
662 return add_class (klass, buf);
664 return cd;
667 typedef struct _MethodDesc MethodDesc;
668 struct _MethodDesc {
669 MethodDesc *next;
670 intptr_t method;
671 char *name;
672 intptr_t code;
673 int len;
674 int recurse_count;
675 int sample_hits;
676 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 */
677 uint64_t calls;
678 uint64_t total_time;
679 uint64_t callee_time;
680 uint64_t self_time;
681 TraceDesc traces;
684 static MethodDesc* method_hash [HASH_SIZE] = {0};
685 static int num_methods = 0;
687 static MethodDesc*
688 add_method (intptr_t method, const char *name, intptr_t code, int len)
690 int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
691 MethodDesc *cd;
692 cd = method_hash [slot];
693 while (cd && cd->method != method)
694 cd = cd->next;
695 /* we resolved an unknown method (unless we had the code unloaded) */
696 if (cd) {
697 cd->code = code;
698 cd->len = len;
699 /*printf ("resolved unknown: %s\n", name);*/
700 free (cd->name);
701 cd->name = pstrdup (name);
702 return cd;
704 cd = (MethodDesc *)calloc (sizeof (MethodDesc), 1);
705 cd->method = method;
706 cd->name = pstrdup (name);
707 cd->code = code;
708 cd->len = len;
709 cd->calls = 0;
710 cd->total_time = 0;
711 cd->traces.count = 0;
712 cd->traces.size = 0;
713 cd->traces.traces = NULL;
714 cd->next = method_hash [slot];
715 method_hash [slot] = cd;
716 num_methods++;
717 return cd;
720 static MethodDesc *
721 lookup_method (intptr_t method)
723 int slot = ((method >> 2) & 0xffff) % HASH_SIZE;
724 MethodDesc *cd = method_hash [slot];
725 while (cd && cd->method != method)
726 cd = cd->next;
727 if (!cd) {
728 char buf [128];
729 snprintf (buf, sizeof (buf), "unknown method %p", (void*)method);
730 return add_method (method, buf, 0, 0);
732 return cd;
735 static int num_stat_samples = 0;
736 static int size_stat_samples = 0;
737 uintptr_t *stat_samples = NULL;
738 int *stat_sample_desc = NULL;
740 static void
741 add_stat_sample (int type, uintptr_t ip) {
742 if (num_stat_samples == size_stat_samples) {
743 size_stat_samples *= 2;
744 if (!size_stat_samples)
745 size_stat_samples = 32;
746 stat_samples = (uintptr_t *)realloc (stat_samples, size_stat_samples * sizeof (uintptr_t));
747 stat_sample_desc = (int *)realloc (stat_sample_desc, size_stat_samples * sizeof (int));
749 stat_samples [num_stat_samples] = ip;
750 stat_sample_desc [num_stat_samples++] = type;
753 static MethodDesc*
754 lookup_method_by_ip (uintptr_t ip)
756 int i;
757 MethodDesc* m;
758 /* dumb */
759 for (i = 0; i < HASH_SIZE; ++i) {
760 m = method_hash [i];
761 while (m) {
762 //printf ("checking %p against %p-%p\n", (void*)ip, (void*)(m->code), (void*)(m->code + m->len));
763 if (ip >= (uintptr_t)m->code && ip < (uintptr_t)m->code + m->len) {
764 return m;
766 m = m->next;
769 return NULL;
772 static int
773 compare_method_samples (const void *a, const void *b)
775 MethodDesc *const *A = (MethodDesc *const *)a;
776 MethodDesc *const *B = (MethodDesc *const *)b;
777 if ((*A)->sample_hits == (*B)->sample_hits)
778 return 0;
779 if ((*B)->sample_hits < (*A)->sample_hits)
780 return -1;
781 return 1;
784 typedef struct _UnmanagedSymbol UnmanagedSymbol;
785 struct _UnmanagedSymbol {
786 UnmanagedSymbol *parent;
787 char *name;
788 int is_binary;
789 uintptr_t addr;
790 uintptr_t size;
791 uintptr_t sample_hits;
794 static UnmanagedSymbol **usymbols = NULL;
795 static int usymbols_size = 0;
796 static int usymbols_num = 0;
798 static int
799 compare_usymbol_addr (const void *a, const void *b)
801 UnmanagedSymbol *const *A = (UnmanagedSymbol *const *)a;
802 UnmanagedSymbol *const *B = (UnmanagedSymbol *const *)b;
803 if ((*B)->addr == (*A)->addr)
804 return 0;
805 if ((*B)->addr > (*A)->addr)
806 return -1;
807 return 1;
810 static int
811 compare_usymbol_samples (const void *a, const void *b)
813 UnmanagedSymbol *const *A = (UnmanagedSymbol *const *)a;
814 UnmanagedSymbol *const *B = (UnmanagedSymbol *const *)b;
815 if ((*B)->sample_hits == (*A)->sample_hits)
816 return 0;
817 if ((*B)->sample_hits < (*A)->sample_hits)
818 return -1;
819 return 1;
822 static void
823 add_unmanaged_symbol (uintptr_t addr, char *name, uintptr_t size)
825 UnmanagedSymbol *sym;
826 if (usymbols_num == usymbols_size) {
827 int new_size = usymbols_size * 2;
828 if (!new_size)
829 new_size = 16;
830 usymbols = (UnmanagedSymbol **)realloc (usymbols, sizeof (void*) * new_size);
831 usymbols_size = new_size;
833 sym = (UnmanagedSymbol *)calloc (sizeof (UnmanagedSymbol), 1);
834 sym->addr = addr;
835 sym->name = name;
836 sym->size = size;
837 usymbols [usymbols_num++] = sym;
840 /* only valid after the symbols are sorted */
841 static UnmanagedSymbol*
842 lookup_unmanaged_symbol (uintptr_t addr)
844 int r = usymbols_num - 1;
845 int l = 0;
846 UnmanagedSymbol *sym;
847 int last_best = -1;
848 while (r >= l) {
849 int m = (l + r) / 2;
850 sym = usymbols [m];
851 if (addr == sym->addr)
852 return sym;
853 if (addr < sym->addr) {
854 r = m - 1;
855 } else if (addr > sym->addr) {
856 l = m + 1;
857 last_best = m;
860 if (last_best >= 0 && (addr - usymbols [last_best]->addr) < 4096)
861 return usymbols [last_best];
862 return NULL;
865 /* we use the same structure for binaries */
866 static UnmanagedSymbol **ubinaries = NULL;
867 static int ubinaries_size = 0;
868 static int ubinaries_num = 0;
870 static void
871 add_unmanaged_binary (uintptr_t addr, char *name, uintptr_t size)
873 UnmanagedSymbol *sym;
874 if (ubinaries_num == ubinaries_size) {
875 int new_size = ubinaries_size * 2;
876 if (!new_size)
877 new_size = 16;
878 ubinaries = (UnmanagedSymbol **)realloc (ubinaries, sizeof (void*) * new_size);
879 ubinaries_size = new_size;
881 sym = (UnmanagedSymbol *)calloc (sizeof (UnmanagedSymbol), 1);
882 sym->addr = addr;
883 sym->name = name;
884 sym->size = size;
885 sym->is_binary = 1;
886 ubinaries [ubinaries_num++] = sym;
889 static UnmanagedSymbol*
890 lookup_unmanaged_binary (uintptr_t addr)
892 int i;
893 for (i = 0; i < ubinaries_num; ++i) {
894 UnmanagedSymbol *ubin = ubinaries [i];
895 if (addr >= ubin->addr && addr < ubin->addr + ubin->size) {
896 return ubin;
899 return NULL;
902 static const char*
903 sample_type_name (int type)
905 switch (type) {
906 case SAMPLE_CYCLES: return "cycles";
907 case SAMPLE_INSTRUCTIONS: return "instructions retired";
908 case SAMPLE_CACHE_MISSES: return "cache misses";
909 case SAMPLE_CACHE_REFS: return "cache references";
910 case SAMPLE_BRANCHES: return "executed branches";
911 case SAMPLE_BRANCH_MISSES: return "unpredicted branches";
913 return "unknown";
916 static void
917 set_usym_parent (UnmanagedSymbol** cachedus, int count)
919 int i;
920 for (i = 0; i < count; ++i) {
921 UnmanagedSymbol *ubin = lookup_unmanaged_binary (cachedus [i]->addr);
922 if (ubin == cachedus [i])
923 continue;
924 cachedus [i]->parent = ubin;
928 static void
929 print_usym (UnmanagedSymbol* um)
931 if (um->parent)
932 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);
933 else
934 fprintf (outfile, "\t%6zd %6.2f %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name);
937 static int
938 sym_percent (uintptr_t sample_hits)
940 double pc;
941 if (verbose)
942 return 1;
943 pc = sample_hits*100.0/num_stat_samples;
944 return pc >= 0.1;
947 static void
948 dump_samples (void)
950 int i, u;
951 int count = 0, msize = 0;
952 int unmanaged_hits = 0;
953 int unresolved_hits = 0;
954 MethodDesc** cachedm = NULL;
955 int ucount = 0, usize = 0;
956 UnmanagedSymbol** cachedus = NULL;
957 if (!num_stat_samples)
958 return;
959 qsort (usymbols, usymbols_num, sizeof (UnmanagedSymbol*), compare_usymbol_addr);
960 for (i = 0; i < num_stat_samples; ++i) {
961 MethodDesc *m = lookup_method_by_ip (stat_samples [i]);
962 if (m) {
963 if (!m->sample_hits) {
964 if (count == msize) {
965 msize *= 2;
966 if (!msize)
967 msize = 4;
968 cachedm = (MethodDesc **)realloc (cachedm, sizeof (void*) * msize);
970 cachedm [count++] = m;
972 m->sample_hits++;
973 } else {
974 UnmanagedSymbol *usym = lookup_unmanaged_symbol (stat_samples [i]);
975 if (!usym) {
976 unresolved_hits++;
977 //printf ("unmanaged hit at %p\n", (void*)stat_samples [i]);
978 usym = lookup_unmanaged_binary (stat_samples [i]);
980 if (usym) {
981 if (!usym->sample_hits) {
982 if (ucount == usize) {
983 usize *= 2;
984 if (!usize)
985 usize = 4;
986 cachedus = (UnmanagedSymbol **)realloc (cachedus, sizeof (void*) * usize);
988 cachedus [ucount++] = usym;
990 usym->sample_hits++;
992 unmanaged_hits++;
995 qsort (cachedm, count, sizeof (MethodDesc*), compare_method_samples);
996 qsort (cachedus, ucount, sizeof (UnmanagedSymbol*), compare_usymbol_samples);
997 set_usym_parent (cachedus, ucount);
998 fprintf (outfile, "\nStatistical samples summary\n");
999 fprintf (outfile, "\tSample type: %s\n", sample_type_name (stat_sample_desc [0]));
1000 fprintf (outfile, "\tUnmanaged hits: %6d (%4.1f%%)\n", unmanaged_hits, (100.0*unmanaged_hits)/num_stat_samples);
1001 fprintf (outfile, "\tManaged hits: %6d (%4.1f%%)\n", num_stat_samples - unmanaged_hits, (100.0*(num_stat_samples-unmanaged_hits))/num_stat_samples);
1002 fprintf (outfile, "\tUnresolved hits: %6d (%4.1f%%)\n", unresolved_hits, (100.0*unresolved_hits)/num_stat_samples);
1003 fprintf (outfile, "\t%6s %6s %s\n", "Hits", "%", "Method name");
1004 i = 0;
1005 u = 0;
1006 while (i < count || u < ucount) {
1007 if (i < count) {
1008 MethodDesc *m = cachedm [i];
1009 if (u < ucount) {
1010 UnmanagedSymbol *um = cachedus [u];
1011 if (um->sample_hits > m->sample_hits) {
1012 if (!sym_percent (um->sample_hits))
1013 break;
1014 print_usym (um);
1015 u++;
1016 continue;
1019 if (!sym_percent (m->sample_hits))
1020 break;
1021 fprintf (outfile, "\t%6d %6.2f %s\n", m->sample_hits, m->sample_hits*100.0/num_stat_samples, m->name);
1022 i++;
1023 continue;
1025 if (u < ucount) {
1026 UnmanagedSymbol *um = cachedus [u];
1027 if (!sym_percent (um->sample_hits))
1028 break;
1029 print_usym (um);
1030 u++;
1031 continue;
1036 typedef struct _HeapClassDesc HeapClassDesc;
1037 typedef struct {
1038 HeapClassDesc *klass;
1039 uint64_t count;
1040 } HeapClassRevRef;
1042 struct _HeapClassDesc {
1043 ClassDesc *klass;
1044 int64_t count;
1045 int64_t total_size;
1046 HeapClassRevRef *rev_hash;
1047 int rev_hash_size;
1048 int rev_count;
1049 uintptr_t pinned_references;
1050 uintptr_t root_references;
1053 static int
1054 add_rev_class_hashed (HeapClassRevRef *rev_hash, uintptr_t size, HeapClassDesc *hklass, uint64_t value)
1056 uintptr_t i;
1057 uintptr_t start_pos;
1058 start_pos = (hklass->klass->klass >> 2) % size;
1059 assert (start_pos < size);
1060 i = start_pos;
1061 do {
1062 if (rev_hash [i].klass == hklass) {
1063 rev_hash [i].count += value;
1064 return 0;
1065 } else if (!rev_hash [i].klass) {
1066 rev_hash [i].klass = hklass;
1067 rev_hash [i].count += value;
1068 start_pos = 0;
1069 for (i = 0; i < size; ++i)
1070 if (rev_hash [i].klass && rev_hash [i].klass->klass == hklass->klass)
1071 start_pos ++;
1072 assert (start_pos == 1);
1073 return 1;
1075 /* wrap around */
1076 if (++i == size)
1077 i = 0;
1078 } while (i != start_pos);
1079 /* should not happen */
1080 printf ("failed revref store\n");
1081 return 0;
1084 static void
1085 add_heap_class_rev (HeapClassDesc *from, HeapClassDesc *to)
1087 uintptr_t i;
1088 if (to->rev_count * 2 >= to->rev_hash_size) {
1089 HeapClassRevRef *n;
1090 uintptr_t old_size = to->rev_hash_size;
1091 to->rev_hash_size *= 2;
1092 if (to->rev_hash_size == 0)
1093 to->rev_hash_size = 4;
1094 n = (HeapClassRevRef *)calloc (sizeof (HeapClassRevRef) * to->rev_hash_size, 1);
1095 for (i = 0; i < old_size; ++i) {
1096 if (to->rev_hash [i].klass)
1097 add_rev_class_hashed (n, to->rev_hash_size, to->rev_hash [i].klass, to->rev_hash [i].count);
1099 if (to->rev_hash)
1100 free (to->rev_hash);
1101 to->rev_hash = n;
1103 to->rev_count += add_rev_class_hashed (to->rev_hash, to->rev_hash_size, from, 1);
1106 typedef struct {
1107 uintptr_t objaddr;
1108 HeapClassDesc *hklass;
1109 uintptr_t num_refs;
1110 uintptr_t refs [0];
1111 } HeapObjectDesc;
1113 typedef struct _HeapShot HeapShot;
1114 struct _HeapShot {
1115 HeapShot *next;
1116 uint64_t timestamp;
1117 int class_count;
1118 int hash_size;
1119 HeapClassDesc **class_hash;
1120 HeapClassDesc **sorted;
1121 HeapObjectDesc **objects_hash;
1122 uintptr_t objects_count;
1123 uintptr_t objects_hash_size;
1124 uintptr_t num_roots;
1125 uintptr_t *roots;
1126 uintptr_t *roots_extra;
1127 int *roots_types;
1130 static HeapShot *heap_shots = NULL;
1131 static int num_heap_shots = 0;
1133 static HeapShot*
1134 new_heap_shot (uint64_t timestamp)
1136 HeapShot *hs = (HeapShot *)calloc (sizeof (HeapShot), 1);
1137 hs->hash_size = 4;
1138 hs->class_hash = (HeapClassDesc **)calloc (sizeof (void*), hs->hash_size);
1139 hs->timestamp = timestamp;
1140 num_heap_shots++;
1141 hs->next = heap_shots;
1142 heap_shots = hs;
1143 return hs;
1146 static HeapClassDesc*
1147 heap_class_lookup (HeapShot *hs, ClassDesc *klass)
1149 int i;
1150 unsigned int start_pos;
1151 start_pos = ((uintptr_t)klass->klass >> 2) % hs->hash_size;
1152 i = start_pos;
1153 do {
1154 HeapClassDesc* cd = hs->class_hash [i];
1155 if (!cd)
1156 return NULL;
1157 if (cd->klass == klass)
1158 return cd;
1159 /* wrap around */
1160 if (++i == hs->hash_size)
1161 i = 0;
1162 } while (i != start_pos);
1163 return NULL;
1166 static int
1167 add_heap_hashed (HeapClassDesc **hash, HeapClassDesc **retv, uintptr_t hsize, ClassDesc *klass, uint64_t size, uint64_t count)
1169 uintptr_t i;
1170 uintptr_t start_pos;
1171 start_pos = ((uintptr_t)klass->klass >> 2) % hsize;
1172 i = start_pos;
1173 do {
1174 if (hash [i] && hash [i]->klass == klass) {
1175 hash [i]->total_size += size;
1176 hash [i]->count += count;
1177 *retv = hash [i];
1178 return 0;
1179 } else if (!hash [i]) {
1180 if (*retv) {
1181 hash [i] = *retv;
1182 return 1;
1184 hash [i] = (HeapClassDesc *)calloc (sizeof (HeapClassDesc), 1);
1185 hash [i]->klass = klass;
1186 hash [i]->total_size += size;
1187 hash [i]->count += count;
1188 *retv = hash [i];
1189 return 1;
1191 /* wrap around */
1192 if (++i == hsize)
1193 i = 0;
1194 } while (i != start_pos);
1195 /* should not happen */
1196 printf ("failed heap class store\n");
1197 return 0;
1200 static HeapClassDesc*
1201 add_heap_shot_class (HeapShot *hs, ClassDesc *klass, uint64_t size)
1203 HeapClassDesc *res;
1204 int i;
1205 if (hs->class_count * 2 >= hs->hash_size) {
1206 HeapClassDesc **n;
1207 int old_size = hs->hash_size;
1208 hs->hash_size *= 2;
1209 if (hs->hash_size == 0)
1210 hs->hash_size = 4;
1211 n = (HeapClassDesc **)calloc (sizeof (void*) * hs->hash_size, 1);
1212 for (i = 0; i < old_size; ++i) {
1213 res = hs->class_hash [i];
1214 if (hs->class_hash [i])
1215 add_heap_hashed (n, &res, hs->hash_size, hs->class_hash [i]->klass, hs->class_hash [i]->total_size, hs->class_hash [i]->count);
1217 if (hs->class_hash)
1218 free (hs->class_hash);
1219 hs->class_hash = n;
1221 res = NULL;
1222 hs->class_count += add_heap_hashed (hs->class_hash, &res, hs->hash_size, klass, size, 1);
1223 //if (res->count == 1)
1224 // printf ("added heap class: %s\n", res->klass->name);
1225 return res;
1228 static HeapObjectDesc*
1229 alloc_heap_obj (uintptr_t objaddr, HeapClassDesc *hklass, uintptr_t num_refs)
1231 HeapObjectDesc* ho = (HeapObjectDesc *)calloc (sizeof (HeapObjectDesc) + num_refs * sizeof (uintptr_t), 1);
1232 ho->objaddr = objaddr;
1233 ho->hklass = hklass;
1234 ho->num_refs = num_refs;
1235 return ho;
1238 static uintptr_t
1239 heap_shot_find_obj_slot (HeapShot *hs, uintptr_t objaddr)
1241 uintptr_t i;
1242 uintptr_t start_pos;
1243 HeapObjectDesc **hash = hs->objects_hash;
1244 start_pos = ((uintptr_t)objaddr >> 3) % hs->objects_hash_size;
1245 i = start_pos;
1246 do {
1247 if (hash [i] && hash [i]->objaddr == objaddr) {
1248 return i;
1249 } else if (!hash [i]) {
1250 break; /* fail */
1252 /* wrap around */
1253 if (++i == hs->objects_hash_size)
1254 i = 0;
1255 } while (i != start_pos);
1256 /* should not happen */
1257 //printf ("failed heap obj slot\n");
1258 return -1;
1261 static HeapObjectDesc*
1262 heap_shot_obj_add_refs (HeapShot *hs, uintptr_t objaddr, uintptr_t num, uintptr_t *ref_offset)
1264 HeapObjectDesc **hash = hs->objects_hash;
1265 uintptr_t i = heap_shot_find_obj_slot (hs, objaddr);
1266 if (i >= 0) {
1267 HeapObjectDesc* ho = alloc_heap_obj (objaddr, hash [i]->hklass, hash [i]->num_refs + num);
1268 *ref_offset = hash [i]->num_refs;
1269 memcpy (ho->refs, hash [i]->refs, hash [i]->num_refs * sizeof (uintptr_t));
1270 free (hash [i]);
1271 hash [i] = ho;
1272 return ho;
1274 /* should not happen */
1275 printf ("failed heap obj update\n");
1276 return NULL;
1280 static uintptr_t
1281 add_heap_hashed_obj (HeapObjectDesc **hash, uintptr_t hsize, HeapObjectDesc *obj)
1283 uintptr_t i;
1284 uintptr_t start_pos;
1285 start_pos = ((uintptr_t)obj->objaddr >> 3) % hsize;
1286 i = start_pos;
1287 do {
1288 if (hash [i] && hash [i]->objaddr == obj->objaddr) {
1289 printf ("duplicate object!\n");
1290 return 0;
1291 } else if (!hash [i]) {
1292 hash [i] = obj;
1293 return 1;
1295 /* wrap around */
1296 if (++i == hsize)
1297 i = 0;
1298 } while (i != start_pos);
1299 /* should not happen */
1300 printf ("failed heap obj store\n");
1301 return 0;
1304 static void
1305 add_heap_shot_obj (HeapShot *hs, HeapObjectDesc *obj)
1307 uintptr_t i;
1308 if (hs->objects_count * 2 >= hs->objects_hash_size) {
1309 HeapObjectDesc **n;
1310 uintptr_t old_size = hs->objects_hash_size;
1311 hs->objects_hash_size *= 2;
1312 if (hs->objects_hash_size == 0)
1313 hs->objects_hash_size = 4;
1314 n = (HeapObjectDesc **)calloc (sizeof (void*) * hs->objects_hash_size, 1);
1315 for (i = 0; i < old_size; ++i) {
1316 if (hs->objects_hash [i])
1317 add_heap_hashed_obj (n, hs->objects_hash_size, hs->objects_hash [i]);
1319 if (hs->objects_hash)
1320 free (hs->objects_hash);
1321 hs->objects_hash = n;
1323 hs->objects_count += add_heap_hashed_obj (hs->objects_hash, hs->objects_hash_size, obj);
1326 static void
1327 heap_shot_resolve_reverse_refs (HeapShot *hs)
1329 uintptr_t i;
1330 for (i = 0; i < hs->objects_hash_size; ++i) {
1331 uintptr_t r;
1332 HeapObjectDesc *ho = hs->objects_hash [i];
1333 if (!ho)
1334 continue;
1335 for (r = 0; r < ho->num_refs; ++r) {
1336 uintptr_t oi = heap_shot_find_obj_slot (hs, ho->refs [r]);
1337 add_heap_class_rev (ho->hklass, hs->objects_hash [oi]->hklass);
1342 #define MARK_GRAY 1
1343 #define MARK_BLACK 2
1345 static void
1346 heap_shot_mark_objects (HeapShot *hs)
1348 uintptr_t i, oi, r;
1349 unsigned char *marks;
1350 HeapObjectDesc *obj, *ref;
1351 int marked_some;
1352 uintptr_t num_marked = 0, num_unmarked;
1353 for (i = 0; i < hs->num_roots; ++i) {
1354 HeapClassDesc *cd;
1355 oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
1356 if (oi == -1) {
1357 continue;
1359 obj = hs->objects_hash [oi];
1360 cd = obj->hklass;
1361 if (hs->roots_types [i] & MONO_PROFILE_GC_ROOT_PINNING)
1362 cd->pinned_references++;
1363 cd->root_references++;
1365 if (!debug)
1366 return;
1367 /* consistency checks: it seems not all the objects are walked in the heap in some cases */
1368 marks = (unsigned char *)calloc (hs->objects_hash_size, 1);
1369 if (!marks)
1370 return;
1371 for (i = 0; i < hs->num_roots; ++i) {
1372 oi = heap_shot_find_obj_slot (hs, hs->roots [i]);
1373 if (oi == -1) {
1374 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);
1375 continue;
1377 obj = hs->objects_hash [oi];
1378 if (!marks [oi]) {
1379 marks [oi] = obj->num_refs? MARK_GRAY: MARK_BLACK;
1380 num_marked++;
1383 marked_some = 1;
1384 while (marked_some) {
1385 marked_some = 0;
1386 for (i = 0; i < hs->objects_hash_size; ++i) {
1387 if (marks [i] != MARK_GRAY)
1388 continue;
1389 marks [i] = MARK_BLACK;
1390 obj = hs->objects_hash [i];
1391 for (r = 0; r < obj->num_refs; ++r) {
1392 oi = heap_shot_find_obj_slot (hs, obj->refs [r]);
1393 if (oi == -1) {
1394 fprintf (outfile, "referenced obj %p not found in heap\n", (void*)obj->refs [r]);
1395 continue;
1397 ref = hs->objects_hash [oi];
1398 if (!marks [oi]) {
1399 marks [oi] = ref->num_refs? MARK_GRAY: MARK_BLACK;
1402 marked_some++;
1406 num_unmarked = 0;
1407 for (i = 0; i < hs->objects_hash_size; ++i) {
1408 if (hs->objects_hash [i] && !marks [i]) {
1409 num_unmarked++;
1410 fprintf (outfile, "object %p (%s) unmarked\n", (void*)hs->objects_hash [i], hs->objects_hash [i]->hklass->klass->name);
1413 fprintf (outfile, "Total unmarked: %zd/%zd\n", num_unmarked, hs->objects_count);
1414 free (marks);
1417 static void
1418 heap_shot_free_objects (HeapShot *hs)
1420 uintptr_t i;
1421 for (i = 0; i < hs->objects_hash_size; ++i) {
1422 HeapObjectDesc *ho = hs->objects_hash [i];
1423 if (ho)
1424 free (ho);
1426 if (hs->objects_hash)
1427 free (hs->objects_hash);
1428 hs->objects_hash = NULL;
1429 hs->objects_hash_size = 0;
1430 hs->objects_count = 0;
1434 struct _BackTrace {
1435 BackTrace *next;
1436 unsigned int hash;
1437 int count;
1438 int id;
1439 MethodDesc *methods [1];
1442 static BackTrace *backtrace_hash [HASH_SIZE];
1443 static BackTrace **backtraces = NULL;
1444 static int num_backtraces = 0;
1445 static int next_backtrace = 0;
1447 static int
1448 hash_backtrace (int count, MethodDesc **methods)
1450 int hash = count;
1451 int i;
1452 for (i = 0; i < count; ++i) {
1453 hash = (hash << 5) - hash + methods [i]->method;
1455 return hash;
1458 static int
1459 compare_backtrace (BackTrace *bt, int count, MethodDesc **methods)
1461 int i;
1462 if (bt->count != count)
1463 return 0;
1464 for (i = 0; i < count; ++i)
1465 if (methods [i] != bt->methods [i])
1466 return 0;
1467 return 1;
1470 static BackTrace*
1471 add_backtrace (int count, MethodDesc **methods)
1473 int hash = hash_backtrace (count, methods);
1474 int slot = (hash & 0xffff) % HASH_SIZE;
1475 BackTrace *bt = backtrace_hash [slot];
1476 while (bt) {
1477 if (bt->hash == hash && compare_backtrace (bt, count, methods))
1478 return bt;
1479 bt = bt->next;
1481 bt = (BackTrace *)malloc (sizeof (BackTrace) + ((count - 1) * sizeof (void*)));
1482 bt->next = backtrace_hash [slot];
1483 backtrace_hash [slot] = bt;
1484 if (next_backtrace == num_backtraces) {
1485 num_backtraces *= 2;
1486 if (!num_backtraces)
1487 num_backtraces = 16;
1488 backtraces = (BackTrace **)realloc (backtraces, sizeof (void*) * num_backtraces);
1490 bt->id = next_backtrace++;
1491 backtraces [bt->id] = bt;
1492 bt->count = count;
1493 bt->hash = hash;
1494 for (slot = 0; slot < count; ++slot)
1495 bt->methods [slot] = methods [slot];
1497 return bt;
1500 typedef struct _MonitorDesc MonitorDesc;
1501 typedef struct _ThreadContext ThreadContext;
1502 typedef struct _DomainContext DomainContext;
1503 typedef struct _RemCtxContext RemCtxContext;
1505 typedef struct {
1506 FILE *file;
1507 #if defined (HAVE_SYS_ZLIB)
1508 gzFile gzfile;
1509 #endif
1510 unsigned char *buf;
1511 int size;
1512 int data_version;
1513 int version_major;
1514 int version_minor;
1515 int timer_overhead;
1516 int pid;
1517 int port;
1518 uint64_t startup_time;
1519 ThreadContext *threads;
1520 ThreadContext *current_thread;
1521 DomainContext *domains;
1522 DomainContext *current_domain;
1523 RemCtxContext *remctxs;
1524 RemCtxContext *current_remctx;
1525 } ProfContext;
1527 struct _ThreadContext {
1528 ThreadContext *next;
1529 intptr_t thread_id;
1530 char *name;
1531 /* emulated stack */
1532 MethodDesc **stack;
1533 uint64_t *time_stack;
1534 uint64_t *callee_time_stack;
1535 uint64_t last_time;
1536 uint64_t contention_start;
1537 MonitorDesc *monitor;
1538 int stack_size;
1539 int stack_id;
1540 HeapShot *current_heap_shot;
1541 uintptr_t num_roots;
1542 uintptr_t size_roots;
1543 uintptr_t *roots;
1544 uintptr_t *roots_extra;
1545 int *roots_types;
1546 uint64_t gc_start_times [3];
1549 struct _DomainContext {
1550 DomainContext *next;
1551 intptr_t domain_id;
1552 const char *friendly_name;
1555 struct _RemCtxContext {
1556 RemCtxContext *next;
1557 intptr_t remctx_id;
1558 intptr_t domain_id;
1561 static void
1562 ensure_buffer (ProfContext *ctx, int size)
1564 if (ctx->size < size) {
1565 ctx->buf = (unsigned char *)realloc (ctx->buf, size);
1566 ctx->size = size;
1570 static int
1571 load_data (ProfContext *ctx, int size)
1573 ensure_buffer (ctx, size);
1574 #if defined (HAVE_SYS_ZLIB)
1575 if (ctx->gzfile) {
1576 int r = gzread (ctx->gzfile, ctx->buf, size);
1577 if (r == 0)
1578 return size == 0? 1: 0;
1579 return r == size;
1580 } else
1581 #endif
1583 int r = fread (ctx->buf, size, 1, ctx->file);
1584 if (r == 0)
1585 return size == 0? 1: 0;
1586 return r;
1590 static ThreadContext*
1591 get_thread (ProfContext *ctx, intptr_t thread_id)
1593 ThreadContext *thread;
1594 if (ctx->current_thread && ctx->current_thread->thread_id == thread_id)
1595 return ctx->current_thread;
1596 thread = ctx->threads;
1597 while (thread) {
1598 if (thread->thread_id == thread_id) {
1599 return thread;
1601 thread = thread->next;
1603 thread = (ThreadContext *)calloc (sizeof (ThreadContext), 1);
1604 thread->next = ctx->threads;
1605 ctx->threads = thread;
1606 thread->thread_id = thread_id;
1607 thread->last_time = 0;
1608 thread->stack_id = 0;
1609 thread->stack_size = 32;
1610 thread->stack = (MethodDesc **)malloc (thread->stack_size * sizeof (void*));
1611 thread->time_stack = (uint64_t *)malloc (thread->stack_size * sizeof (uint64_t));
1612 thread->callee_time_stack = (uint64_t *)malloc (thread->stack_size * sizeof (uint64_t));
1613 return thread;
1616 static DomainContext *
1617 get_domain (ProfContext *ctx, intptr_t domain_id)
1619 if (ctx->current_domain && ctx->current_domain->domain_id == domain_id)
1620 return ctx->current_domain;
1622 DomainContext *domain = ctx->domains;
1624 while (domain) {
1625 if (domain->domain_id == domain_id)
1626 return domain;
1628 domain = domain->next;
1631 domain = (DomainContext *)calloc (sizeof (DomainContext), 1);
1632 domain->next = ctx->domains;
1633 ctx->domains = domain;
1634 domain->domain_id = domain_id;
1636 return domain;
1639 static RemCtxContext *
1640 get_remctx (ProfContext *ctx, intptr_t remctx_id)
1642 if (ctx->current_remctx && ctx->current_remctx->remctx_id == remctx_id)
1643 return ctx->current_remctx;
1645 RemCtxContext *remctx = ctx->remctxs;
1647 while (remctx) {
1648 if (remctx->remctx_id == remctx_id)
1649 return remctx;
1651 remctx = remctx->next;
1654 remctx = (RemCtxContext *)calloc (sizeof (RemCtxContext), 1);
1655 remctx->next = ctx->remctxs;
1656 ctx->remctxs = remctx;
1657 remctx->remctx_id = remctx_id;
1659 return remctx;
1662 static ThreadContext*
1663 load_thread (ProfContext *ctx, intptr_t thread_id)
1665 ThreadContext *thread = get_thread (ctx, thread_id);
1666 ctx->current_thread = thread;
1667 return thread;
1670 static void
1671 ensure_thread_stack (ThreadContext *thread)
1673 if (thread->stack_id == thread->stack_size) {
1674 thread->stack_size *= 2;
1675 thread->stack = (MethodDesc **)realloc (thread->stack, thread->stack_size * sizeof (void*));
1676 thread->time_stack = (uint64_t *)realloc (thread->time_stack, thread->stack_size * sizeof (uint64_t));
1677 thread->callee_time_stack = (uint64_t *)realloc (thread->callee_time_stack, thread->stack_size * sizeof (uint64_t));
1681 static int
1682 add_trace_hashed (CallContext *traces, int size, BackTrace *bt, uint64_t value)
1684 int i;
1685 unsigned int start_pos;
1686 start_pos = bt->hash % size;
1687 i = start_pos;
1688 do {
1689 if (traces [i].bt == bt) {
1690 traces [i].count += value;
1691 return 0;
1692 } else if (!traces [i].bt) {
1693 traces [i].bt = bt;
1694 traces [i].count += value;
1695 return 1;
1697 /* wrap around */
1698 if (++i == size)
1699 i = 0;
1700 } while (i != start_pos);
1701 /* should not happen */
1702 printf ("failed trace store\n");
1703 return 0;
1706 static void
1707 add_trace_bt (BackTrace *bt, TraceDesc *trace, uint64_t value)
1709 int i;
1710 if (!collect_traces)
1711 return;
1712 if (trace->count * 2 >= trace->size) {
1713 CallContext *n;
1714 int old_size = trace->size;
1715 trace->size *= 2;
1716 if (trace->size == 0)
1717 trace->size = 4;
1718 n = (CallContext *)calloc (sizeof (CallContext) * trace->size, 1);
1719 for (i = 0; i < old_size; ++i) {
1720 if (trace->traces [i].bt)
1721 add_trace_hashed (n, trace->size, trace->traces [i].bt, trace->traces [i].count);
1723 if (trace->traces)
1724 free (trace->traces);
1725 trace->traces = n;
1727 trace->count += add_trace_hashed (trace->traces, trace->size, bt, value);
1730 static BackTrace*
1731 add_trace_thread (ThreadContext *thread, TraceDesc *trace, uint64_t value)
1733 BackTrace *bt;
1734 int count = thread->stack_id;
1735 if (!collect_traces)
1736 return NULL;
1737 if (count > trace_max)
1738 count = trace_max;
1739 bt = add_backtrace (count, thread->stack + thread->stack_id - count);
1740 add_trace_bt (bt, trace, value);
1741 return bt;
1744 static BackTrace*
1745 add_trace_methods (MethodDesc **methods, int count, TraceDesc *trace, uint64_t value)
1747 BackTrace *bt;
1748 if (!collect_traces)
1749 return NULL;
1750 if (count > trace_max)
1751 count = trace_max;
1752 bt = add_backtrace (count, methods);
1753 add_trace_bt (bt, trace, value);
1754 return bt;
1757 static void
1758 thread_add_root (ThreadContext *ctx, uintptr_t obj, int root_type, uintptr_t extra_info)
1760 if (ctx->num_roots == ctx->size_roots) {
1761 int new_size = ctx->size_roots * 2;
1762 if (!new_size)
1763 new_size = 4;
1764 ctx->roots = (uintptr_t *)realloc (ctx->roots, new_size * sizeof (uintptr_t));
1765 ctx->roots_extra = (uintptr_t *)realloc (ctx->roots_extra, new_size * sizeof (uintptr_t));
1766 ctx->roots_types = (int *)realloc (ctx->roots_types, new_size * sizeof (int));
1767 ctx->size_roots = new_size;
1769 ctx->roots_types [ctx->num_roots] = root_type;
1770 ctx->roots_extra [ctx->num_roots] = extra_info;
1771 ctx->roots [ctx->num_roots++] = obj;
1774 static int
1775 compare_callc (const void *a, const void *b)
1777 const CallContext *A = (const CallContext *)a;
1778 const CallContext *B = (const CallContext *)b;
1779 if (B->count == A->count)
1780 return 0;
1781 if (B->count < A->count)
1782 return -1;
1783 return 1;
1786 static void
1787 sort_context_array (TraceDesc* traces)
1789 int i, j;
1790 for (i = 0, j = 0; i < traces->size; ++i) {
1791 if (traces->traces [i].bt) {
1792 traces->traces [j].bt = traces->traces [i].bt;
1793 traces->traces [j].count = traces->traces [i].count;
1794 j++;
1797 qsort (traces->traces, traces->count, sizeof (CallContext), compare_callc);
1800 static void
1801 push_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
1803 ensure_thread_stack (thread);
1804 thread->time_stack [thread->stack_id] = timestamp;
1805 thread->callee_time_stack [thread->stack_id] = 0;
1806 thread->stack [thread->stack_id++] = method;
1807 method->recurse_count++;
1810 static void
1811 pop_method (ThreadContext *thread, MethodDesc *method, uint64_t timestamp)
1813 method->recurse_count--;
1814 if (thread->stack_id > 0 && thread->stack [thread->stack_id - 1] == method) {
1815 uint64_t tdiff;
1816 thread->stack_id--;
1817 method->calls++;
1818 if (timestamp < thread->time_stack [thread->stack_id])
1819 fprintf (outfile, "time went backwards for %s\n", method->name);
1820 tdiff = timestamp - thread->time_stack [thread->stack_id];
1821 if (thread->callee_time_stack [thread->stack_id] > tdiff)
1822 fprintf (outfile, "callee time bigger for %s\n", method->name);
1823 method->self_time += tdiff - thread->callee_time_stack [thread->stack_id];
1824 method->callee_time += thread->callee_time_stack [thread->stack_id];
1825 if (thread->stack_id)
1826 thread->callee_time_stack [thread->stack_id - 1] += tdiff;
1827 //fprintf (outfile, "method %s took %d\n", method->name, (int)(tdiff/1000));
1828 } else {
1829 fprintf (outfile, "unmatched leave at stack pos: %d for method %s\n", thread->stack_id, method->name);
1833 typedef struct {
1834 uint64_t total_time;
1835 uint64_t max_time;
1836 int count;
1837 } GCDesc;
1838 static GCDesc gc_info [3];
1839 static uint64_t max_heap_size;
1840 static uint64_t gc_object_moves;
1841 static int gc_resizes;
1842 typedef struct {
1843 uint64_t created;
1844 uint64_t destroyed;
1845 uint64_t live;
1846 uint64_t max_live;
1847 TraceDesc traces;
1848 TraceDesc destroy_traces;
1849 } HandleInfo;
1850 static HandleInfo handle_info [4];
1852 static const char*
1853 gc_event_name (int ev)
1855 switch (ev) {
1856 case MONO_GC_EVENT_START: return "start";
1857 case MONO_GC_EVENT_MARK_START: return "mark start";
1858 case MONO_GC_EVENT_MARK_END: return "mark end";
1859 case MONO_GC_EVENT_RECLAIM_START: return "reclaim start";
1860 case MONO_GC_EVENT_RECLAIM_END: return "reclaim end";
1861 case MONO_GC_EVENT_END: return "end";
1862 case MONO_GC_EVENT_PRE_STOP_WORLD: return "pre stop";
1863 case MONO_GC_EVENT_POST_STOP_WORLD: return "post stop";
1864 case MONO_GC_EVENT_PRE_START_WORLD: return "pre start";
1865 case MONO_GC_EVENT_POST_START_WORLD: return "post start";
1866 default:
1867 return "unknown";
1871 static uint64_t clause_summary [MONO_EXCEPTION_CLAUSE_FAULT + 1];
1872 static uint64_t throw_count = 0;
1873 static TraceDesc exc_traces;
1875 static const char*
1876 clause_name (int type)
1878 switch (type) {
1879 case MONO_EXCEPTION_CLAUSE_NONE: return "catch";
1880 case MONO_EXCEPTION_CLAUSE_FILTER: return "filter";
1881 case MONO_EXCEPTION_CLAUSE_FINALLY: return "finally";
1882 case MONO_EXCEPTION_CLAUSE_FAULT: return "fault";
1883 default: return "invalid";
1887 static uint64_t monitor_contention;
1888 static uint64_t monitor_failed;
1889 static uint64_t monitor_acquired;
1891 struct _MonitorDesc {
1892 MonitorDesc *next;
1893 uintptr_t objid;
1894 uintptr_t contentions;
1895 uint64_t wait_time;
1896 uint64_t max_wait_time;
1897 TraceDesc traces;
1900 static MonitorDesc* monitor_hash [SMALL_HASH_SIZE] = {0};
1901 static int num_monitors = 0;
1903 static MonitorDesc*
1904 lookup_monitor (uintptr_t objid)
1906 int slot = ((objid >> 3) & 0xffff) % SMALL_HASH_SIZE;
1907 MonitorDesc *cd = monitor_hash [slot];
1908 while (cd && cd->objid != objid)
1909 cd = cd->next;
1910 if (!cd) {
1911 cd = (MonitorDesc *)calloc (sizeof (MonitorDesc), 1);
1912 cd->objid = objid;
1913 cd->next = monitor_hash [slot];
1914 monitor_hash [slot] = cd;
1915 num_monitors++;
1917 return cd;
1920 static const char*
1921 monitor_ev_name (int ev)
1923 switch (ev) {
1924 case MONO_PROFILER_MONITOR_CONTENTION: return "contended";
1925 case MONO_PROFILER_MONITOR_DONE: return "acquired";
1926 case MONO_PROFILER_MONITOR_FAIL: return "not taken";
1927 default: return "invalid";
1931 static const char*
1932 get_handle_name (int htype)
1934 switch (htype) {
1935 case 0: return "weak";
1936 case 1: return "weaktrack";
1937 case 2: return "normal";
1938 case 3: return "pinned";
1939 default: return "unknown";
1943 static const char*
1944 get_root_name (int rtype)
1946 switch (rtype & MONO_PROFILE_GC_ROOT_TYPEMASK) {
1947 case MONO_PROFILE_GC_ROOT_STACK: return "stack";
1948 case MONO_PROFILE_GC_ROOT_FINALIZER: return "finalizer";
1949 case MONO_PROFILE_GC_ROOT_HANDLE: return "handle";
1950 case MONO_PROFILE_GC_ROOT_OTHER: return "other";
1951 case MONO_PROFILE_GC_ROOT_MISC: return "misc";
1952 default: return "unknown";
1956 static MethodDesc**
1957 decode_bt (MethodDesc** sframes, int *size, unsigned char *p, unsigned char **endp, intptr_t ptr_base)
1959 MethodDesc **frames;
1960 int i;
1961 int flags = decode_uleb128 (p, &p);
1962 int count = decode_uleb128 (p, &p);
1963 if (flags != 0)
1964 return NULL;
1965 if (count > *size)
1966 frames = (MethodDesc **)malloc (count * sizeof (void*));
1967 else
1968 frames = sframes;
1969 for (i = 0; i < count; ++i) {
1970 intptr_t ptrdiff = decode_sleb128 (p, &p);
1971 frames [i] = lookup_method (ptr_base + ptrdiff);
1973 *size = count;
1974 *endp = p;
1975 return frames;
1978 static void
1979 tracked_creation (uintptr_t obj, ClassDesc *cd, uint64_t size, BackTrace *bt, uint64_t timestamp)
1981 int i;
1982 for (i = 0; i < num_tracked_objects; ++i) {
1983 if (tracked_objects [i] != obj)
1984 continue;
1985 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);
1986 if (bt && bt->count) {
1987 int k;
1988 for (k = 0; k < bt->count; ++k)
1989 fprintf (outfile, "\t%s\n", bt->methods [k]->name);
1994 static void
1995 track_handle (uintptr_t obj, int htype, uint32_t handle, BackTrace *bt, uint64_t timestamp)
1997 int i;
1998 for (i = 0; i < num_tracked_objects; ++i) {
1999 if (tracked_objects [i] != obj)
2000 continue;
2001 fprintf (outfile, "Object %p referenced from handle %u at %.3f secs.\n", (void*)obj, handle, (timestamp - startup_time) / 1000000000.0);
2002 if (bt && bt->count) {
2003 int k;
2004 for (k = 0; k < bt->count; ++k)
2005 fprintf (outfile, "\t%s\n", bt->methods [k]->name);
2010 static void
2011 track_move (uintptr_t src, uintptr_t dst)
2013 int i;
2014 for (i = 0; i < num_tracked_objects; ++i) {
2015 if (tracked_objects [i] == src)
2016 fprintf (outfile, "Object %p moved to %p\n", (void*)src, (void*)dst);
2017 else if (tracked_objects [i] == dst)
2018 fprintf (outfile, "Object %p moved from %p\n", (void*)dst, (void*)src);
2022 static void
2023 track_obj_reference (uintptr_t obj, uintptr_t parent, ClassDesc *cd)
2025 int i;
2026 for (i = 0; i < num_tracked_objects; ++i) {
2027 if (tracked_objects [i] == obj)
2028 fprintf (outfile, "Object %p referenced from %p (%s).\n", (void*)obj, (void*)parent, cd->name);
2032 static void
2033 found_object (uintptr_t obj)
2035 num_tracked_objects ++;
2036 tracked_objects = (uintptr_t *)realloc (tracked_objects, num_tracked_objects * sizeof (tracked_objects [0]));
2037 tracked_objects [num_tracked_objects - 1] = obj;
2040 static int num_jit_helpers = 0;
2041 static int jit_helpers_code_size = 0;
2043 static const char*
2044 code_buffer_desc (int type)
2046 switch (type) {
2047 case MONO_PROFILER_CODE_BUFFER_METHOD:
2048 return "method";
2049 case MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE:
2050 return "method trampoline";
2051 case MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE:
2052 return "unbox trampoline";
2053 case MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE:
2054 return "imt trampoline";
2055 case MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE:
2056 return "generics trampoline";
2057 case MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE:
2058 return "specific trampoline";
2059 case MONO_PROFILER_CODE_BUFFER_HELPER:
2060 return "misc helper";
2061 case MONO_PROFILER_CODE_BUFFER_MONITOR:
2062 return "monitor/lock";
2063 case MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE:
2064 return "delegate invoke";
2065 case MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING:
2066 return "exception handling";
2067 default:
2068 return "unspecified";
2072 typedef struct _CoverageAssembly CoverageAssembly;
2073 struct _CoverageAssembly {
2074 char *name;
2075 char *guid;
2076 char *filename;
2077 int number_of_methods;
2078 int fully_covered;
2079 int partially_covered;
2082 typedef struct _CoverageClass CoverageClass;
2083 struct _CoverageClass {
2084 char *assembly_name;
2085 char *class_name;
2086 int number_of_methods;
2087 int fully_covered;
2088 int partially_covered;
2091 typedef struct _CoverageCoverage CoverageCoverage;
2092 struct _CoverageCoverage {
2093 int method_id;
2094 int offset;
2095 int count;
2096 int line;
2097 int column;
2100 typedef struct _CoverageMethod CoverageMethod;
2101 struct _CoverageMethod {
2102 char *assembly_name;
2103 char *class_name;
2104 char *method_name;
2105 char *method_signature;
2106 char *filename;
2107 int token;
2108 int n_statements;
2109 int method_id;
2110 GPtrArray *coverage;
2112 static GPtrArray *coverage_assemblies = NULL;
2113 static GPtrArray *coverage_methods = NULL;
2114 static GPtrArray *coverage_statements = NULL;
2115 static GHashTable *coverage_methods_hash = NULL;
2116 static GPtrArray *coverage_classes = NULL;
2117 static GHashTable *coverage_assembly_classes = NULL;
2119 static void
2120 gather_coverage_statements (void)
2122 for (guint i = 0; i < coverage_statements->len; i++) {
2123 CoverageCoverage *coverage = (CoverageCoverage *)coverage_statements->pdata[i];
2124 CoverageMethod *method = (CoverageMethod *)g_hash_table_lookup (coverage_methods_hash, GINT_TO_POINTER (coverage->method_id));
2125 if (method == NULL) {
2126 fprintf (outfile, "Cannot find method with ID: %d\n", coverage->method_id);
2127 continue;
2130 g_ptr_array_add (method->coverage, coverage);
2134 static void
2135 coverage_add_assembly (CoverageAssembly *assembly)
2137 if (coverage_assemblies == NULL)
2138 coverage_assemblies = g_ptr_array_new ();
2140 g_ptr_array_add (coverage_assemblies, assembly);
2143 static void
2144 coverage_add_method (CoverageMethod *method)
2146 if (coverage_methods == NULL) {
2147 coverage_methods = g_ptr_array_new ();
2148 coverage_methods_hash = g_hash_table_new (NULL, NULL);
2151 g_ptr_array_add (coverage_methods, method);
2152 g_hash_table_insert (coverage_methods_hash, GINT_TO_POINTER (method->method_id), method);
2155 static void
2156 coverage_add_class (CoverageClass *klass)
2158 GPtrArray *classes = NULL;
2160 if (coverage_classes == NULL) {
2161 coverage_classes = g_ptr_array_new ();
2162 coverage_assembly_classes = g_hash_table_new (g_str_hash, g_str_equal);
2165 g_ptr_array_add (coverage_classes, klass);
2166 classes = (GPtrArray *)g_hash_table_lookup (coverage_assembly_classes, klass->assembly_name);
2167 if (classes == NULL) {
2168 classes = g_ptr_array_new ();
2169 g_hash_table_insert (coverage_assembly_classes, klass->assembly_name, classes);
2171 g_ptr_array_add (classes, klass);
2174 static void
2175 coverage_add_coverage (CoverageCoverage *coverage)
2177 if (coverage_statements == NULL)
2178 coverage_statements = g_ptr_array_new ();
2180 g_ptr_array_add (coverage_statements, coverage);
2183 #define OBJ_ADDR(diff) ((obj_base + diff) << 3)
2184 #define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
2187 /* Stats */
2188 #define BUFFER_HEADER_SIZE 48
2190 typedef struct {
2191 int count, min_size, max_size, bytes;
2192 } EventStat;
2194 static int buffer_count;
2195 static EventStat stats [256];
2197 static void
2198 record_event_stats (int type, int size)
2200 ++stats [type].count;
2201 if (!stats [type].min_size)
2202 stats [type].min_size = size;
2203 stats [type].min_size = MIN (stats [type].min_size, size);
2204 stats [type].max_size = MAX (stats [type].max_size, size);
2205 stats [type].bytes += size;
2208 static int
2209 decode_buffer (ProfContext *ctx)
2211 unsigned char *p;
2212 unsigned char *end;
2213 intptr_t thread_id;
2214 intptr_t ptr_base;
2215 intptr_t obj_base;
2216 intptr_t method_base;
2217 uint64_t time_base;
2218 uint64_t file_offset;
2219 int len, i;
2220 ThreadContext *thread;
2222 #ifdef HAVE_SYS_ZLIB
2223 if (ctx->gzfile)
2224 file_offset = gztell (ctx->gzfile);
2225 else
2226 #endif
2227 file_offset = ftell (ctx->file);
2228 if (!load_data (ctx, 48))
2229 return 0;
2230 p = ctx->buf;
2231 if (read_int32 (p) != BUF_ID) {
2232 fprintf (outfile, "Incorrect buffer id: 0x%x\n", read_int32 (p));
2233 for (i = 0; i < 48; ++i) {
2234 fprintf (outfile, "0x%x%s", p [i], i % 8?" ":"\n");
2236 return 0;
2238 len = read_int32 (p + 4);
2239 time_base = read_int64 (p + 8);
2240 ptr_base = read_int64 (p + 16);
2241 obj_base = read_int64 (p + 24);
2242 thread_id = read_int64 (p + 32);
2243 method_base = read_int64 (p + 40);
2244 if (debug)
2245 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);
2246 thread = load_thread (ctx, thread_id);
2247 if (!load_data (ctx, len))
2248 return 0;
2250 ++buffer_count;
2252 if (!startup_time) {
2253 startup_time = time_base;
2254 if (use_time_filter) {
2255 time_from += startup_time;
2256 time_to += startup_time;
2258 if (!thread->name)
2259 thread->name = pstrdup ("Main");
2261 for (i = 0; i < thread->stack_id; ++i)
2262 thread->stack [i]->recurse_count++;
2263 p = ctx->buf;
2264 end = p + len;
2265 while (p < end) {
2266 unsigned char *start = p;
2267 unsigned char event = *p;
2268 switch (*p & 0xf) {
2269 case TYPE_GC: {
2270 int subtype = *p & 0xf0;
2271 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2272 LOG_TIME (time_base, tdiff);
2273 time_base += tdiff;
2274 if (subtype == TYPE_GC_RESIZE) {
2275 uint64_t new_size = decode_uleb128 (p, &p);
2276 if (debug)
2277 fprintf (outfile, "gc heap resized to %llu\n", (unsigned long long) new_size);
2278 gc_resizes++;
2279 if (new_size > max_heap_size)
2280 max_heap_size = new_size;
2281 } else if (subtype == TYPE_GC_EVENT) {
2282 uint64_t ev = decode_uleb128 (p, &p);
2283 int gen = decode_uleb128 (p, &p);
2284 if (debug)
2285 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);
2286 if (gen > 2) {
2287 fprintf (outfile, "incorrect gc gen: %d\n", gen);
2288 break;
2290 if (ev == MONO_GC_EVENT_START) {
2291 thread->gc_start_times [gen] = time_base;
2292 gc_info [gen].count++;
2293 } else if (ev == MONO_GC_EVENT_END) {
2294 tdiff = time_base - thread->gc_start_times [gen];
2295 gc_info [gen].total_time += tdiff;
2296 if (tdiff > gc_info [gen].max_time)
2297 gc_info [gen].max_time = tdiff;
2299 } else if (subtype == TYPE_GC_MOVE) {
2300 int j, num = decode_uleb128 (p, &p);
2301 gc_object_moves += num / 2;
2302 for (j = 0; j < num; j += 2) {
2303 intptr_t obj1diff = decode_sleb128 (p, &p);
2304 intptr_t obj2diff = decode_sleb128 (p, &p);
2305 if (num_tracked_objects)
2306 track_move (OBJ_ADDR (obj1diff), OBJ_ADDR (obj2diff));
2307 if (debug) {
2308 fprintf (outfile, "moved obj %p to %p\n", (void*)OBJ_ADDR (obj1diff), (void*)OBJ_ADDR (obj2diff));
2311 } else if (subtype == TYPE_GC_HANDLE_CREATED || subtype == TYPE_GC_HANDLE_CREATED_BT) {
2312 int has_bt = subtype == TYPE_GC_HANDLE_CREATED_BT;
2313 int num_bt = 0;
2314 MethodDesc *sframes [8];
2315 MethodDesc **frames = sframes;
2316 int htype = decode_uleb128 (p, &p);
2317 uint32_t handle = decode_uleb128 (p, &p);
2318 intptr_t objdiff = decode_sleb128 (p, &p);
2319 if (has_bt) {
2320 num_bt = 8;
2321 frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
2322 if (!frames) {
2323 fprintf (outfile, "Cannot load backtrace\n");
2324 return 0;
2327 if (htype > 3)
2328 return 0;
2329 if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2330 handle_info [htype].created++;
2331 handle_info [htype].live++;
2332 if (handle_info [htype].live > handle_info [htype].max_live)
2333 handle_info [htype].max_live = handle_info [htype].live;
2334 BackTrace *bt;
2335 if (has_bt)
2336 bt = add_trace_methods (frames, num_bt, &handle_info [htype].traces, 1);
2337 else
2338 bt = add_trace_thread (thread, &handle_info [htype].traces, 1);
2339 if (num_tracked_objects)
2340 track_handle (OBJ_ADDR (objdiff), htype, handle, bt, time_base);
2342 if (debug)
2343 fprintf (outfile, "handle (%s) %u created for object %p\n", get_handle_name (htype), handle, (void*)OBJ_ADDR (objdiff));
2344 if (frames != sframes)
2345 free (frames);
2346 } else if (subtype == TYPE_GC_HANDLE_DESTROYED || subtype == TYPE_GC_HANDLE_DESTROYED_BT) {
2347 int has_bt = subtype == TYPE_GC_HANDLE_DESTROYED_BT;
2348 int num_bt = 0;
2349 MethodDesc *sframes [8];
2350 MethodDesc **frames = sframes;
2351 int htype = decode_uleb128 (p, &p);
2352 uint32_t handle = decode_uleb128 (p, &p);
2353 if (has_bt) {
2354 num_bt = 8;
2355 frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
2356 if (!frames) {
2357 fprintf (outfile, "Cannot load backtrace\n");
2358 return 0;
2361 if (htype > 3)
2362 return 0;
2363 if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2364 handle_info [htype].destroyed ++;
2365 handle_info [htype].live--;
2366 BackTrace *bt;
2367 if (has_bt)
2368 bt = add_trace_methods (frames, num_bt, &handle_info [htype].destroy_traces, 1);
2369 else
2370 bt = add_trace_thread (thread, &handle_info [htype].destroy_traces, 1);
2371 /* TODO: track_handle_free () - would need to record and keep track of the associated object address... */
2373 if (debug)
2374 fprintf (outfile, "handle (%s) %u destroyed\n", get_handle_name (htype), handle);
2375 if (frames != sframes)
2376 free (frames);
2378 break;
2380 case TYPE_METADATA: {
2381 int subtype = *p & 0xf0;
2382 const char *load_str = subtype == TYPE_END_LOAD ? "loaded" : "unloaded";
2383 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2384 int mtype = *p++;
2385 intptr_t ptrdiff = decode_sleb128 (p, &p);
2386 LOG_TIME (time_base, tdiff);
2387 time_base += tdiff;
2388 if (mtype == TYPE_CLASS) {
2389 intptr_t imptrdiff = decode_sleb128 (p, &p);
2390 uint64_t flags = decode_uleb128 (p, &p);
2391 if (flags) {
2392 fprintf (outfile, "non-zero flags in class\n");
2393 return 0;
2395 if (debug)
2396 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);
2397 if (subtype == TYPE_END_LOAD)
2398 add_class (ptr_base + ptrdiff, (char*)p);
2399 while (*p) p++;
2400 p++;
2401 } else if (mtype == TYPE_IMAGE) {
2402 uint64_t flags = decode_uleb128 (p, &p);
2403 if (flags) {
2404 fprintf (outfile, "non-zero flags in image\n");
2405 return 0;
2407 if (debug)
2408 fprintf (outfile, "%s image %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
2409 if (subtype == TYPE_END_LOAD)
2410 add_image (ptr_base + ptrdiff, (char*)p);
2411 while (*p) p++;
2412 p++;
2413 } else if (mtype == TYPE_ASSEMBLY) {
2414 uint64_t flags = decode_uleb128 (p, &p);
2415 if (flags) {
2416 fprintf (outfile, "non-zero flags in assembly\n");
2417 return 0;
2419 if (debug)
2420 fprintf (outfile, "%s assembly %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
2421 if (subtype == TYPE_END_LOAD)
2422 add_assembly (ptr_base + ptrdiff, (char*)p);
2423 while (*p) p++;
2424 p++;
2425 } else if (mtype == TYPE_DOMAIN) {
2426 uint64_t flags = decode_uleb128 (p, &p);
2427 if (flags) {
2428 fprintf (outfile, "non-zero flags in domain\n");
2429 return 0;
2431 DomainContext *nd = get_domain (ctx, ptr_base + ptrdiff);
2432 /* no subtype means it's a name event, rather than start/stop */
2433 if (subtype == 0)
2434 nd->friendly_name = pstrdup ((char *) p);
2435 if (debug) {
2436 if (subtype == 0)
2437 fprintf (outfile, "domain %p named at %llu: %s\n", (void *) (ptr_base + ptrdiff), (unsigned long long) time_base, p);
2438 else
2439 fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
2441 if (subtype == 0) {
2442 while (*p) p++;
2443 p++;
2445 } else if (mtype == TYPE_CONTEXT) {
2446 uint64_t flags = decode_uleb128 (p, &p);
2447 if (flags) {
2448 fprintf (outfile, "non-zero flags in context\n");
2449 return 0;
2451 intptr_t domaindiff = decode_sleb128 (p, &p);
2452 if (debug)
2453 fprintf (outfile, "%s context %p (%p) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), (void *) (ptr_base + domaindiff), (unsigned long long) time_base);
2454 if (subtype == TYPE_END_LOAD)
2455 get_remctx (ctx, ptr_base + ptrdiff)->domain_id = ptr_base + domaindiff;
2456 } else if (mtype == TYPE_THREAD) {
2457 uint64_t flags = decode_uleb128 (p, &p);
2458 if (flags) {
2459 fprintf (outfile, "non-zero flags in thread\n");
2460 return 0;
2462 ThreadContext *nt = get_thread (ctx, ptr_base + ptrdiff);
2463 /* no subtype means it's a name event, rather than start/stop */
2464 if (subtype == 0)
2465 nt->name = pstrdup ((char*)p);
2466 if (debug) {
2467 if (subtype == 0)
2468 fprintf (outfile, "thread %p named at %llu: %s\n", (void*)(ptr_base + ptrdiff), (unsigned long long) time_base, p);
2469 else
2470 fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
2472 if (subtype == 0) {
2473 while (*p) p++;
2474 p++;
2477 break;
2479 case TYPE_ALLOC: {
2480 int has_bt = *p & TYPE_ALLOC_BT;
2481 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2482 intptr_t ptrdiff = decode_sleb128 (p, &p);
2483 intptr_t objdiff = decode_sleb128 (p, &p);
2484 uint64_t len;
2485 int num_bt = 0;
2486 MethodDesc* sframes [8];
2487 MethodDesc** frames = sframes;
2488 ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
2489 len = decode_uleb128 (p, &p);
2490 LOG_TIME (time_base, tdiff);
2491 time_base += tdiff;
2492 if (debug)
2493 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);
2494 if (has_bt) {
2495 num_bt = 8;
2496 frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
2497 if (!frames) {
2498 fprintf (outfile, "Cannot load backtrace\n");
2499 return 0;
2502 if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
2503 BackTrace *bt;
2504 cd->allocs++;
2505 cd->alloc_size += len;
2506 if (has_bt)
2507 bt = add_trace_methods (frames, num_bt, &cd->traces, len);
2508 else
2509 bt = add_trace_thread (thread, &cd->traces, len);
2510 if (find_size && len >= find_size) {
2511 if (!find_name || strstr (cd->name, find_name))
2512 found_object (OBJ_ADDR (objdiff));
2513 } else if (!find_size && find_name && strstr (cd->name, find_name)) {
2514 found_object (OBJ_ADDR (objdiff));
2516 if (num_tracked_objects)
2517 tracked_creation (OBJ_ADDR (objdiff), cd, len, bt, time_base);
2519 if (frames != sframes)
2520 free (frames);
2521 break;
2523 case TYPE_METHOD: {
2524 int subtype = *p & 0xf0;
2525 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2526 int64_t ptrdiff = decode_sleb128 (p, &p);
2527 LOG_TIME (time_base, tdiff);
2528 time_base += tdiff;
2529 method_base += ptrdiff;
2530 if (subtype == TYPE_JIT) {
2531 intptr_t codediff = decode_sleb128 (p, &p);
2532 int codelen = decode_uleb128 (p, &p);
2533 MethodDesc *jitted_method;
2534 if (debug)
2535 fprintf (outfile, "jitted method %p (%s), size: %d, code: %p\n", (void*)(method_base), p, codelen, (void*)(ptr_base + codediff));
2536 jitted_method = add_method (method_base, (char*)p, ptr_base + codediff, codelen);
2537 if (!(time_base >= time_from && time_base < time_to))
2538 jitted_method->ignore_jit = 1;
2539 while (*p) p++;
2540 p++;
2541 } else {
2542 MethodDesc *method;
2543 if ((thread_filter && thread_filter != thread->thread_id))
2544 break;
2545 if (!(time_base >= time_from && time_base < time_to))
2546 break;
2547 method = lookup_method (method_base);
2548 if (subtype == TYPE_ENTER) {
2549 add_trace_thread (thread, &method->traces, 1);
2550 push_method (thread, method, time_base);
2551 } else {
2552 pop_method (thread, method, time_base);
2554 if (debug)
2555 fprintf (outfile, "%s method %s\n", subtype == TYPE_ENTER? "enter": subtype == TYPE_EXC_LEAVE? "exleave": "leave", method->name);
2557 break;
2559 case TYPE_HEAP: {
2560 int subtype = *p & 0xf0;
2561 if (subtype == TYPE_HEAP_OBJECT) {
2562 HeapObjectDesc *ho = NULL;
2563 int i;
2564 intptr_t objdiff = decode_sleb128 (p + 1, &p);
2565 intptr_t ptrdiff = decode_sleb128 (p, &p);
2566 uint64_t size = decode_uleb128 (p, &p);
2567 uintptr_t num = decode_uleb128 (p, &p);
2568 uintptr_t ref_offset = 0;
2569 uintptr_t last_obj_offset = 0;
2570 ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
2571 if (size) {
2572 HeapClassDesc *hcd = add_heap_shot_class (thread->current_heap_shot, cd, size);
2573 if (collect_traces) {
2574 ho = alloc_heap_obj (OBJ_ADDR (objdiff), hcd, num);
2575 add_heap_shot_obj (thread->current_heap_shot, ho);
2576 ref_offset = 0;
2578 } else {
2579 if (collect_traces)
2580 ho = heap_shot_obj_add_refs (thread->current_heap_shot, OBJ_ADDR (objdiff), num, &ref_offset);
2582 for (i = 0; i < num; ++i) {
2583 /* FIXME: use object distance to measure how good
2584 * the GC is at keeping related objects close
2586 uintptr_t offset = ctx->data_version > 1? last_obj_offset + decode_uleb128 (p, &p): -1;
2587 intptr_t obj1diff = decode_sleb128 (p, &p);
2588 last_obj_offset = offset;
2589 if (collect_traces)
2590 ho->refs [ref_offset + i] = OBJ_ADDR (obj1diff);
2591 if (num_tracked_objects)
2592 track_obj_reference (OBJ_ADDR (obj1diff), OBJ_ADDR (objdiff), cd);
2594 if (debug && size)
2595 fprintf (outfile, "traced object %p, size %llu (%s), refs: %zd\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) size, cd->name, num);
2596 } else if (subtype == TYPE_HEAP_ROOT) {
2597 uintptr_t num = decode_uleb128 (p + 1, &p);
2598 uintptr_t gc_num G_GNUC_UNUSED = decode_uleb128 (p, &p);
2599 int i;
2600 for (i = 0; i < num; ++i) {
2601 intptr_t objdiff = decode_sleb128 (p, &p);
2602 int root_type = decode_uleb128 (p, &p);
2603 /* we just discard the extra info for now */
2604 uintptr_t extra_info = decode_uleb128 (p, &p);
2605 if (debug)
2606 fprintf (outfile, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff), get_root_name (root_type));
2607 if (collect_traces)
2608 thread_add_root (thread, OBJ_ADDR (objdiff), root_type, extra_info);
2610 } else if (subtype == TYPE_HEAP_END) {
2611 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2612 LOG_TIME (time_base, tdiff);
2613 time_base += tdiff;
2614 if (debug)
2615 fprintf (outfile, "heap shot end\n");
2616 if (collect_traces) {
2617 HeapShot *hs = thread->current_heap_shot;
2618 if (hs && thread->num_roots) {
2619 /* transfer the root ownershipt to the heapshot */
2620 hs->num_roots = thread->num_roots;
2621 hs->roots = thread->roots;
2622 hs->roots_extra = thread->roots_extra;
2623 hs->roots_types = thread->roots_types;
2624 } else {
2625 free (thread->roots);
2626 free (thread->roots_extra);
2627 free (thread->roots_types);
2629 thread->num_roots = 0;
2630 thread->size_roots = 0;
2631 thread->roots = NULL;
2632 thread->roots_extra = NULL;
2633 thread->roots_types = NULL;
2634 heap_shot_resolve_reverse_refs (hs);
2635 heap_shot_mark_objects (hs);
2636 heap_shot_free_objects (hs);
2638 thread->current_heap_shot = NULL;
2639 } else if (subtype == TYPE_HEAP_START) {
2640 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2641 LOG_TIME (time_base, tdiff);
2642 time_base += tdiff;
2643 if (debug)
2644 fprintf (outfile, "heap shot start\n");
2645 thread->current_heap_shot = new_heap_shot (time_base);
2647 break;
2649 case TYPE_MONITOR: {
2650 int event = (*p >> 4) & 0x3;
2651 int has_bt = *p & TYPE_MONITOR_BT;
2652 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2653 intptr_t objdiff = decode_sleb128 (p, &p);
2654 MethodDesc* sframes [8];
2655 MethodDesc** frames = sframes;
2656 int record;
2657 int num_bt = 0;
2658 LOG_TIME (time_base, tdiff);
2659 time_base += tdiff;
2660 record = (!thread_filter || thread_filter == thread->thread_id);
2661 if (!(time_base >= time_from && time_base < time_to))
2662 record = 0;
2663 if (event == MONO_PROFILER_MONITOR_CONTENTION) {
2664 MonitorDesc *mdesc = lookup_monitor (OBJ_ADDR (objdiff));
2665 if (record) {
2666 monitor_contention++;
2667 mdesc->contentions++;
2668 thread->monitor = mdesc;
2669 thread->contention_start = time_base;
2671 if (has_bt) {
2672 num_bt = 8;
2673 frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
2674 if (!frames) {
2675 fprintf (outfile, "Cannot load backtrace\n");
2676 return 0;
2678 if (record)
2679 add_trace_methods (frames, num_bt, &mdesc->traces, 1);
2680 } else {
2681 if (record)
2682 add_trace_thread (thread, &mdesc->traces, 1);
2684 } else if (event == MONO_PROFILER_MONITOR_FAIL) {
2685 if (record) {
2686 monitor_failed++;
2687 if (thread->monitor && thread->contention_start) {
2688 uint64_t wait_time = time_base - thread->contention_start;
2689 if (wait_time > thread->monitor->max_wait_time)
2690 thread->monitor->max_wait_time = wait_time;
2691 thread->monitor->wait_time += wait_time;
2692 thread->monitor = NULL;
2693 thread->contention_start = 0;
2696 } else if (event == MONO_PROFILER_MONITOR_DONE) {
2697 if (record) {
2698 monitor_acquired++;
2699 if (thread->monitor && thread->contention_start) {
2700 uint64_t wait_time = time_base - thread->contention_start;
2701 if (wait_time > thread->monitor->max_wait_time)
2702 thread->monitor->max_wait_time = wait_time;
2703 thread->monitor->wait_time += wait_time;
2704 thread->monitor = NULL;
2705 thread->contention_start = 0;
2709 if (debug)
2710 fprintf (outfile, "monitor %s for object %p\n", monitor_ev_name (event), (void*)OBJ_ADDR (objdiff));
2711 if (frames != sframes)
2712 free (frames);
2713 break;
2715 case TYPE_EXCEPTION: {
2716 int subtype = *p & 0x70;
2717 int has_bt = *p & TYPE_EXCEPTION_BT;
2718 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2719 MethodDesc* sframes [8];
2720 MethodDesc** frames = sframes;
2721 int record;
2722 LOG_TIME (time_base, tdiff);
2723 time_base += tdiff;
2724 record = (!thread_filter || thread_filter == thread->thread_id);
2725 if (!(time_base >= time_from && time_base < time_to))
2726 record = 0;
2727 if (subtype == TYPE_CLAUSE) {
2728 int clause_type = decode_uleb128 (p, &p);
2729 int clause_num = decode_uleb128 (p, &p);
2730 int64_t ptrdiff = decode_sleb128 (p, &p);
2731 method_base += ptrdiff;
2732 if (record)
2733 clause_summary [clause_type]++;
2734 if (debug)
2735 fprintf (outfile, "clause %s (%d) in method %s\n", clause_name (clause_type), clause_num, lookup_method (method_base)->name);
2736 } else {
2737 intptr_t objdiff = decode_sleb128 (p, &p);
2738 if (record)
2739 throw_count++;
2740 if (has_bt) {
2741 has_bt = 8;
2742 frames = decode_bt (sframes, &has_bt, p, &p, ptr_base);
2743 if (!frames) {
2744 fprintf (outfile, "Cannot load backtrace\n");
2745 return 0;
2747 if (record)
2748 add_trace_methods (frames, has_bt, &exc_traces, 1);
2749 } else {
2750 if (record)
2751 add_trace_thread (thread, &exc_traces, 1);
2753 if (frames != sframes)
2754 free (frames);
2755 if (debug)
2756 fprintf (outfile, "throw %p\n", (void*)OBJ_ADDR (objdiff));
2758 break;
2760 case TYPE_RUNTIME: {
2761 int subtype = *p & 0xf0;
2762 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2763 LOG_TIME (time_base, tdiff);
2764 time_base += tdiff;
2765 if (subtype == TYPE_JITHELPER) {
2766 int type = decode_uleb128 (p, &p);
2767 intptr_t codediff = decode_sleb128 (p, &p);
2768 int codelen = decode_uleb128 (p, &p);
2769 const char *name;
2770 if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
2771 name = (const char *)p;
2772 while (*p) p++;
2773 p++;
2774 } else {
2775 name = code_buffer_desc (type);
2777 num_jit_helpers++;
2778 jit_helpers_code_size += codelen;
2779 if (debug)
2780 fprintf (outfile, "jit helper %s, size: %d, code: %p\n", name, codelen, (void*)(ptr_base + codediff));
2782 break;
2784 case TYPE_SAMPLE: {
2785 int subtype = *p & 0xf0;
2786 if (subtype == TYPE_SAMPLE_HIT) {
2787 int i;
2788 int sample_type = decode_uleb128 (p + 1, &p);
2789 uint64_t tstamp = decode_uleb128 (p, &p);
2790 void *tid = (void *) thread_id;
2791 if (ctx->data_version > 10)
2792 tid = (void *) (ptr_base + decode_sleb128 (p, &p));
2793 int count = decode_uleb128 (p, &p);
2794 for (i = 0; i < count; ++i) {
2795 uintptr_t ip = ptr_base + decode_sleb128 (p, &p);
2796 if ((tstamp >= time_from && tstamp < time_to))
2797 add_stat_sample (sample_type, ip);
2798 if (debug)
2799 fprintf (outfile, "sample hit, type: %d at %p for thread %p\n", sample_type, (void*)ip, tid);
2801 if (ctx->data_version > 5) {
2802 count = decode_uleb128 (p, &p);
2803 for (i = 0; i < count; ++i) {
2804 MethodDesc *method;
2805 int64_t ptrdiff = decode_sleb128 (p, &p);
2806 int il_offset = decode_sleb128 (p, &p);
2807 int native_offset = decode_sleb128 (p, &p);
2808 method_base += ptrdiff;
2809 method = lookup_method (method_base);
2810 if (debug)
2811 fprintf (outfile, "sample hit bt %d: %s at IL offset %d (native: %d)\n", i, method->name, il_offset, native_offset);
2814 } else if (subtype == TYPE_SAMPLE_USYM) {
2815 /* un unmanaged symbol description */
2816 uintptr_t addr = ptr_base + decode_sleb128 (p + 1, &p);
2817 uintptr_t size = decode_uleb128 (p, &p);
2818 char *name;
2819 name = pstrdup ((char*)p);
2820 add_unmanaged_symbol (addr, name, size);
2821 if (debug)
2822 fprintf (outfile, "unmanaged symbol %s at %p\n", name, (void*)addr);
2823 while (*p) p++;
2824 p++;
2825 } else if (subtype == TYPE_SAMPLE_UBIN) {
2826 /* un unmanaged binary loaded in memory */
2827 uint64_t tdiff = decode_uleb128 (p + 1, &p);
2828 uintptr_t addr = decode_sleb128 (p, &p);
2829 uint64_t offset G_GNUC_UNUSED = decode_uleb128 (p, &p);
2830 uintptr_t size = decode_uleb128 (p, &p);
2831 char *name;
2832 LOG_TIME (time_base, tdiff);
2833 time_base += tdiff;
2834 name = pstrdup ((char*)p);
2835 add_unmanaged_binary (addr, name, size);
2836 if (debug)
2837 fprintf (outfile, "unmanaged binary %s at %p\n", name, (void*)addr);
2838 while (*p) p++;
2839 p++;
2840 } else if (subtype == TYPE_SAMPLE_COUNTERS_DESC) {
2841 uint64_t i, len = decode_uleb128 (p + 1, &p);
2842 for (i = 0; i < len; i++) {
2843 uint64_t type, unit, variance, index;
2844 uint64_t section = decode_uleb128 (p, &p);
2845 char *section_str, *name;
2846 if (section != MONO_COUNTER_PERFCOUNTERS) {
2847 section_str = (char*) section_name (section);
2848 } else {
2849 section_str = pstrdup ((char*)p);
2850 while (*p++);
2852 name = pstrdup ((char*)p);
2853 while (*p++);
2854 type = decode_uleb128 (p, &p);
2855 unit = decode_uleb128 (p, &p);
2856 variance = decode_uleb128 (p, &p);
2857 index = decode_uleb128 (p, &p);
2858 add_counter (section_str, name, (int)type, (int)unit, (int)variance, (int)index);
2860 } else if (subtype == TYPE_SAMPLE_COUNTERS) {
2861 int i;
2862 CounterValue *value, *previous = NULL;
2863 CounterList *list;
2864 uint64_t timestamp = decode_uleb128 (p + 1, &p);
2865 uint64_t time_between = timestamp / 1000 * 1000 * 1000 * 1000 + startup_time;
2866 while (1) {
2867 uint64_t type, index = decode_uleb128 (p, &p);
2868 if (index == 0)
2869 break;
2871 for (list = counters; list; list = list->next) {
2872 if (list->counter->index == (int)index) {
2873 previous = list->counter->values_last;
2874 break;
2878 type = decode_uleb128 (p, &p);
2880 value = (CounterValue *)calloc (1, sizeof (CounterValue));
2881 value->timestamp = timestamp;
2883 switch (type) {
2884 case MONO_COUNTER_INT:
2885 #if SIZEOF_VOID_P == 4
2886 case MONO_COUNTER_WORD:
2887 #endif
2888 value->buffer = (unsigned char *)malloc (sizeof (int32_t));
2889 *(int32_t*)value->buffer = (int32_t)decode_sleb128 (p, &p) + (previous ? (*(int32_t*)previous->buffer) : 0);
2890 break;
2891 case MONO_COUNTER_UINT:
2892 value->buffer = (unsigned char *)malloc (sizeof (uint32_t));
2893 *(uint32_t*)value->buffer = (uint32_t)decode_uleb128 (p, &p) + (previous ? (*(uint32_t*)previous->buffer) : 0);
2894 break;
2895 case MONO_COUNTER_LONG:
2896 #if SIZEOF_VOID_P == 8
2897 case MONO_COUNTER_WORD:
2898 #endif
2899 case MONO_COUNTER_TIME_INTERVAL:
2900 value->buffer = (unsigned char *)malloc (sizeof (int64_t));
2901 *(int64_t*)value->buffer = (int64_t)decode_sleb128 (p, &p) + (previous ? (*(int64_t*)previous->buffer) : 0);
2902 break;
2903 case MONO_COUNTER_ULONG:
2904 value->buffer = (unsigned char *)malloc (sizeof (uint64_t));
2905 *(uint64_t*)value->buffer = (uint64_t)decode_uleb128 (p, &p) + (previous ? (*(uint64_t*)previous->buffer) : 0);
2906 break;
2907 case MONO_COUNTER_DOUBLE:
2908 value->buffer = (unsigned char *)malloc (sizeof (double));
2909 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
2910 for (i = 0; i < sizeof (double); i++)
2911 #else
2912 for (i = sizeof (double) - 1; i >= 0; i--)
2913 #endif
2914 value->buffer[i] = *p++;
2915 break;
2916 case MONO_COUNTER_STRING:
2917 if (*p++ == 0) {
2918 value->buffer = NULL;
2919 } else {
2920 value->buffer = (unsigned char*) pstrdup ((char*)p);
2921 while (*p++);
2923 break;
2925 if (time_between >= time_from && time_between <= time_to)
2926 add_counter_value (index, value);
2928 } else {
2929 return 0;
2931 break;
2933 case TYPE_COVERAGE:{
2934 int subtype = *p & 0xf0;
2935 switch (subtype) {
2936 case TYPE_COVERAGE_METHOD: {
2937 CoverageMethod *method = g_new0 (CoverageMethod, 1);
2938 const char *assembly, *klass, *name, *sig, *filename;
2939 int token, n_offsets, method_id;
2941 p++;
2942 assembly = (const char *)p; while (*p) p++; p++;
2943 klass = (const char *)p; while (*p) p++; p++;
2944 name = (const char *)p; while (*p) p++; p++;
2945 sig = (const char *)p; while (*p) p++; p++;
2946 filename = (const char *)p; while (*p) p++; p++;
2948 token = decode_uleb128 (p, &p);
2949 method_id = decode_uleb128 (p, &p);
2950 n_offsets = decode_uleb128 (p, &p);
2952 method->assembly_name = g_strdup (assembly);
2953 method->class_name = g_strdup (klass);
2954 method->method_name = g_strdup (name);
2955 method->method_signature = g_strdup (sig);
2956 method->filename = g_strdup (filename);
2957 method->token = token;
2958 method->n_statements = n_offsets;
2959 method->coverage = g_ptr_array_new ();
2960 method->method_id = method_id;
2962 coverage_add_method (method);
2964 break;
2966 case TYPE_COVERAGE_STATEMENT: {
2967 CoverageCoverage *coverage = g_new0 (CoverageCoverage, 1);
2968 int offset, count, line, column, method_id;
2970 p++;
2971 method_id = decode_uleb128 (p, &p);
2972 offset = decode_uleb128 (p, &p);
2973 count = decode_uleb128 (p, &p);
2974 line = decode_uleb128 (p, &p);
2975 column = decode_uleb128 (p, &p);
2977 coverage->method_id = method_id;
2978 coverage->offset = offset;
2979 coverage->count = count;
2980 coverage->line = line;
2981 coverage->column = column;
2983 coverage_add_coverage (coverage);
2984 break;
2986 case TYPE_COVERAGE_ASSEMBLY: {
2987 CoverageAssembly *assembly = g_new0 (CoverageAssembly, 1);
2988 char *name, *guid, *filename;
2989 int number_of_methods, fully_covered, partially_covered;
2990 p++;
2992 name = (char *)p; while (*p) p++; p++;
2993 guid = (char *)p; while (*p) p++; p++;
2994 filename = (char *)p; while (*p) p++; p++;
2995 number_of_methods = decode_uleb128 (p, &p);
2996 fully_covered = decode_uleb128 (p, &p);
2997 partially_covered = decode_uleb128 (p, &p);
2999 assembly->name = g_strdup (name);
3000 assembly->guid = g_strdup (guid);
3001 assembly->filename = g_strdup (filename);
3002 assembly->number_of_methods = number_of_methods;
3003 assembly->fully_covered = fully_covered;
3004 assembly->partially_covered = partially_covered;
3006 coverage_add_assembly (assembly);
3007 break;
3009 case TYPE_COVERAGE_CLASS: {
3010 CoverageClass *klass = g_new0 (CoverageClass, 1);
3011 char *assembly_name, *class_name;
3012 int number_of_methods, fully_covered, partially_covered;
3013 p++;
3015 assembly_name = (char *)p; while (*p) p++; p++;
3016 class_name = (char *)p; while (*p) p++; p++;
3017 number_of_methods = decode_uleb128 (p, &p);
3018 fully_covered = decode_uleb128 (p, &p);
3019 partially_covered = decode_uleb128 (p, &p);
3021 klass->assembly_name = g_strdup (assembly_name);
3022 klass->class_name = g_strdup (class_name);
3023 klass->number_of_methods = number_of_methods;
3024 klass->fully_covered = fully_covered;
3025 klass->partially_covered = partially_covered;
3027 coverage_add_class (klass);
3028 break;
3031 default:
3032 break;
3034 break;
3036 default:
3037 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);
3038 exit (1);
3040 record_event_stats (event, p - start);
3042 thread->last_time = time_base;
3043 for (i = 0; i < thread->stack_id; ++i)
3044 thread->stack [i]->recurse_count = 0;
3045 return 1;
3048 static ProfContext*
3049 load_file (char *name)
3051 unsigned char *p;
3052 ProfContext *ctx = (ProfContext *)calloc (sizeof (ProfContext), 1);
3053 if (strcmp (name, "-") == 0)
3054 ctx->file = stdin;
3055 else
3056 ctx->file = fopen (name, "rb");
3057 if (!ctx->file) {
3058 printf ("Cannot open file: %s\n", name);
3059 exit (1);
3061 #if defined (HAVE_SYS_ZLIB)
3062 if (ctx->file != stdin)
3063 ctx->gzfile = gzdopen (fileno (ctx->file), "rb");
3064 #endif
3065 if (!load_data (ctx, 32))
3066 return NULL;
3067 p = ctx->buf;
3068 if (read_int32 (p) != LOG_HEADER_ID || p [6] > LOG_DATA_VERSION)
3069 return NULL;
3070 ctx->version_major = p [4];
3071 ctx->version_minor = p [5];
3072 ctx->data_version = p [6];
3073 /* reading 64 bit files on 32 bit systems not supported yet */
3074 if (p [7] > sizeof (void*))
3075 return NULL;
3076 if (read_int32 (p + 20)) /* flags must be 0 */
3077 return NULL;
3078 ctx->startup_time = read_int64 (p + 8);
3079 ctx->timer_overhead = read_int32 (p + 16);
3080 ctx->pid = read_int32 (p + 24);
3081 ctx->port = read_int16 (p + 28);
3082 return ctx;
3085 enum {
3086 ALLOC_SORT_BYTES,
3087 ALLOC_SORT_COUNT
3089 static int alloc_sort_mode = ALLOC_SORT_BYTES;
3091 static int
3092 compare_class (const void *a, const void *b)
3094 ClassDesc *const *A = (ClassDesc *const *)a;
3095 ClassDesc *const *B = (ClassDesc *const *)b;
3096 uint64_t vala, valb;
3097 if (alloc_sort_mode == ALLOC_SORT_BYTES) {
3098 vala = (*A)->alloc_size;
3099 valb = (*B)->alloc_size;
3100 } else {
3101 vala = (*A)->allocs;
3102 valb = (*B)->allocs;
3104 if (valb == vala)
3105 return 0;
3106 if (valb < vala)
3107 return -1;
3108 return 1;
3111 static void
3112 dump_header (ProfContext *ctx)
3114 time_t st = ctx->startup_time / 1000;
3115 char *t = ctime (&st);
3116 fprintf (outfile, "\nMono log profiler data\n");
3117 fprintf (outfile, "\tProfiler version: %d.%d\n", ctx->version_major, ctx->version_minor);
3118 fprintf (outfile, "\tData version: %d\n", ctx->data_version);
3119 fprintf (outfile, "\tMean timer overhead: %d nanoseconds\n", ctx->timer_overhead);
3120 fprintf (outfile, "\tProgram startup: %s", t);
3121 if (ctx->pid)
3122 fprintf (outfile, "\tProgram ID: %d\n", ctx->pid);
3123 if (ctx->port)
3124 fprintf (outfile, "\tServer listening on: %d\n", ctx->port);
3127 static void
3128 dump_traces (TraceDesc *traces, const char *desc)
3130 int j;
3131 if (!show_traces)
3132 return;
3133 if (!traces->count)
3134 return;
3135 sort_context_array (traces);
3136 for (j = 0; j < traces->count; ++j) {
3137 int k;
3138 BackTrace *bt;
3139 bt = traces->traces [j].bt;
3140 if (!bt->count)
3141 continue;
3142 fprintf (outfile, "\t%llu %s from:\n", (unsigned long long) traces->traces [j].count, desc);
3143 for (k = 0; k < bt->count; ++k)
3144 fprintf (outfile, "\t\t%s\n", bt->methods [k]->name);
3148 static void
3149 dump_threads (ProfContext *ctx)
3151 ThreadContext *thread;
3152 fprintf (outfile, "\nThread summary\n");
3153 for (thread = ctx->threads; thread; thread = thread->next) {
3154 fprintf (outfile, "\tThread: %p, name: \"%s\"\n", (void*)thread->thread_id, thread->name? thread->name: "");
3158 static void
3159 dump_domains (ProfContext *ctx)
3161 fprintf (outfile, "\nDomain summary\n");
3163 for (DomainContext *domain = ctx->domains; domain; domain = domain->next)
3164 fprintf (outfile, "\tDomain: %p, friendly name: \"%s\"\n", (void *) domain->domain_id, domain->friendly_name);
3167 static void
3168 dump_remctxs (ProfContext *ctx)
3170 fprintf (outfile, "\nContext summary\n");
3172 for (RemCtxContext *remctx = ctx->remctxs; remctx; remctx = remctx->next)
3173 fprintf (outfile, "\tContext: %p, domain: %p\n", (void *) remctx->remctx_id, (void *) remctx->domain_id);
3176 static void
3177 dump_exceptions (void)
3179 int i;
3180 fprintf (outfile, "\nException summary\n");
3181 fprintf (outfile, "\tThrows: %llu\n", (unsigned long long) throw_count);
3182 dump_traces (&exc_traces, "throws");
3183 for (i = 0; i <= MONO_EXCEPTION_CLAUSE_FAULT; ++i) {
3184 if (!clause_summary [i])
3185 continue;
3186 fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), (unsigned long long) clause_summary [i]);
3190 static int
3191 compare_monitor (const void *a, const void *b)
3193 MonitorDesc *const *A = (MonitorDesc *const *)a;
3194 MonitorDesc *const *B = (MonitorDesc *const *)b;
3195 if ((*B)->wait_time == (*A)->wait_time)
3196 return 0;
3197 if ((*B)->wait_time < (*A)->wait_time)
3198 return -1;
3199 return 1;
3202 static void
3203 dump_monitors (void)
3205 MonitorDesc **monitors;
3206 int i, j;
3207 if (!num_monitors)
3208 return;
3209 monitors = (MonitorDesc **)malloc (sizeof (void*) * num_monitors);
3210 for (i = 0, j = 0; i < SMALL_HASH_SIZE; ++i) {
3211 MonitorDesc *mdesc = monitor_hash [i];
3212 while (mdesc) {
3213 monitors [j++] = mdesc;
3214 mdesc = mdesc->next;
3217 qsort (monitors, num_monitors, sizeof (void*), compare_monitor);
3218 fprintf (outfile, "\nMonitor lock summary\n");
3219 for (i = 0; i < num_monitors; ++i) {
3220 MonitorDesc *mdesc = monitors [i];
3221 fprintf (outfile, "\tLock object %p: %d contentions\n", (void*)mdesc->objid, (int)mdesc->contentions);
3222 fprintf (outfile, "\t\t%.6f secs total wait time, %.6f max, %.6f average\n",
3223 mdesc->wait_time/1000000000.0, mdesc->max_wait_time/1000000000.0, mdesc->wait_time/1000000000.0/mdesc->contentions);
3224 dump_traces (&mdesc->traces, "contentions");
3226 fprintf (outfile, "\tLock contentions: %llu\n", (unsigned long long) monitor_contention);
3227 fprintf (outfile, "\tLock acquired: %llu\n", (unsigned long long) monitor_acquired);
3228 fprintf (outfile, "\tLock failures: %llu\n", (unsigned long long) monitor_failed);
3231 static void
3232 dump_gcs (void)
3234 int i;
3235 fprintf (outfile, "\nGC summary\n");
3236 fprintf (outfile, "\tGC resizes: %d\n", gc_resizes);
3237 fprintf (outfile, "\tMax heap size: %llu\n", (unsigned long long) max_heap_size);
3238 fprintf (outfile, "\tObject moves: %llu\n", (unsigned long long) gc_object_moves);
3239 for (i = 0; i < 3; ++i) {
3240 if (!gc_info [i].count)
3241 continue;
3242 fprintf (outfile, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
3243 i, gc_info [i].count,
3244 (unsigned long long) (gc_info [i].max_time / 1000),
3245 (unsigned long long) (gc_info [i].total_time / 1000),
3246 (unsigned long long) (gc_info [i].total_time / gc_info [i].count / 1000));
3248 for (i = 0; i < 3; ++i) {
3249 if (!handle_info [i].max_live)
3250 continue;
3251 fprintf (outfile, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
3252 get_handle_name (i),
3253 (unsigned long long) (handle_info [i].created),
3254 (unsigned long long) (handle_info [i].destroyed),
3255 (unsigned long long) (handle_info [i].max_live));
3256 dump_traces (&handle_info [i].traces, "created");
3257 dump_traces (&handle_info [i].destroy_traces, "destroyed");
3261 static void
3262 dump_jit (void)
3264 int i;
3265 int code_size = 0;
3266 int compiled_methods = 0;
3267 MethodDesc* m;
3268 fprintf (outfile, "\nJIT summary\n");
3269 for (i = 0; i < HASH_SIZE; ++i) {
3270 m = method_hash [i];
3271 for (m = method_hash [i]; m; m = m->next) {
3272 if (!m->code || m->ignore_jit)
3273 continue;
3274 compiled_methods++;
3275 code_size += m->len;
3278 fprintf (outfile, "\tCompiled methods: %d\n", compiled_methods);
3279 fprintf (outfile, "\tGenerated code size: %d\n", code_size);
3280 fprintf (outfile, "\tJIT helpers: %d\n", num_jit_helpers);
3281 fprintf (outfile, "\tJIT helpers code size: %d\n", jit_helpers_code_size);
3284 static void
3285 dump_allocations (void)
3287 int i, c;
3288 intptr_t allocs = 0;
3289 uint64_t size = 0;
3290 int header_done = 0;
3291 ClassDesc **classes = (ClassDesc **)malloc (num_classes * sizeof (void*));
3292 ClassDesc *cd;
3293 c = 0;
3294 for (i = 0; i < HASH_SIZE; ++i) {
3295 cd = class_hash [i];
3296 while (cd) {
3297 classes [c++] = cd;
3298 cd = cd->next;
3301 qsort (classes, num_classes, sizeof (void*), compare_class);
3302 for (i = 0; i < num_classes; ++i) {
3303 cd = classes [i];
3304 if (!cd->allocs)
3305 continue;
3306 allocs += cd->allocs;
3307 size += cd->alloc_size;
3308 if (!header_done++) {
3309 fprintf (outfile, "\nAllocation summary\n");
3310 fprintf (outfile, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
3312 fprintf (outfile, "%10llu %10zd %8llu %s\n",
3313 (unsigned long long) (cd->alloc_size),
3314 cd->allocs,
3315 (unsigned long long) (cd->alloc_size / cd->allocs),
3316 cd->name);
3317 dump_traces (&cd->traces, "bytes");
3319 if (allocs)
3320 fprintf (outfile, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size, allocs);
3323 enum {
3324 METHOD_SORT_TOTAL,
3325 METHOD_SORT_SELF,
3326 METHOD_SORT_CALLS
3329 static int method_sort_mode = METHOD_SORT_TOTAL;
3331 static int
3332 compare_method (const void *a, const void *b)
3334 MethodDesc *const *A = (MethodDesc *const *)a;
3335 MethodDesc *const *B = (MethodDesc *const *)b;
3336 uint64_t vala, valb;
3337 if (method_sort_mode == METHOD_SORT_SELF) {
3338 vala = (*A)->self_time;
3339 valb = (*B)->self_time;
3340 } else if (method_sort_mode == METHOD_SORT_CALLS) {
3341 vala = (*A)->calls;
3342 valb = (*B)->calls;
3343 } else {
3344 vala = (*A)->total_time;
3345 valb = (*B)->total_time;
3347 if (vala == valb)
3348 return 0;
3349 if (valb < vala)
3350 return -1;
3351 return 1;
3354 static void
3355 dump_metadata (void)
3357 fprintf (outfile, "\nMetadata summary\n");
3358 fprintf (outfile, "\tLoaded images: %d\n", num_images);
3359 if (verbose) {
3360 ImageDesc *image;
3361 int i;
3362 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
3363 image = image_hash [i];
3364 while (image) {
3365 fprintf (outfile, "\t\t%s\n", image->filename);
3366 image = image->next;
3370 fprintf (outfile, "\tLoaded assemblies: %d\n", num_assemblies);
3371 if (verbose) {
3372 AssemblyDesc *assembly;
3373 int i;
3374 for (i = 0; i < SMALL_HASH_SIZE; ++i) {
3375 assembly = assembly_hash [i];
3376 while (assembly) {
3377 fprintf (outfile, "\t\t%s\n", assembly->asmname);
3378 assembly = assembly->next;
3384 static void
3385 dump_methods (void)
3387 int i, c;
3388 uint64_t calls = 0;
3389 int header_done = 0;
3390 MethodDesc **methods = (MethodDesc **)malloc (num_methods * sizeof (void*));
3391 MethodDesc *cd;
3392 c = 0;
3393 for (i = 0; i < HASH_SIZE; ++i) {
3394 cd = method_hash [i];
3395 while (cd) {
3396 cd->total_time = cd->self_time + cd->callee_time;
3397 methods [c++] = cd;
3398 cd = cd->next;
3401 qsort (methods, num_methods, sizeof (void*), compare_method);
3402 for (i = 0; i < num_methods; ++i) {
3403 uint64_t msecs;
3404 uint64_t smsecs;
3405 cd = methods [i];
3406 if (!cd->calls)
3407 continue;
3408 calls += cd->calls;
3409 msecs = cd->total_time / 1000000;
3410 smsecs = (cd->total_time - cd->callee_time) / 1000000;
3411 if (!msecs && !verbose)
3412 continue;
3413 if (!header_done++) {
3414 fprintf (outfile, "\nMethod call summary\n");
3415 fprintf (outfile, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
3417 fprintf (outfile, "%8llu %8llu %10llu %s\n",
3418 (unsigned long long) (msecs),
3419 (unsigned long long) (smsecs),
3420 (unsigned long long) (cd->calls),
3421 cd->name);
3422 dump_traces (&cd->traces, "calls");
3424 if (calls)
3425 fprintf (outfile, "Total calls: %llu\n", (unsigned long long) calls);
3428 static int
3429 compare_heap_class (const void *a, const void *b)
3431 HeapClassDesc *const *A = (HeapClassDesc *const *)a;
3432 HeapClassDesc *const *B = (HeapClassDesc *const *)b;
3433 uint64_t vala, valb;
3434 if (alloc_sort_mode == ALLOC_SORT_BYTES) {
3435 vala = (*A)->total_size;
3436 valb = (*B)->total_size;
3437 } else {
3438 vala = (*A)->count;
3439 valb = (*B)->count;
3441 if (valb == vala)
3442 return 0;
3443 if (valb < vala)
3444 return -1;
3445 return 1;
3448 static int
3449 compare_rev_class (const void *a, const void *b)
3451 const HeapClassRevRef *A = (const HeapClassRevRef *)a;
3452 const HeapClassRevRef *B = (const HeapClassRevRef *)b;
3453 if (B->count == A->count)
3454 return 0;
3455 if (B->count < A->count)
3456 return -1;
3457 return 1;
3460 static void
3461 dump_rev_claases (HeapClassRevRef *revs, int count)
3463 int j;
3464 if (!show_traces)
3465 return;
3466 if (!count)
3467 return;
3468 for (j = 0; j < count; ++j) {
3469 HeapClassDesc *cd = revs [j].klass;
3470 fprintf (outfile, "\t\t%llu references from: %s\n",
3471 (unsigned long long) (revs [j].count),
3472 cd->klass->name);
3476 static void
3477 heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs)
3479 uint64_t size = 0;
3480 uint64_t count = 0;
3481 int ccount = 0;
3482 int i;
3483 HeapClassDesc *cd;
3484 HeapClassDesc **sorted;
3485 sorted = (HeapClassDesc **)malloc (sizeof (void*) * hs->class_count);
3486 for (i = 0; i < hs->hash_size; ++i) {
3487 cd = hs->class_hash [i];
3488 if (!cd)
3489 continue;
3490 count += cd->count;
3491 size += cd->total_size;
3492 sorted [ccount++] = cd;
3494 hs->sorted = sorted;
3495 qsort (sorted, ccount, sizeof (void*), compare_heap_class);
3496 fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %zd\n",
3497 hs_num,
3498 (hs->timestamp - startup_time)/1000000000.0,
3499 (unsigned long long) (size),
3500 (unsigned long long) (count),
3501 ccount, hs->num_roots);
3502 if (!verbose && ccount > 30)
3503 ccount = 30;
3504 fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
3505 for (i = 0; i < ccount; ++i) {
3506 HeapClassRevRef *rev_sorted;
3507 int j, k;
3508 HeapClassDesc *ocd = NULL;
3509 cd = sorted [i];
3510 if (last_hs)
3511 ocd = heap_class_lookup (last_hs, cd->klass);
3512 fprintf (outfile, "\t%10llu %10llu %8llu %s",
3513 (unsigned long long) (cd->total_size),
3514 (unsigned long long) (cd->count),
3515 (unsigned long long) (cd->total_size / cd->count),
3516 cd->klass->name);
3517 if (ocd) {
3518 int64_t bdiff = cd->total_size - ocd->total_size;
3519 int64_t cdiff = cd->count - ocd->count;
3520 fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", (long long) bdiff, (long long) cdiff);
3521 } else {
3522 fprintf (outfile, "\n");
3524 if (!collect_traces)
3525 continue;
3526 rev_sorted = (HeapClassRevRef *)malloc (cd->rev_count * sizeof (HeapClassRevRef));
3527 k = 0;
3528 for (j = 0; j < cd->rev_hash_size; ++j) {
3529 if (cd->rev_hash [j].klass)
3530 rev_sorted [k++] = cd->rev_hash [j];
3532 assert (cd->rev_count == k);
3533 qsort (rev_sorted, cd->rev_count, sizeof (HeapClassRevRef), compare_rev_class);
3534 if (cd->root_references)
3535 fprintf (outfile, "\t\t%zd root references (%zd pinning)\n", cd->root_references, cd->pinned_references);
3536 dump_rev_claases (rev_sorted, cd->rev_count);
3537 free (rev_sorted);
3539 free (sorted);
3542 static int
3543 compare_heap_shots (const void *a, const void *b)
3545 HeapShot *const *A = (HeapShot *const *)a;
3546 HeapShot *const *B = (HeapShot *const *)b;
3547 if ((*B)->timestamp == (*A)->timestamp)
3548 return 0;
3549 if ((*B)->timestamp > (*A)->timestamp)
3550 return -1;
3551 return 1;
3554 static void
3555 dump_heap_shots (void)
3557 HeapShot **hs_sorted;
3558 HeapShot *hs;
3559 HeapShot *last_hs = NULL;
3560 int i;
3561 if (!heap_shots)
3562 return;
3563 hs_sorted = (HeapShot **)malloc (num_heap_shots * sizeof (void*));
3564 fprintf (outfile, "\nHeap shot summary\n");
3565 i = 0;
3566 for (hs = heap_shots; hs; hs = hs->next)
3567 hs_sorted [i++] = hs;
3568 qsort (hs_sorted, num_heap_shots, sizeof (void*), compare_heap_shots);
3569 for (i = 0; i < num_heap_shots; ++i) {
3570 hs = hs_sorted [i];
3571 heap_shot_summary (hs, i, last_hs);
3572 last_hs = hs;
3576 /* This is a very basic escape function that escapes < > and &
3577 Ideally we'd use g_markup_escape_string but that function isn't
3578 available in Mono's eglib. This was written without looking at the
3579 source of that function in glib. */
3580 static char *
3581 escape_string_for_xml (const char *string)
3583 GString *string_builder = g_string_new (NULL);
3584 const char *start, *p;
3586 start = p = string;
3587 while (*p) {
3588 while (*p && *p != '&' && *p != '<' && *p != '>')
3589 p++;
3591 g_string_append_len (string_builder, start, p - start);
3593 if (*p == '\0')
3594 break;
3596 switch (*p) {
3597 case '<':
3598 g_string_append (string_builder, "&lt;");
3599 break;
3601 case '>':
3602 g_string_append (string_builder, "&gt;");
3603 break;
3605 case '&':
3606 g_string_append (string_builder, "&amp;");
3607 break;
3609 default:
3610 break;
3613 p++;
3614 start = p;
3617 return g_string_free (string_builder, FALSE);
3620 static int
3621 sort_assemblies (gconstpointer a, gconstpointer b)
3623 CoverageAssembly *assembly_a = *(CoverageAssembly **)a;
3624 CoverageAssembly *assembly_b = *(CoverageAssembly **)b;
3626 if (assembly_a->name == NULL && assembly_b->name == NULL)
3627 return 0;
3628 else if (assembly_a->name == NULL)
3629 return -1;
3630 else if (assembly_b->name == NULL)
3631 return 1;
3633 return strcmp (assembly_a->name, assembly_b->name);
3636 static void
3637 dump_coverage (void)
3639 if (!coverage_methods && !coverage_assemblies)
3640 return;
3642 gather_coverage_statements ();
3643 fprintf (outfile, "\nCoverage Summary:\n");
3645 if (coverage_outfile) {
3646 fprintf (coverage_outfile, "<?xml version=\"1.0\"?>\n");
3647 fprintf (coverage_outfile, "<coverage version=\"0.3\">\n");
3650 g_ptr_array_sort (coverage_assemblies, sort_assemblies);
3652 for (guint i = 0; i < coverage_assemblies->len; i++) {
3653 CoverageAssembly *assembly = (CoverageAssembly *)coverage_assemblies->pdata[i];
3654 GPtrArray *classes;
3656 if (assembly->number_of_methods != 0) {
3657 int percentage = ((assembly->fully_covered + assembly->partially_covered) * 100) / assembly->number_of_methods;
3658 fprintf (outfile, "\t%s (%s) %d%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, percentage, assembly->number_of_methods, assembly->fully_covered);
3659 } else
3660 fprintf (outfile, "\t%s (%s) ?%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, assembly->number_of_methods, assembly->fully_covered);
3662 if (coverage_outfile) {
3663 char *escaped_name, *escaped_filename;
3664 escaped_name = escape_string_for_xml (assembly->name);
3665 escaped_filename = escape_string_for_xml (assembly->filename);
3667 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);
3669 g_free (escaped_name);
3670 g_free (escaped_filename);
3673 classes = (GPtrArray *)g_hash_table_lookup (coverage_assembly_classes, assembly->name);
3674 if (classes) {
3675 for (guint j = 0; j < classes->len; j++) {
3676 CoverageClass *klass = (CoverageClass *)classes->pdata [j];
3678 if (klass->number_of_methods > 0) {
3679 int percentage = ((klass->fully_covered + klass->partially_covered) * 100) / klass->number_of_methods;
3680 fprintf (outfile, "\t\t%s %d%% covered (%d methods - %d covered)\n", klass->class_name, percentage, klass->number_of_methods, klass->fully_covered);
3681 } else
3682 fprintf (outfile, "\t\t%s ?%% covered (%d methods - %d covered)\n", klass->class_name, klass->number_of_methods, klass->fully_covered);
3684 if (coverage_outfile) {
3685 char *escaped_name;
3686 escaped_name = escape_string_for_xml (klass->class_name);
3688 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);
3689 g_free (escaped_name);
3695 for (guint i = 0; i < coverage_methods->len; i++) {
3696 CoverageMethod *method = (CoverageMethod *)coverage_methods->pdata [i];
3698 if (coverage_outfile) {
3699 char *escaped_assembly, *escaped_class, *escaped_method, *escaped_sig, *escaped_filename;
3701 escaped_assembly = escape_string_for_xml (method->assembly_name);
3702 escaped_class = escape_string_for_xml (method->class_name);
3703 escaped_method = escape_string_for_xml (method->method_name);
3704 escaped_sig = escape_string_for_xml (method->method_signature);
3705 escaped_filename = escape_string_for_xml (method->filename);
3707 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);
3709 g_free (escaped_assembly);
3710 g_free (escaped_class);
3711 g_free (escaped_method);
3712 g_free (escaped_sig);
3713 g_free (escaped_filename);
3715 for (guint j = 0; j < method->coverage->len; j++) {
3716 CoverageCoverage *coverage = (CoverageCoverage *)method->coverage->pdata [j];
3717 fprintf (coverage_outfile, "\t\t<statement offset=\"%d\" counter=\"%d\" line=\"%d\" column=\"%d\"/>\n", coverage->offset, coverage->count, coverage->line, coverage->column);
3719 fprintf (coverage_outfile, "\t</method>\n");
3723 if (coverage_outfile) {
3724 fprintf (coverage_outfile, "</coverage>\n");
3725 fclose (coverage_outfile);
3726 coverage_outfile = NULL;
3730 #define DUMP_EVENT_STAT(EVENT,SUBTYPE) dump_event (#EVENT, #SUBTYPE, EVENT, SUBTYPE);
3732 static void
3733 dump_event (const char *event_name, const char *subtype_name, int event, int subtype)
3735 int idx = event | subtype;
3736 EventStat evt = stats [idx];
3737 if (!evt.count)
3738 return;
3740 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);
3743 static void
3744 dump_stats (void)
3746 fprintf (outfile, "\nMlpd statistics\n");
3747 fprintf (outfile, "\tBuffer count %d\toverhead %d (%d bytes per header)\n", buffer_count, buffer_count * BUFFER_HEADER_SIZE, BUFFER_HEADER_SIZE);
3748 fprintf (outfile, "\nEvent details:\n");
3750 DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_NO_BT);
3751 DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_BT);
3753 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_EVENT);
3754 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_RESIZE);
3755 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_MOVE);
3756 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED);
3757 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED);
3758 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED_BT);
3759 DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED_BT);
3761 DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_LOAD);
3762 DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_UNLOAD);
3764 DUMP_EVENT_STAT (TYPE_METHOD, TYPE_LEAVE);
3765 DUMP_EVENT_STAT (TYPE_METHOD, TYPE_ENTER);
3766 DUMP_EVENT_STAT (TYPE_METHOD, TYPE_EXC_LEAVE);
3767 DUMP_EVENT_STAT (TYPE_METHOD, TYPE_JIT);
3769 DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_THROW);
3770 DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_CLAUSE);
3771 DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_EXCEPTION_BT);
3773 DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_NO_BT);
3774 DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_BT);
3776 DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_START);
3777 DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_END);
3778 DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_OBJECT);
3779 DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_ROOT);
3781 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_HIT);
3782 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_USYM);
3783 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_UBIN);
3784 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS_DESC);
3785 DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS);
3787 DUMP_EVENT_STAT (TYPE_RUNTIME, TYPE_JITHELPER);
3789 DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_ASSEMBLY);
3790 DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_METHOD);
3791 DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_STATEMENT);
3792 DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_CLASS);
3797 static void
3798 flush_context (ProfContext *ctx)
3800 ThreadContext *thread;
3801 /* FIXME: sometimes there are leftovers: indagate */
3802 for (thread = ctx->threads; thread; thread = thread->next) {
3803 while (thread->stack_id) {
3804 if (debug)
3805 fprintf (outfile, "thread %p has %d items on stack\n", (void*)thread->thread_id, thread->stack_id);
3806 pop_method (thread, thread->stack [thread->stack_id - 1], thread->last_time);
3811 static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,heapshot,counters,coverage";
3813 static const char*
3814 match_option (const char *p, const char *opt)
3816 int len = strlen (opt);
3817 if (strncmp (p, opt, len) == 0) {
3818 if (p [len] == ',')
3819 len++;
3820 return p + len;
3822 return p;
3825 static int
3826 print_reports (ProfContext *ctx, const char *reps, int parse_only)
3828 const char *opt;
3829 const char *p;
3830 for (p = reps; *p; p = opt) {
3831 if ((opt = match_option (p, "header")) != p) {
3832 if (!parse_only)
3833 dump_header (ctx);
3834 continue;
3836 if ((opt = match_option (p, "thread")) != p) {
3837 if (!parse_only)
3838 dump_threads (ctx);
3839 continue;
3841 if ((opt = match_option (p, "domain")) != p) {
3842 if (!parse_only)
3843 dump_domains (ctx);
3844 continue;
3846 if ((opt = match_option (p, "context")) != p) {
3847 if (!parse_only)
3848 dump_remctxs (ctx);
3849 continue;
3851 if ((opt = match_option (p, "gc")) != p) {
3852 if (!parse_only)
3853 dump_gcs ();
3854 continue;
3856 if ((opt = match_option (p, "jit")) != p) {
3857 if (!parse_only)
3858 dump_jit ();
3859 continue;
3861 if ((opt = match_option (p, "alloc")) != p) {
3862 if (!parse_only)
3863 dump_allocations ();
3864 continue;
3866 if ((opt = match_option (p, "call")) != p) {
3867 if (!parse_only)
3868 dump_methods ();
3869 continue;
3871 if ((opt = match_option (p, "metadata")) != p) {
3872 if (!parse_only)
3873 dump_metadata ();
3874 continue;
3876 if ((opt = match_option (p, "exception")) != p) {
3877 if (!parse_only)
3878 dump_exceptions ();
3879 continue;
3881 if ((opt = match_option (p, "monitor")) != p) {
3882 if (!parse_only)
3883 dump_monitors ();
3884 continue;
3886 if ((opt = match_option (p, "heapshot")) != p) {
3887 if (!parse_only)
3888 dump_heap_shots ();
3889 continue;
3891 if ((opt = match_option (p, "sample")) != p) {
3892 if (!parse_only)
3893 dump_samples ();
3894 continue;
3896 if ((opt = match_option (p, "counters")) != p) {
3897 if (!parse_only)
3898 dump_counters ();
3899 continue;
3901 if ((opt = match_option (p, "coverage")) != p) {
3902 if (!parse_only)
3903 dump_coverage ();
3904 continue;
3906 if ((opt = match_option (p, "stats")) != p) {
3907 if (!parse_only)
3908 dump_stats ();
3909 continue;
3911 return 0;
3913 return 1;
3916 static int
3917 add_find_spec (const char *p)
3919 if (p [0] == 'S' && p [1] == ':') {
3920 char *vale;
3921 find_size = strtoul (p + 2, &vale, 10);
3922 return 1;
3923 } else if (p [0] == 'T' && p [1] == ':') {
3924 find_name = p + 2;
3925 return 1;
3927 return 0;
3930 static void
3931 usage (void)
3933 printf ("Mono log profiler report version %d.%d\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR);
3934 printf ("Usage: mprof-report [OPTIONS] FILENAME\n");
3935 printf ("FILENAME can be '-' to read from standard input.\n");
3936 printf ("Options:\n");
3937 printf ("\t--help display this help\n");
3938 printf ("\t--out=FILE write to FILE instead of stdout\n");
3939 printf ("\t--traces collect and show backtraces\n");
3940 printf ("\t--maxframes=NUM limit backtraces to NUM entries\n");
3941 printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
3942 printf ("\t %s\n", reports);
3943 printf ("\t--method-sort=MODE sort methods according to MODE: total, self, calls\n");
3944 printf ("\t--alloc-sort=MODE sort allocations according to MODE: bytes, count\n");
3945 printf ("\t--counters-sort=MODE sort counters according to MODE: time, category\n");
3946 printf ("\t only accessible in verbose mode\n");
3947 printf ("\t--track=OB1[,OB2...] track what happens to objects OBJ1, O2 etc.\n");
3948 printf ("\t--find=FINDSPEC find and track objects matching FINFSPEC, where FINDSPEC is:\n");
3949 printf ("\t S:minimum_size or T:partial_name\n");
3950 printf ("\t--thread=THREADID consider just the data for thread THREADID\n");
3951 printf ("\t--time=FROM-TO consider data FROM seconds from startup up to TO seconds\n");
3952 printf ("\t--verbose increase verbosity level\n");
3953 printf ("\t--debug display decoding debug info for mprof-report devs\n");
3954 printf ("\t--coverage-out=FILE write the coverage info to FILE as XML\n");
3958 main (int argc, char *argv[])
3960 ProfContext *ctx;
3961 int i;
3962 outfile = stdout;
3963 for (i = 1; i < argc; ++i) {
3964 if (strcmp ("--debug", argv [i]) == 0) {
3965 debug++;
3966 } else if (strcmp ("--help", argv [i]) == 0) {
3967 usage ();
3968 return 0;
3969 } else if (strncmp ("--alloc-sort=", argv [i], 13) == 0) {
3970 const char *val = argv [i] + 13;
3971 if (strcmp (val, "bytes") == 0) {
3972 alloc_sort_mode = ALLOC_SORT_BYTES;
3973 } else if (strcmp (val, "count") == 0) {
3974 alloc_sort_mode = ALLOC_SORT_COUNT;
3975 } else {
3976 usage ();
3977 return 1;
3979 } else if (strncmp ("--method-sort=", argv [i], 14) == 0) {
3980 const char *val = argv [i] + 14;
3981 if (strcmp (val, "total") == 0) {
3982 method_sort_mode = METHOD_SORT_TOTAL;
3983 } else if (strcmp (val, "self") == 0) {
3984 method_sort_mode = METHOD_SORT_SELF;
3985 } else if (strcmp (val, "calls") == 0) {
3986 method_sort_mode = METHOD_SORT_CALLS;
3987 } else {
3988 usage ();
3989 return 1;
3991 } else if (strncmp ("--counters-sort=", argv [i], 16) == 0) {
3992 const char *val = argv [i] + 16;
3993 if (strcmp (val, "time") == 0) {
3994 counters_sort_mode = COUNTERS_SORT_TIME;
3995 } else if (strcmp (val, "category") == 0) {
3996 counters_sort_mode = COUNTERS_SORT_CATEGORY;
3997 } else {
3998 usage ();
3999 return 1;
4001 } else if (strncmp ("--reports=", argv [i], 10) == 0) {
4002 const char *val = argv [i] + 10;
4003 if (!print_reports (NULL, val, 1)) {
4004 usage ();
4005 return 1;
4007 reports = val;
4008 } else if (strncmp ("--out=", argv [i], 6) == 0) {
4009 const char *val = argv [i] + 6;
4010 outfile = fopen (val, "w");
4011 if (!outfile) {
4012 printf ("Cannot open output file: %s\n", val);
4013 return 1;
4015 } else if (strncmp ("--maxframes=", argv [i], 12) == 0) {
4016 const char *val = argv [i] + 12;
4017 char *vale;
4018 trace_max = strtoul (val, &vale, 10);
4019 } else if (strncmp ("--find=", argv [i], 7) == 0) {
4020 const char *val = argv [i] + 7;
4021 if (!add_find_spec (val)) {
4022 usage ();
4023 return 1;
4025 } else if (strncmp ("--track=", argv [i], 8) == 0) {
4026 const char *val = argv [i] + 8;
4027 char *vale;
4028 while (*val) {
4029 uintptr_t tracked_obj;
4030 if (*val == ',') {
4031 val++;
4032 continue;
4034 tracked_obj = strtoul (val, &vale, 0);
4035 found_object (tracked_obj);
4036 val = vale;
4038 } else if (strncmp ("--thread=", argv [i], 9) == 0) {
4039 const char *val = argv [i] + 9;
4040 char *vale;
4041 thread_filter = strtoul (val, &vale, 0);
4042 } else if (strncmp ("--time=", argv [i], 7) == 0) {
4043 char *val = pstrdup (argv [i] + 7);
4044 double from_secs, to_secs;
4045 char *top = strchr (val, '-');
4046 if (!top) {
4047 usage ();
4048 return 1;
4050 *top++ = 0;
4051 from_secs = atof (val);
4052 to_secs = atof (top);
4053 free (val);
4054 if (from_secs > to_secs) {
4055 usage ();
4056 return 1;
4058 time_from = from_secs * 1000000000;
4059 time_to = to_secs * 1000000000;
4060 use_time_filter = 1;
4061 } else if (strcmp ("--verbose", argv [i]) == 0) {
4062 verbose++;
4063 } else if (strcmp ("--traces", argv [i]) == 0) {
4064 show_traces = 1;
4065 collect_traces = 1;
4066 } else if (strncmp ("--coverage-out=", argv [i], 15) == 0) {
4067 const char *val = argv [i] + 15;
4068 coverage_outfile = fopen (val, "w");
4069 if (!coverage_outfile) {
4070 printf ("Cannot open output file: %s\n", val);
4071 return 1;
4073 } else {
4074 break;
4077 if (i >= argc) {
4078 usage ();
4079 return 2;
4081 ctx = load_file (argv [i]);
4082 if (!ctx) {
4083 printf ("Not a log profiler data file (or unsupported version).\n");
4084 return 1;
4086 while (decode_buffer (ctx));
4087 flush_context (ctx);
4088 if (num_tracked_objects)
4089 return 0;
4090 print_reports (ctx, reports, 0);
4091 return 0;