2 * decode.c: mprof-report program source: decode and analyze the log profiler data
5 * Paolo Molaro (lupus@ximian.com)
6 * Alex Rønne Petersen (alexrp@xamarin.com)
8 * Copyright 2010 Novell, Inc (http://www.novell.com)
12 * The Coverage XML output schema
22 * <coverage> - The root element of the documentation. It can contain any number of
23 * <assembly>, <class> or <method> elements.
25 * - version: The version number for the file format - (eg: "0.3")
26 * <assembly> - Contains data about assemblies. Has no child elements
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
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
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
47 * <statement> - Contains data about IL statements. Has no child elements
49 * - offset: The offset of the statement in the IL code after the previous
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
61 #if !defined(__APPLE__) && !defined(__FreeBSD__)
66 #if defined (HAVE_SYS_ZLIB)
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() {}
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
;
101 read_int16 (unsigned char *p
)
103 int32_t value
= *p
++;
104 value
|= (*p
++) << 8;
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;
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;
133 pstrdup (const char *s
)
135 int len
= strlen (s
) + 1;
136 char *p
= (char *)malloc (len
);
141 typedef struct _CounterValue CounterValue
;
142 struct _CounterValue
{
144 unsigned char *buffer
;
148 typedef struct _Counter Counter
;
156 CounterValue
*values
;
157 CounterValue
*values_last
;
160 typedef struct _CounterList CounterList
;
161 struct _CounterList
{
166 typedef struct _CounterSection CounterSection
;
167 struct _CounterSection
{
169 CounterList
*counters
;
170 CounterList
*counters_last
;
171 CounterSection
*next
;
174 typedef struct _CounterTimestamp CounterTimestamp
;
175 struct _CounterTimestamp
{
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
;
188 COUNTERS_SORT_CATEGORY
191 static int counters_sort_mode
= COUNTERS_SORT_TIME
;
194 add_counter_to_section (Counter
*counter
)
196 CounterSection
*csection
, *s
;
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
;
208 csection
->counters_last
->next
= clist
;
209 csection
->counters_last
= clist
;
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
;
223 s
= counters_sections
;
231 add_counter (const char *section
, const char *name
, int type
, int unit
, int variance
, int index
)
233 CounterList
*list
, *l
;
236 for (list
= counters
; list
; list
= list
->next
)
237 if (list
->counter
->index
== index
)
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
;
260 if (counters_sort_mode
== COUNTERS_SORT_CATEGORY
|| !verbose
)
261 add_counter_to_section (counter
);
265 add_counter_to_timestamp (uint64_t timestamp
, Counter
*counter
)
267 CounterTimestamp
*ctimestamp
, *t
;
268 CounterSection
*csection
;
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
;
282 csection
->counters_last
->next
= clist
;
283 csection
->counters_last
= clist
;
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
;
297 ctimestamp
->sections_last
->next
= csection
;
298 ctimestamp
->sections_last
= csection
;
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
;
317 t
= counters_timestamps
;
320 t
->next
= ctimestamp
;
325 add_counter_value (int index
, CounterValue
*value
)
329 for (list
= counters
; list
; list
= list
->next
) {
330 if (list
->counter
->index
== index
) {
331 if (!list
->counter
->values
)
332 list
->counter
->values
= value
;
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
);
346 section_name (int 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>";
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>";
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>";
390 variance_name (int 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>";
401 dump_counters_value (Counter
*counter
, const char *key_format
, const char *key
, void *value
)
406 snprintf (format
, sizeof (format
), "%s : %%s\n", key_format
);
407 fprintf (outfile
, format
, key
, "<null>");
409 switch (counter
->type
) {
410 case MONO_COUNTER_INT
:
411 #if SIZEOF_VOID_P == 4
412 case MONO_COUNTER_WORD
:
414 snprintf (format
, sizeof (format
), "%s : %%d\n", key_format
);
415 fprintf (outfile
, format
, key
, *(int32_t*)value
);
417 case MONO_COUNTER_UINT
:
418 snprintf (format
, sizeof (format
), "%s : %%u\n", key_format
);
419 fprintf (outfile
, format
, key
, *(uint32_t*)value
);
421 case MONO_COUNTER_LONG
:
422 #if SIZEOF_VOID_P == 8
423 case MONO_COUNTER_WORD
:
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);
433 snprintf (format
, sizeof (format
), "%s : %%u\n", key_format
);
434 fprintf (outfile
, format
, key
, *(int64_t*)value
);
437 case MONO_COUNTER_ULONG
:
438 snprintf (format
, sizeof (format
), "%s : %%llu\n", key_format
);
439 fprintf (outfile
, format
, key
, *(uint64_t*)value
);
441 case MONO_COUNTER_DOUBLE
:
442 snprintf (format
, sizeof (format
), "%s : %%f\n", key_format
);
443 fprintf (outfile
, format
, key
, *(double*)value
);
445 case MONO_COUNTER_STRING
:
446 snprintf (format
, sizeof (format
), "%s : %%s\n", key_format
);
447 fprintf (outfile
, format
, key
, *(char*)value
);
457 CounterValue
*cvalue
;
458 CounterTimestamp
*ctimestamp
;
459 CounterSection
*csection
;
461 char strtimestamp
[17];
462 int i
, section_printed
;
464 fprintf (outfile
, "\nCounters:\n");
467 char counters_to_print
[][64] = {
469 "Methods JITted using mono JIT",
470 "Methods JITted using LLVM",
471 "Total time spent JITting (sec)",
479 "CPU Load Average - 1min",
480 "CPU Load Average - 5min",
481 "CPU Load Average - 15min",
485 for (csection
= counters_sections
; csection
; csection
= csection
->next
) {
488 for (clist
= csection
->counters
; clist
; clist
= clist
->next
) {
489 counter
= clist
->counter
;
490 if (!counter
->values_last
)
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
);
500 dump_counters_value (counter
, "\t\t%-30s", counter
->name
, counter
->values_last
->buffer
);
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
)
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
;
561 static ImageDesc
* image_hash
[SMALL_HASH_SIZE
] = {0};
564 add_image (intptr_t image
, char *name
)
566 int slot
= ((image
>> 2) & 0xffff) % SMALL_HASH_SIZE
;
567 ImageDesc
*cd
= (ImageDesc
*)malloc (sizeof (ImageDesc
));
569 cd
->filename
= pstrdup (name
);
570 cd
->next
= image_hash
[slot
];
571 image_hash
[slot
] = cd
;
575 static int num_assemblies
;
577 typedef struct _AssemblyDesc AssemblyDesc
;
578 struct _AssemblyDesc
{
584 static AssemblyDesc
* assembly_hash
[SMALL_HASH_SIZE
] = {0};
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
;
598 typedef struct _BackTrace BackTrace
;
610 typedef struct _ClassDesc ClassDesc
;
620 static ClassDesc
* class_hash
[HASH_SIZE
] = {0};
621 static int num_classes
= 0;
624 add_class (intptr_t klass
, const char *name
)
626 int slot
= ((klass
>> 2) & 0xffff) % HASH_SIZE
;
628 cd
= class_hash
[slot
];
629 while (cd
&& cd
->klass
!= klass
)
631 /* we resolved an unknown class (unless we had the code unloaded) */
633 /*printf ("resolved unknown: %s\n", name);*/
635 cd
->name
= pstrdup (name
);
638 cd
= (ClassDesc
*)calloc (sizeof (ClassDesc
), 1);
640 cd
->name
= pstrdup (name
);
641 cd
->next
= class_hash
[slot
];
644 cd
->traces
.count
= 0;
646 cd
->traces
.traces
= NULL
;
647 class_hash
[slot
] = cd
;
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
)
661 snprintf (buf
, sizeof (buf
), "unresolved class %p", (void*)klass
);
662 return add_class (klass
, buf
);
667 typedef struct _MethodDesc MethodDesc
;
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 */
679 uint64_t callee_time
;
684 static MethodDesc
* method_hash
[HASH_SIZE
] = {0};
685 static int num_methods
= 0;
688 add_method (intptr_t method
, const char *name
, intptr_t code
, int len
)
690 int slot
= ((method
>> 2) & 0xffff) % HASH_SIZE
;
692 cd
= method_hash
[slot
];
693 while (cd
&& cd
->method
!= method
)
695 /* we resolved an unknown method (unless we had the code unloaded) */
699 /*printf ("resolved unknown: %s\n", name);*/
701 cd
->name
= pstrdup (name
);
704 cd
= (MethodDesc
*)calloc (sizeof (MethodDesc
), 1);
706 cd
->name
= pstrdup (name
);
711 cd
->traces
.count
= 0;
713 cd
->traces
.traces
= NULL
;
714 cd
->next
= method_hash
[slot
];
715 method_hash
[slot
] = cd
;
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
)
729 snprintf (buf
, sizeof (buf
), "unknown method %p", (void*)method
);
730 return add_method (method
, buf
, 0, 0);
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
;
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
;
754 lookup_method_by_ip (uintptr_t ip
)
759 for (i
= 0; i
< HASH_SIZE
; ++i
) {
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
) {
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
)
779 if ((*B
)->sample_hits
< (*A
)->sample_hits
)
784 typedef struct _UnmanagedSymbol UnmanagedSymbol
;
785 struct _UnmanagedSymbol
{
786 UnmanagedSymbol
*parent
;
791 uintptr_t sample_hits
;
794 static UnmanagedSymbol
**usymbols
= NULL
;
795 static int usymbols_size
= 0;
796 static int usymbols_num
= 0;
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
)
805 if ((*B
)->addr
> (*A
)->addr
)
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
)
817 if ((*B
)->sample_hits
< (*A
)->sample_hits
)
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;
830 usymbols
= (UnmanagedSymbol
**)realloc (usymbols
, sizeof (void*) * new_size
);
831 usymbols_size
= new_size
;
833 sym
= (UnmanagedSymbol
*)calloc (sizeof (UnmanagedSymbol
), 1);
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;
846 UnmanagedSymbol
*sym
;
851 if (addr
== sym
->addr
)
853 if (addr
< sym
->addr
) {
855 } else if (addr
> sym
->addr
) {
860 if (last_best
>= 0 && (addr
- usymbols
[last_best
]->addr
) < 4096)
861 return usymbols
[last_best
];
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;
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;
878 ubinaries
= (UnmanagedSymbol
**)realloc (ubinaries
, sizeof (void*) * new_size
);
879 ubinaries_size
= new_size
;
881 sym
= (UnmanagedSymbol
*)calloc (sizeof (UnmanagedSymbol
), 1);
886 ubinaries
[ubinaries_num
++] = sym
;
889 static UnmanagedSymbol
*
890 lookup_unmanaged_binary (uintptr_t addr
)
893 for (i
= 0; i
< ubinaries_num
; ++i
) {
894 UnmanagedSymbol
*ubin
= ubinaries
[i
];
895 if (addr
>= ubin
->addr
&& addr
< ubin
->addr
+ ubin
->size
) {
903 sample_type_name (int 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";
917 set_usym_parent (UnmanagedSymbol
** cachedus
, int count
)
920 for (i
= 0; i
< count
; ++i
) {
921 UnmanagedSymbol
*ubin
= lookup_unmanaged_binary (cachedus
[i
]->addr
);
922 if (ubin
== cachedus
[i
])
924 cachedus
[i
]->parent
= ubin
;
929 print_usym (UnmanagedSymbol
* um
)
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
);
934 fprintf (outfile
, "\t%6zd %6.2f %s\n", um
->sample_hits
, um
->sample_hits
*100.0/num_stat_samples
, um
->name
);
938 sym_percent (uintptr_t sample_hits
)
943 pc
= sample_hits
*100.0/num_stat_samples
;
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
)
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
]);
963 if (!m
->sample_hits
) {
964 if (count
== msize
) {
968 cachedm
= (MethodDesc
**)realloc (cachedm
, sizeof (void*) * msize
);
970 cachedm
[count
++] = m
;
974 UnmanagedSymbol
*usym
= lookup_unmanaged_symbol (stat_samples
[i
]);
977 //printf ("unmanaged hit at %p\n", (void*)stat_samples [i]);
978 usym
= lookup_unmanaged_binary (stat_samples
[i
]);
981 if (!usym
->sample_hits
) {
982 if (ucount
== usize
) {
986 cachedus
= (UnmanagedSymbol
**)realloc (cachedus
, sizeof (void*) * usize
);
988 cachedus
[ucount
++] = usym
;
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");
1006 while (i
< count
|| u
< ucount
) {
1008 MethodDesc
*m
= cachedm
[i
];
1010 UnmanagedSymbol
*um
= cachedus
[u
];
1011 if (um
->sample_hits
> m
->sample_hits
) {
1012 if (!sym_percent (um
->sample_hits
))
1019 if (!sym_percent (m
->sample_hits
))
1021 fprintf (outfile
, "\t%6d %6.2f %s\n", m
->sample_hits
, m
->sample_hits
*100.0/num_stat_samples
, m
->name
);
1026 UnmanagedSymbol
*um
= cachedus
[u
];
1027 if (!sym_percent (um
->sample_hits
))
1036 typedef struct _HeapClassDesc HeapClassDesc
;
1038 HeapClassDesc
*klass
;
1042 struct _HeapClassDesc
{
1046 HeapClassRevRef
*rev_hash
;
1049 uintptr_t pinned_references
;
1050 uintptr_t root_references
;
1054 add_rev_class_hashed (HeapClassRevRef
*rev_hash
, uintptr_t size
, HeapClassDesc
*hklass
, uint64_t value
)
1057 uintptr_t start_pos
;
1058 start_pos
= (hklass
->klass
->klass
>> 2) % size
;
1059 assert (start_pos
< size
);
1062 if (rev_hash
[i
].klass
== hklass
) {
1063 rev_hash
[i
].count
+= value
;
1065 } else if (!rev_hash
[i
].klass
) {
1066 rev_hash
[i
].klass
= hklass
;
1067 rev_hash
[i
].count
+= value
;
1069 for (i
= 0; i
< size
; ++i
)
1070 if (rev_hash
[i
].klass
&& rev_hash
[i
].klass
->klass
== hklass
->klass
)
1072 assert (start_pos
== 1);
1078 } while (i
!= start_pos
);
1079 /* should not happen */
1080 printf ("failed revref store\n");
1085 add_heap_class_rev (HeapClassDesc
*from
, HeapClassDesc
*to
)
1088 if (to
->rev_count
* 2 >= to
->rev_hash_size
) {
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
);
1100 free (to
->rev_hash
);
1103 to
->rev_count
+= add_rev_class_hashed (to
->rev_hash
, to
->rev_hash_size
, from
, 1);
1108 HeapClassDesc
*hklass
;
1113 typedef struct _HeapShot HeapShot
;
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
;
1126 uintptr_t *roots_extra
;
1130 static HeapShot
*heap_shots
= NULL
;
1131 static int num_heap_shots
= 0;
1134 new_heap_shot (uint64_t timestamp
)
1136 HeapShot
*hs
= (HeapShot
*)calloc (sizeof (HeapShot
), 1);
1138 hs
->class_hash
= (HeapClassDesc
**)calloc (sizeof (void*), hs
->hash_size
);
1139 hs
->timestamp
= timestamp
;
1141 hs
->next
= heap_shots
;
1146 static HeapClassDesc
*
1147 heap_class_lookup (HeapShot
*hs
, ClassDesc
*klass
)
1150 unsigned int start_pos
;
1151 start_pos
= ((uintptr_t)klass
->klass
>> 2) % hs
->hash_size
;
1154 HeapClassDesc
* cd
= hs
->class_hash
[i
];
1157 if (cd
->klass
== klass
)
1160 if (++i
== hs
->hash_size
)
1162 } while (i
!= start_pos
);
1167 add_heap_hashed (HeapClassDesc
**hash
, HeapClassDesc
**retv
, uintptr_t hsize
, ClassDesc
*klass
, uint64_t size
, uint64_t count
)
1170 uintptr_t start_pos
;
1171 start_pos
= ((uintptr_t)klass
->klass
>> 2) % hsize
;
1174 if (hash
[i
] && hash
[i
]->klass
== klass
) {
1175 hash
[i
]->total_size
+= size
;
1176 hash
[i
]->count
+= count
;
1179 } else if (!hash
[i
]) {
1184 hash
[i
] = (HeapClassDesc
*)calloc (sizeof (HeapClassDesc
), 1);
1185 hash
[i
]->klass
= klass
;
1186 hash
[i
]->total_size
+= size
;
1187 hash
[i
]->count
+= count
;
1194 } while (i
!= start_pos
);
1195 /* should not happen */
1196 printf ("failed heap class store\n");
1200 static HeapClassDesc
*
1201 add_heap_shot_class (HeapShot
*hs
, ClassDesc
*klass
, uint64_t size
)
1205 if (hs
->class_count
* 2 >= hs
->hash_size
) {
1207 int old_size
= hs
->hash_size
;
1209 if (hs
->hash_size
== 0)
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
);
1218 free (hs
->class_hash
);
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);
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
;
1239 heap_shot_find_obj_slot (HeapShot
*hs
, uintptr_t objaddr
)
1242 uintptr_t start_pos
;
1243 HeapObjectDesc
**hash
= hs
->objects_hash
;
1244 start_pos
= ((uintptr_t)objaddr
>> 3) % hs
->objects_hash_size
;
1247 if (hash
[i
] && hash
[i
]->objaddr
== objaddr
) {
1249 } else if (!hash
[i
]) {
1253 if (++i
== hs
->objects_hash_size
)
1255 } while (i
!= start_pos
);
1256 /* should not happen */
1257 //printf ("failed heap obj slot\n");
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
);
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));
1274 /* should not happen */
1275 printf ("failed heap obj update\n");
1281 add_heap_hashed_obj (HeapObjectDesc
**hash
, uintptr_t hsize
, HeapObjectDesc
*obj
)
1284 uintptr_t start_pos
;
1285 start_pos
= ((uintptr_t)obj
->objaddr
>> 3) % hsize
;
1288 if (hash
[i
] && hash
[i
]->objaddr
== obj
->objaddr
) {
1289 printf ("duplicate object!\n");
1291 } else if (!hash
[i
]) {
1298 } while (i
!= start_pos
);
1299 /* should not happen */
1300 printf ("failed heap obj store\n");
1305 add_heap_shot_obj (HeapShot
*hs
, HeapObjectDesc
*obj
)
1308 if (hs
->objects_count
* 2 >= hs
->objects_hash_size
) {
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
);
1327 heap_shot_resolve_reverse_refs (HeapShot
*hs
)
1330 for (i
= 0; i
< hs
->objects_hash_size
; ++i
) {
1332 HeapObjectDesc
*ho
= hs
->objects_hash
[i
];
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
);
1343 #define MARK_BLACK 2
1346 heap_shot_mark_objects (HeapShot
*hs
)
1349 unsigned char *marks
;
1350 HeapObjectDesc
*obj
, *ref
;
1352 uintptr_t num_marked
= 0, num_unmarked
;
1353 for (i
= 0; i
< hs
->num_roots
; ++i
) {
1355 oi
= heap_shot_find_obj_slot (hs
, hs
->roots
[i
]);
1359 obj
= hs
->objects_hash
[oi
];
1361 if (hs
->roots_types
[i
] & MONO_PROFILE_GC_ROOT_PINNING
)
1362 cd
->pinned_references
++;
1363 cd
->root_references
++;
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);
1371 for (i
= 0; i
< hs
->num_roots
; ++i
) {
1372 oi
= heap_shot_find_obj_slot (hs
, hs
->roots
[i
]);
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
);
1377 obj
= hs
->objects_hash
[oi
];
1379 marks
[oi
] = obj
->num_refs
? MARK_GRAY
: MARK_BLACK
;
1384 while (marked_some
) {
1386 for (i
= 0; i
< hs
->objects_hash_size
; ++i
) {
1387 if (marks
[i
] != MARK_GRAY
)
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
]);
1394 fprintf (outfile
, "referenced obj %p not found in heap\n", (void*)obj
->refs
[r
]);
1397 ref
= hs
->objects_hash
[oi
];
1399 marks
[oi
] = ref
->num_refs
? MARK_GRAY
: MARK_BLACK
;
1407 for (i
= 0; i
< hs
->objects_hash_size
; ++i
) {
1408 if (hs
->objects_hash
[i
] && !marks
[i
]) {
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
);
1418 heap_shot_free_objects (HeapShot
*hs
)
1421 for (i
= 0; i
< hs
->objects_hash_size
; ++i
) {
1422 HeapObjectDesc
*ho
= hs
->objects_hash
[i
];
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;
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;
1448 hash_backtrace (int count
, MethodDesc
**methods
)
1452 for (i
= 0; i
< count
; ++i
) {
1453 hash
= (hash
<< 5) - hash
+ methods
[i
]->method
;
1459 compare_backtrace (BackTrace
*bt
, int count
, MethodDesc
**methods
)
1462 if (bt
->count
!= count
)
1464 for (i
= 0; i
< count
; ++i
)
1465 if (methods
[i
] != bt
->methods
[i
])
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
];
1477 if (bt
->hash
== hash
&& compare_backtrace (bt
, count
, methods
))
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
;
1494 for (slot
= 0; slot
< count
; ++slot
)
1495 bt
->methods
[slot
] = methods
[slot
];
1500 typedef struct _MonitorDesc MonitorDesc
;
1501 typedef struct _ThreadContext ThreadContext
;
1502 typedef struct _DomainContext DomainContext
;
1503 typedef struct _RemCtxContext RemCtxContext
;
1507 #if defined (HAVE_SYS_ZLIB)
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
;
1527 struct _ThreadContext
{
1528 ThreadContext
*next
;
1531 /* emulated stack */
1533 uint64_t *time_stack
;
1534 uint64_t *callee_time_stack
;
1536 uint64_t contention_start
;
1537 MonitorDesc
*monitor
;
1540 HeapShot
*current_heap_shot
;
1541 uintptr_t num_roots
;
1542 uintptr_t size_roots
;
1544 uintptr_t *roots_extra
;
1546 uint64_t gc_start_times
[3];
1549 struct _DomainContext
{
1550 DomainContext
*next
;
1552 const char *friendly_name
;
1555 struct _RemCtxContext
{
1556 RemCtxContext
*next
;
1562 ensure_buffer (ProfContext
*ctx
, int size
)
1564 if (ctx
->size
< size
) {
1565 ctx
->buf
= (unsigned char *)realloc (ctx
->buf
, size
);
1571 load_data (ProfContext
*ctx
, int size
)
1573 ensure_buffer (ctx
, size
);
1574 #if defined (HAVE_SYS_ZLIB)
1576 int r
= gzread (ctx
->gzfile
, ctx
->buf
, size
);
1578 return size
== 0? 1: 0;
1583 int r
= fread (ctx
->buf
, size
, 1, ctx
->file
);
1585 return size
== 0? 1: 0;
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
;
1598 if (thread
->thread_id
== thread_id
) {
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));
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
;
1625 if (domain
->domain_id
== domain_id
)
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
;
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
;
1648 if (remctx
->remctx_id
== remctx_id
)
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
;
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
;
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));
1682 add_trace_hashed (CallContext
*traces
, int size
, BackTrace
*bt
, uint64_t value
)
1685 unsigned int start_pos
;
1686 start_pos
= bt
->hash
% size
;
1689 if (traces
[i
].bt
== bt
) {
1690 traces
[i
].count
+= value
;
1692 } else if (!traces
[i
].bt
) {
1694 traces
[i
].count
+= value
;
1700 } while (i
!= start_pos
);
1701 /* should not happen */
1702 printf ("failed trace store\n");
1707 add_trace_bt (BackTrace
*bt
, TraceDesc
*trace
, uint64_t value
)
1710 if (!collect_traces
)
1712 if (trace
->count
* 2 >= trace
->size
) {
1714 int old_size
= trace
->size
;
1716 if (trace
->size
== 0)
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
);
1724 free (trace
->traces
);
1727 trace
->count
+= add_trace_hashed (trace
->traces
, trace
->size
, bt
, value
);
1731 add_trace_thread (ThreadContext
*thread
, TraceDesc
*trace
, uint64_t value
)
1734 int count
= thread
->stack_id
;
1735 if (!collect_traces
)
1737 if (count
> trace_max
)
1739 bt
= add_backtrace (count
, thread
->stack
+ thread
->stack_id
- count
);
1740 add_trace_bt (bt
, trace
, value
);
1745 add_trace_methods (MethodDesc
**methods
, int count
, TraceDesc
*trace
, uint64_t value
)
1748 if (!collect_traces
)
1750 if (count
> trace_max
)
1752 bt
= add_backtrace (count
, methods
);
1753 add_trace_bt (bt
, trace
, value
);
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;
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
;
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
)
1781 if (B
->count
< A
->count
)
1787 sort_context_array (TraceDesc
* traces
)
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
;
1797 qsort (traces
->traces
, traces
->count
, sizeof (CallContext
), compare_callc
);
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
++;
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
) {
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));
1829 fprintf (outfile
, "unmatched leave at stack pos: %d for method %s\n", thread
->stack_id
, method
->name
);
1834 uint64_t total_time
;
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
;
1848 TraceDesc destroy_traces
;
1850 static HandleInfo handle_info
[4];
1853 gc_event_name (int 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";
1871 static uint64_t clause_summary
[MONO_EXCEPTION_CLAUSE_FAULT
+ 1];
1872 static uint64_t throw_count
= 0;
1873 static TraceDesc exc_traces
;
1876 clause_name (int 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
{
1894 uintptr_t contentions
;
1896 uint64_t max_wait_time
;
1900 static MonitorDesc
* monitor_hash
[SMALL_HASH_SIZE
] = {0};
1901 static int num_monitors
= 0;
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
)
1911 cd
= (MonitorDesc
*)calloc (sizeof (MonitorDesc
), 1);
1913 cd
->next
= monitor_hash
[slot
];
1914 monitor_hash
[slot
] = cd
;
1921 monitor_ev_name (int 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";
1932 get_handle_name (int 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";
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";
1957 decode_bt (MethodDesc
** sframes
, int *size
, unsigned char *p
, unsigned char **endp
, intptr_t ptr_base
)
1959 MethodDesc
**frames
;
1961 int flags
= decode_uleb128 (p
, &p
);
1962 int count
= decode_uleb128 (p
, &p
);
1966 frames
= (MethodDesc
**)malloc (count
* sizeof (void*));
1969 for (i
= 0; i
< count
; ++i
) {
1970 intptr_t ptrdiff
= decode_sleb128 (p
, &p
);
1971 frames
[i
] = lookup_method (ptr_base
+ ptrdiff
);
1979 tracked_creation (uintptr_t obj
, ClassDesc
*cd
, uint64_t size
, BackTrace
*bt
, uint64_t timestamp
)
1982 for (i
= 0; i
< num_tracked_objects
; ++i
) {
1983 if (tracked_objects
[i
] != obj
)
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
) {
1988 for (k
= 0; k
< bt
->count
; ++k
)
1989 fprintf (outfile
, "\t%s\n", bt
->methods
[k
]->name
);
1995 track_handle (uintptr_t obj
, int htype
, uint32_t handle
, BackTrace
*bt
, uint64_t timestamp
)
1998 for (i
= 0; i
< num_tracked_objects
; ++i
) {
1999 if (tracked_objects
[i
] != obj
)
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
) {
2004 for (k
= 0; k
< bt
->count
; ++k
)
2005 fprintf (outfile
, "\t%s\n", bt
->methods
[k
]->name
);
2011 track_move (uintptr_t src
, uintptr_t dst
)
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
);
2023 track_obj_reference (uintptr_t obj
, uintptr_t parent
, ClassDesc
*cd
)
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
);
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;
2044 code_buffer_desc (int type
)
2047 case MONO_PROFILER_CODE_BUFFER_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";
2068 return "unspecified";
2072 typedef struct _CoverageAssembly CoverageAssembly
;
2073 struct _CoverageAssembly
{
2077 int number_of_methods
;
2079 int partially_covered
;
2082 typedef struct _CoverageClass CoverageClass
;
2083 struct _CoverageClass
{
2084 char *assembly_name
;
2086 int number_of_methods
;
2088 int partially_covered
;
2091 typedef struct _CoverageCoverage CoverageCoverage
;
2092 struct _CoverageCoverage
{
2100 typedef struct _CoverageMethod CoverageMethod
;
2101 struct _CoverageMethod
{
2102 char *assembly_name
;
2105 char *method_signature
;
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
;
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
);
2130 g_ptr_array_add (method
->coverage
, coverage
);
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
);
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
);
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
);
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)*/
2188 #define BUFFER_HEADER_SIZE 48
2191 int count
, min_size
, max_size
, bytes
;
2194 static int buffer_count
;
2195 static EventStat stats
[256];
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
;
2209 decode_buffer (ProfContext
*ctx
)
2216 intptr_t method_base
;
2218 uint64_t file_offset
;
2220 ThreadContext
*thread
;
2222 #ifdef HAVE_SYS_ZLIB
2224 file_offset
= gztell (ctx
->gzfile
);
2227 file_offset
= ftell (ctx
->file
);
2228 if (!load_data (ctx
, 48))
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");
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);
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
))
2252 if (!startup_time
) {
2253 startup_time
= time_base
;
2254 if (use_time_filter
) {
2255 time_from
+= startup_time
;
2256 time_to
+= startup_time
;
2259 thread
->name
= pstrdup ("Main");
2261 for (i
= 0; i
< thread
->stack_id
; ++i
)
2262 thread
->stack
[i
]->recurse_count
++;
2266 unsigned char *start
= p
;
2267 unsigned char event
= *p
;
2270 int subtype
= *p
& 0xf0;
2271 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2272 LOG_TIME (time_base
, tdiff
);
2274 if (subtype
== TYPE_GC_RESIZE
) {
2275 uint64_t new_size
= decode_uleb128 (p
, &p
);
2277 fprintf (outfile
, "gc heap resized to %llu\n", (unsigned long long) new_size
);
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
);
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
);
2287 fprintf (outfile
, "incorrect gc gen: %d\n", gen
);
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
));
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
;
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
);
2321 frames
= decode_bt (sframes
, &num_bt
, p
, &p
, ptr_base
);
2323 fprintf (outfile
, "Cannot load backtrace\n");
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
;
2336 bt
= add_trace_methods (frames
, num_bt
, &handle_info
[htype
].traces
, 1);
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
);
2343 fprintf (outfile
, "handle (%s) %u created for object %p\n", get_handle_name (htype
), handle
, (void*)OBJ_ADDR (objdiff
));
2344 if (frames
!= sframes
)
2346 } else if (subtype
== TYPE_GC_HANDLE_DESTROYED
|| subtype
== TYPE_GC_HANDLE_DESTROYED_BT
) {
2347 int has_bt
= subtype
== TYPE_GC_HANDLE_DESTROYED_BT
;
2349 MethodDesc
*sframes
[8];
2350 MethodDesc
**frames
= sframes
;
2351 int htype
= decode_uleb128 (p
, &p
);
2352 uint32_t handle
= decode_uleb128 (p
, &p
);
2355 frames
= decode_bt (sframes
, &num_bt
, p
, &p
, ptr_base
);
2357 fprintf (outfile
, "Cannot load backtrace\n");
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
--;
2368 bt
= add_trace_methods (frames
, num_bt
, &handle_info
[htype
].destroy_traces
, 1);
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... */
2374 fprintf (outfile
, "handle (%s) %u destroyed\n", get_handle_name (htype
), handle
);
2375 if (frames
!= sframes
)
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
);
2385 intptr_t ptrdiff
= decode_sleb128 (p
, &p
);
2386 LOG_TIME (time_base
, tdiff
);
2388 if (mtype
== TYPE_CLASS
) {
2389 intptr_t imptrdiff
= decode_sleb128 (p
, &p
);
2390 uint64_t flags
= decode_uleb128 (p
, &p
);
2392 fprintf (outfile
, "non-zero flags in class\n");
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
);
2401 } else if (mtype
== TYPE_IMAGE
) {
2402 uint64_t flags
= decode_uleb128 (p
, &p
);
2404 fprintf (outfile
, "non-zero flags in image\n");
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
);
2413 } else if (mtype
== TYPE_ASSEMBLY
) {
2414 uint64_t flags
= decode_uleb128 (p
, &p
);
2416 fprintf (outfile
, "non-zero flags in assembly\n");
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
);
2425 } else if (mtype
== TYPE_DOMAIN
) {
2426 uint64_t flags
= decode_uleb128 (p
, &p
);
2428 fprintf (outfile
, "non-zero flags in domain\n");
2431 DomainContext
*nd
= get_domain (ctx
, ptr_base
+ ptrdiff
);
2432 /* no subtype means it's a name event, rather than start/stop */
2434 nd
->friendly_name
= pstrdup ((char *) p
);
2437 fprintf (outfile
, "domain %p named at %llu: %s\n", (void *) (ptr_base
+ ptrdiff
), (unsigned long long) time_base
, p
);
2439 fprintf (outfile
, "%s thread %p at %llu\n", load_str
, (void *) (ptr_base
+ ptrdiff
), (unsigned long long) time_base
);
2445 } else if (mtype
== TYPE_CONTEXT
) {
2446 uint64_t flags
= decode_uleb128 (p
, &p
);
2448 fprintf (outfile
, "non-zero flags in context\n");
2451 intptr_t domaindiff
= decode_sleb128 (p
, &p
);
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
);
2459 fprintf (outfile
, "non-zero flags in thread\n");
2462 ThreadContext
*nt
= get_thread (ctx
, ptr_base
+ ptrdiff
);
2463 /* no subtype means it's a name event, rather than start/stop */
2465 nt
->name
= pstrdup ((char*)p
);
2468 fprintf (outfile
, "thread %p named at %llu: %s\n", (void*)(ptr_base
+ ptrdiff
), (unsigned long long) time_base
, p
);
2470 fprintf (outfile
, "%s thread %p at %llu\n", load_str
, (void *) (ptr_base
+ ptrdiff
), (unsigned long long) time_base
);
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
);
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
);
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
);
2496 frames
= decode_bt (sframes
, &num_bt
, p
, &p
, ptr_base
);
2498 fprintf (outfile
, "Cannot load backtrace\n");
2502 if ((thread_filter
&& thread_filter
== thread
->thread_id
) || (time_base
>= time_from
&& time_base
< time_to
)) {
2505 cd
->alloc_size
+= len
;
2507 bt
= add_trace_methods (frames
, num_bt
, &cd
->traces
, len
);
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
)
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
);
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
;
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;
2543 if ((thread_filter
&& thread_filter
!= thread
->thread_id
))
2545 if (!(time_base
>= time_from
&& time_base
< time_to
))
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
);
2552 pop_method (thread
, method
, time_base
);
2555 fprintf (outfile
, "%s method %s\n", subtype
== TYPE_ENTER
? "enter": subtype
== TYPE_EXC_LEAVE
? "exleave": "leave", method
->name
);
2560 int subtype
= *p
& 0xf0;
2561 if (subtype
== TYPE_HEAP_OBJECT
) {
2562 HeapObjectDesc
*ho
= NULL
;
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
);
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
);
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
;
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
);
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
);
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
);
2606 fprintf (outfile
, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff
), get_root_name (root_type
));
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
);
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
;
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
);
2644 fprintf (outfile
, "heap shot start\n");
2645 thread
->current_heap_shot
= new_heap_shot (time_base
);
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
;
2658 LOG_TIME (time_base
, tdiff
);
2660 record
= (!thread_filter
|| thread_filter
== thread
->thread_id
);
2661 if (!(time_base
>= time_from
&& time_base
< time_to
))
2663 if (event
== MONO_PROFILER_MONITOR_CONTENTION
) {
2664 MonitorDesc
*mdesc
= lookup_monitor (OBJ_ADDR (objdiff
));
2666 monitor_contention
++;
2667 mdesc
->contentions
++;
2668 thread
->monitor
= mdesc
;
2669 thread
->contention_start
= time_base
;
2673 frames
= decode_bt (sframes
, &num_bt
, p
, &p
, ptr_base
);
2675 fprintf (outfile
, "Cannot load backtrace\n");
2679 add_trace_methods (frames
, num_bt
, &mdesc
->traces
, 1);
2682 add_trace_thread (thread
, &mdesc
->traces
, 1);
2684 } else if (event
== MONO_PROFILER_MONITOR_FAIL
) {
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
) {
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;
2710 fprintf (outfile
, "monitor %s for object %p\n", monitor_ev_name (event
), (void*)OBJ_ADDR (objdiff
));
2711 if (frames
!= sframes
)
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
;
2722 LOG_TIME (time_base
, tdiff
);
2724 record
= (!thread_filter
|| thread_filter
== thread
->thread_id
);
2725 if (!(time_base
>= time_from
&& time_base
< time_to
))
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
;
2733 clause_summary
[clause_type
]++;
2735 fprintf (outfile
, "clause %s (%d) in method %s\n", clause_name (clause_type
), clause_num
, lookup_method (method_base
)->name
);
2737 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2742 frames
= decode_bt (sframes
, &has_bt
, p
, &p
, ptr_base
);
2744 fprintf (outfile
, "Cannot load backtrace\n");
2748 add_trace_methods (frames
, has_bt
, &exc_traces
, 1);
2751 add_trace_thread (thread
, &exc_traces
, 1);
2753 if (frames
!= sframes
)
2756 fprintf (outfile
, "throw %p\n", (void*)OBJ_ADDR (objdiff
));
2760 case TYPE_RUNTIME
: {
2761 int subtype
= *p
& 0xf0;
2762 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2763 LOG_TIME (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
);
2770 if (type
== MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
) {
2771 name
= (const char *)p
;
2775 name
= code_buffer_desc (type
);
2778 jit_helpers_code_size
+= codelen
;
2780 fprintf (outfile
, "jit helper %s, size: %d, code: %p\n", name
, codelen
, (void*)(ptr_base
+ codediff
));
2785 int subtype
= *p
& 0xf0;
2786 if (subtype
== TYPE_SAMPLE_HIT
) {
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
);
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
) {
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
);
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
);
2819 name
= pstrdup ((char*)p
);
2820 add_unmanaged_symbol (addr
, name
, size
);
2822 fprintf (outfile
, "unmanaged symbol %s at %p\n", name
, (void*)addr
);
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
);
2832 LOG_TIME (time_base
, tdiff
);
2834 name
= pstrdup ((char*)p
);
2835 add_unmanaged_binary (addr
, name
, size
);
2837 fprintf (outfile
, "unmanaged binary %s at %p\n", name
, (void*)addr
);
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
);
2849 section_str
= pstrdup ((char*)p
);
2852 name
= pstrdup ((char*)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
) {
2862 CounterValue
*value
, *previous
= NULL
;
2864 uint64_t timestamp
= decode_uleb128 (p
+ 1, &p
);
2865 uint64_t time_between
= timestamp
/ 1000 * 1000 * 1000 * 1000 + startup_time
;
2867 uint64_t type
, index
= decode_uleb128 (p
, &p
);
2871 for (list
= counters
; list
; list
= list
->next
) {
2872 if (list
->counter
->index
== (int)index
) {
2873 previous
= list
->counter
->values_last
;
2878 type
= decode_uleb128 (p
, &p
);
2880 value
= (CounterValue
*)calloc (1, sizeof (CounterValue
));
2881 value
->timestamp
= timestamp
;
2884 case MONO_COUNTER_INT
:
2885 #if SIZEOF_VOID_P == 4
2886 case MONO_COUNTER_WORD
:
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);
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);
2895 case MONO_COUNTER_LONG
:
2896 #if SIZEOF_VOID_P == 8
2897 case MONO_COUNTER_WORD
:
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);
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);
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
++)
2912 for (i
= sizeof (double) - 1; i
>= 0; i
--)
2914 value
->buffer
[i
] = *p
++;
2916 case MONO_COUNTER_STRING
:
2918 value
->buffer
= NULL
;
2920 value
->buffer
= (unsigned char*) pstrdup ((char*)p
);
2925 if (time_between
>= time_from
&& time_between
<= time_to
)
2926 add_counter_value (index
, value
);
2933 case TYPE_COVERAGE
:{
2934 int subtype
= *p
& 0xf0;
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
;
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
);
2966 case TYPE_COVERAGE_STATEMENT
: {
2967 CoverageCoverage
*coverage
= g_new0 (CoverageCoverage
, 1);
2968 int offset
, count
, line
, column
, method_id
;
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
);
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
;
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
);
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
;
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
);
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
);
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;
3049 load_file (char *name
)
3052 ProfContext
*ctx
= (ProfContext
*)calloc (sizeof (ProfContext
), 1);
3053 if (strcmp (name
, "-") == 0)
3056 ctx
->file
= fopen (name
, "rb");
3058 printf ("Cannot open file: %s\n", name
);
3061 #if defined (HAVE_SYS_ZLIB)
3062 if (ctx
->file
!= stdin
)
3063 ctx
->gzfile
= gzdopen (fileno (ctx
->file
), "rb");
3065 if (!load_data (ctx
, 32))
3068 if (read_int32 (p
) != LOG_HEADER_ID
|| p
[6] > LOG_DATA_VERSION
)
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*))
3076 if (read_int32 (p
+ 20)) /* flags must be 0 */
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);
3089 static int alloc_sort_mode
= ALLOC_SORT_BYTES
;
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
;
3101 vala
= (*A
)->allocs
;
3102 valb
= (*B
)->allocs
;
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
);
3122 fprintf (outfile
, "\tProgram ID: %d\n", ctx
->pid
);
3124 fprintf (outfile
, "\tServer listening on: %d\n", ctx
->port
);
3128 dump_traces (TraceDesc
*traces
, const char *desc
)
3135 sort_context_array (traces
);
3136 for (j
= 0; j
< traces
->count
; ++j
) {
3139 bt
= traces
->traces
[j
].bt
;
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
);
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
: "");
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
);
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
);
3177 dump_exceptions (void)
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
])
3186 fprintf (outfile
, "\tExecuted %s clauses: %llu\n", clause_name (i
), (unsigned long long) clause_summary
[i
]);
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
)
3197 if ((*B
)->wait_time
< (*A
)->wait_time
)
3203 dump_monitors (void)
3205 MonitorDesc
**monitors
;
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
];
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
);
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
)
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
)
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");
3266 int compiled_methods
= 0;
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
)
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
);
3285 dump_allocations (void)
3288 intptr_t allocs
= 0;
3290 int header_done
= 0;
3291 ClassDesc
**classes
= (ClassDesc
**)malloc (num_classes
* sizeof (void*));
3294 for (i
= 0; i
< HASH_SIZE
; ++i
) {
3295 cd
= class_hash
[i
];
3301 qsort (classes
, num_classes
, sizeof (void*), compare_class
);
3302 for (i
= 0; i
< num_classes
; ++i
) {
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
),
3315 (unsigned long long) (cd
->alloc_size
/ cd
->allocs
),
3317 dump_traces (&cd
->traces
, "bytes");
3320 fprintf (outfile
, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size
, allocs
);
3329 static int method_sort_mode
= METHOD_SORT_TOTAL
;
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
) {
3344 vala
= (*A
)->total_time
;
3345 valb
= (*B
)->total_time
;
3355 dump_metadata (void)
3357 fprintf (outfile
, "\nMetadata summary\n");
3358 fprintf (outfile
, "\tLoaded images: %d\n", num_images
);
3362 for (i
= 0; i
< SMALL_HASH_SIZE
; ++i
) {
3363 image
= image_hash
[i
];
3365 fprintf (outfile
, "\t\t%s\n", image
->filename
);
3366 image
= image
->next
;
3370 fprintf (outfile
, "\tLoaded assemblies: %d\n", num_assemblies
);
3372 AssemblyDesc
*assembly
;
3374 for (i
= 0; i
< SMALL_HASH_SIZE
; ++i
) {
3375 assembly
= assembly_hash
[i
];
3377 fprintf (outfile
, "\t\t%s\n", assembly
->asmname
);
3378 assembly
= assembly
->next
;
3389 int header_done
= 0;
3390 MethodDesc
**methods
= (MethodDesc
**)malloc (num_methods
* sizeof (void*));
3393 for (i
= 0; i
< HASH_SIZE
; ++i
) {
3394 cd
= method_hash
[i
];
3396 cd
->total_time
= cd
->self_time
+ cd
->callee_time
;
3401 qsort (methods
, num_methods
, sizeof (void*), compare_method
);
3402 for (i
= 0; i
< num_methods
; ++i
) {
3409 msecs
= cd
->total_time
/ 1000000;
3410 smsecs
= (cd
->total_time
- cd
->callee_time
) / 1000000;
3411 if (!msecs
&& !verbose
)
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
),
3422 dump_traces (&cd
->traces
, "calls");
3425 fprintf (outfile
, "Total calls: %llu\n", (unsigned long long) calls
);
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
;
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
)
3455 if (B
->count
< A
->count
)
3461 dump_rev_claases (HeapClassRevRef
*revs
, int count
)
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
),
3477 heap_shot_summary (HeapShot
*hs
, int hs_num
, HeapShot
*last_hs
)
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
];
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",
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)
3504 fprintf (outfile
, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
3505 for (i
= 0; i
< ccount
; ++i
) {
3506 HeapClassRevRef
*rev_sorted
;
3508 HeapClassDesc
*ocd
= NULL
;
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
),
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
);
3522 fprintf (outfile
, "\n");
3524 if (!collect_traces
)
3526 rev_sorted
= (HeapClassRevRef
*)malloc (cd
->rev_count
* sizeof (HeapClassRevRef
));
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
);
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
)
3549 if ((*B
)->timestamp
> (*A
)->timestamp
)
3555 dump_heap_shots (void)
3557 HeapShot
**hs_sorted
;
3559 HeapShot
*last_hs
= NULL
;
3563 hs_sorted
= (HeapShot
**)malloc (num_heap_shots
* sizeof (void*));
3564 fprintf (outfile
, "\nHeap shot summary\n");
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
) {
3571 heap_shot_summary (hs
, i
, last_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. */
3581 escape_string_for_xml (const char *string
)
3583 GString
*string_builder
= g_string_new (NULL
);
3584 const char *start
, *p
;
3588 while (*p
&& *p
!= '&' && *p
!= '<' && *p
!= '>')
3591 g_string_append_len (string_builder
, start
, p
- start
);
3598 g_string_append (string_builder
, "<");
3602 g_string_append (string_builder
, ">");
3606 g_string_append (string_builder
, "&");
3617 return g_string_free (string_builder
, FALSE
);
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
)
3628 else if (assembly_a
->name
== NULL
)
3630 else if (assembly_b
->name
== NULL
)
3633 return strcmp (assembly_a
->name
, assembly_b
->name
);
3637 dump_coverage (void)
3639 if (!coverage_methods
&& !coverage_assemblies
)
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
];
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
);
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
);
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
);
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
) {
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);
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
];
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
);
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
);
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
) {
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";
3814 match_option (const char *p
, const char *opt
)
3816 int len
= strlen (opt
);
3817 if (strncmp (p
, opt
, len
) == 0) {
3826 print_reports (ProfContext
*ctx
, const char *reps
, int parse_only
)
3830 for (p
= reps
; *p
; p
= opt
) {
3831 if ((opt
= match_option (p
, "header")) != p
) {
3836 if ((opt
= match_option (p
, "thread")) != p
) {
3841 if ((opt
= match_option (p
, "domain")) != p
) {
3846 if ((opt
= match_option (p
, "context")) != p
) {
3851 if ((opt
= match_option (p
, "gc")) != p
) {
3856 if ((opt
= match_option (p
, "jit")) != p
) {
3861 if ((opt
= match_option (p
, "alloc")) != p
) {
3863 dump_allocations ();
3866 if ((opt
= match_option (p
, "call")) != p
) {
3871 if ((opt
= match_option (p
, "metadata")) != p
) {
3876 if ((opt
= match_option (p
, "exception")) != p
) {
3881 if ((opt
= match_option (p
, "monitor")) != p
) {
3886 if ((opt
= match_option (p
, "heapshot")) != p
) {
3891 if ((opt
= match_option (p
, "sample")) != p
) {
3896 if ((opt
= match_option (p
, "counters")) != p
) {
3901 if ((opt
= match_option (p
, "coverage")) != p
) {
3906 if ((opt
= match_option (p
, "stats")) != p
) {
3917 add_find_spec (const char *p
)
3919 if (p
[0] == 'S' && p
[1] == ':') {
3921 find_size
= strtoul (p
+ 2, &vale
, 10);
3923 } else if (p
[0] == 'T' && p
[1] == ':') {
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
[])
3963 for (i
= 1; i
< argc
; ++i
) {
3964 if (strcmp ("--debug", argv
[i
]) == 0) {
3966 } else if (strcmp ("--help", argv
[i
]) == 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
;
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
;
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
;
4001 } else if (strncmp ("--reports=", argv
[i
], 10) == 0) {
4002 const char *val
= argv
[i
] + 10;
4003 if (!print_reports (NULL
, val
, 1)) {
4008 } else if (strncmp ("--out=", argv
[i
], 6) == 0) {
4009 const char *val
= argv
[i
] + 6;
4010 outfile
= fopen (val
, "w");
4012 printf ("Cannot open output file: %s\n", val
);
4015 } else if (strncmp ("--maxframes=", argv
[i
], 12) == 0) {
4016 const char *val
= argv
[i
] + 12;
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
)) {
4025 } else if (strncmp ("--track=", argv
[i
], 8) == 0) {
4026 const char *val
= argv
[i
] + 8;
4029 uintptr_t tracked_obj
;
4034 tracked_obj
= strtoul (val
, &vale
, 0);
4035 found_object (tracked_obj
);
4038 } else if (strncmp ("--thread=", argv
[i
], 9) == 0) {
4039 const char *val
= argv
[i
] + 9;
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
, '-');
4051 from_secs
= atof (val
);
4052 to_secs
= atof (top
);
4054 if (from_secs
> to_secs
) {
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) {
4063 } else if (strcmp ("--traces", argv
[i
]) == 0) {
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
);
4081 ctx
= load_file (argv
[i
]);
4083 printf ("Not a log profiler data file (or unsupported version).\n");
4086 while (decode_buffer (ctx
));
4087 flush_context (ctx
);
4088 if (num_tracked_objects
)
4090 print_reports (ctx
, reports
, 0);