2 * mprof-report.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)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13 * The Coverage XML output schema
23 * <coverage> - The root element of the documentation. It can contain any number of
24 * <assembly>, <class> or <method> elements.
26 * - version: The version number for the file format - (eg: "0.3")
27 * <assembly> - Contains data about assemblies. Has no child elements
29 * - name: The name of the assembly - (eg: "System.Xml")
30 * - guid: The GUID of the assembly
31 * - filename: The filename of the assembly
32 * - method-count: The number of methods in the assembly
33 * - full: The number of fully covered methods
34 * - partial: The number of partially covered methods
35 * <class> - Contains data about classes. Has no child elements
37 * - name: The name of the class
38 * - method-count: The number of methods in the class
39 * - full: The number of fully covered methods
40 * - partial: The number of partially covered methods
41 * <method> - Contains data about methods. Can contain any number of <statement> elements
43 * - assembly: The name of the parent assembly
44 * - class: The name of the parent class
45 * - name: The name of the method, with all it's parameters
46 * - filename: The name of the source file containing this method
48 * <statement> - Contains data about IL statements. Has no child elements
50 * - offset: The offset of the statement in the IL code after the previous
52 * - counter: 1 if the line was covered, 0 if it was not
53 * - line: The line number in the parent method's file
54 * - column: The column on the line
57 #include "mono-profiler-log.h"
62 #if !defined(__APPLE__) && !defined(__FreeBSD__)
67 #if defined (HAVE_SYS_ZLIB)
71 #include <mono/metadata/profiler.h>
72 #include <mono/metadata/object.h>
73 #include <mono/metadata/debug-helpers.h>
74 #include <mono/utils/mono-counters.h>
76 #define HASH_SIZE 9371
77 #define SMALL_HASH_SIZE 31
79 #if defined(__native_client__) || defined(__native_client_codegen__)
80 volatile int __nacl_thread_suspension_needed
= 0;
81 void __nacl_suspend_thread_if_needed() {}
85 static int collect_traces
= 0;
86 static int show_traces
= 0;
87 static int trace_max
= 6;
88 static int verbose
= 0;
89 static uintptr_t *tracked_objects
= 0;
90 static int num_tracked_objects
= 0;
91 static uintptr_t thread_filter
= 0;
92 static uint64_t find_size
= 0;
93 static const char* find_name
= NULL
;
94 static uint64_t time_from
= 0;
95 static uint64_t time_to
= 0xffffffffffffffffULL
;
96 static int use_time_filter
= 0;
97 static uint64_t startup_time
= 0;
98 static FILE* outfile
= NULL
;
99 static FILE* coverage_outfile
= NULL
;
102 read_int16 (unsigned char *p
)
104 int32_t value
= *p
++;
105 value
|= (*p
++) << 8;
110 read_int32 (unsigned char *p
)
112 int32_t value
= *p
++;
113 value
|= (*p
++) << 8;
114 value
|= (*p
++) << 16;
115 value
|= (uint32_t)(*p
++) << 24;
120 read_int64 (unsigned char *p
)
122 uint64_t value
= *p
++;
123 value
|= (*p
++) << 8;
124 value
|= (*p
++) << 16;
125 value
|= (uint64_t)(*p
++) << 24;
126 value
|= (uint64_t)(*p
++) << 32;
127 value
|= (uint64_t)(*p
++) << 40;
128 value
|= (uint64_t)(*p
++) << 48;
129 value
|= (uint64_t)(*p
++) << 54;
134 pstrdup (const char *s
)
136 int len
= strlen (s
) + 1;
137 char *p
= (char *) g_malloc (len
);
142 typedef struct _CounterValue CounterValue
;
143 struct _CounterValue
{
145 unsigned char *buffer
;
149 typedef struct _Counter Counter
;
157 CounterValue
*values
;
158 CounterValue
*values_last
;
161 typedef struct _CounterList CounterList
;
162 struct _CounterList
{
167 typedef struct _CounterSection CounterSection
;
168 struct _CounterSection
{
170 CounterList
*counters
;
171 CounterList
*counters_last
;
172 CounterSection
*next
;
175 typedef struct _CounterTimestamp CounterTimestamp
;
176 struct _CounterTimestamp
{
178 CounterSection
*sections
;
179 CounterSection
*sections_last
;
180 CounterTimestamp
*next
;
183 static CounterList
*counters
= NULL
;
184 static CounterSection
*counters_sections
= NULL
;
185 static CounterTimestamp
*counters_timestamps
= NULL
;
189 COUNTERS_SORT_CATEGORY
192 static int counters_sort_mode
= COUNTERS_SORT_TIME
;
195 add_counter_to_section (Counter
*counter
)
197 CounterSection
*csection
, *s
;
200 clist
= (CounterList
*) g_calloc (1, sizeof (CounterList
));
201 clist
->counter
= counter
;
203 for (csection
= counters_sections
; csection
; csection
= csection
->next
) {
204 if (strcmp (csection
->value
, counter
->section
) == 0) {
205 /* If section exist */
206 if (!csection
->counters
)
207 csection
->counters
= clist
;
209 csection
->counters_last
->next
= clist
;
210 csection
->counters_last
= clist
;
215 /* If section does not exist */
216 csection
= (CounterSection
*) g_calloc (1, sizeof (CounterSection
));
217 csection
->value
= counter
->section
;
218 csection
->counters
= clist
;
219 csection
->counters_last
= clist
;
221 if (!counters_sections
) {
222 counters_sections
= csection
;
224 s
= counters_sections
;
232 add_counter (const char *section
, const char *name
, int type
, int unit
, int variance
, int index
)
234 CounterList
*list
, *l
;
237 for (list
= counters
; list
; list
= list
->next
)
238 if (list
->counter
->index
== index
)
241 counter
= (Counter
*) g_calloc (1, sizeof (Counter
));
242 counter
->section
= section
;
243 counter
->name
= name
;
244 counter
->type
= type
;
245 counter
->unit
= unit
;
246 counter
->variance
= variance
;
247 counter
->index
= index
;
249 list
= (CounterList
*) g_calloc (1, sizeof (CounterList
));
250 list
->counter
= counter
;
261 if (counters_sort_mode
== COUNTERS_SORT_CATEGORY
|| !verbose
)
262 add_counter_to_section (counter
);
266 add_counter_to_timestamp (uint64_t timestamp
, Counter
*counter
)
268 CounterTimestamp
*ctimestamp
, *t
;
269 CounterSection
*csection
;
272 clist
= (CounterList
*) g_calloc (1, sizeof (CounterList
));
273 clist
->counter
= counter
;
275 for (ctimestamp
= counters_timestamps
; ctimestamp
; ctimestamp
= ctimestamp
->next
) {
276 if (ctimestamp
->value
== timestamp
) {
277 for (csection
= ctimestamp
->sections
; csection
; csection
= csection
->next
) {
278 if (strcmp (csection
->value
, counter
->section
) == 0) {
279 /* if timestamp exist and section exist */
280 if (!csection
->counters
)
281 csection
->counters
= clist
;
283 csection
->counters_last
->next
= clist
;
284 csection
->counters_last
= clist
;
289 /* if timestamp exist and section does not exist */
290 csection
= (CounterSection
*) g_calloc (1, sizeof (CounterSection
));
291 csection
->value
= counter
->section
;
292 csection
->counters
= clist
;
293 csection
->counters_last
= clist
;
295 if (!ctimestamp
->sections
)
296 ctimestamp
->sections
= csection
;
298 ctimestamp
->sections_last
->next
= csection
;
299 ctimestamp
->sections_last
= csection
;
304 /* If timestamp do not exist and section does not exist */
305 csection
= (CounterSection
*) g_calloc (1, sizeof (CounterSection
));
306 csection
->value
= counter
->section
;
307 csection
->counters
= clist
;
308 csection
->counters_last
= clist
;
310 ctimestamp
= (CounterTimestamp
*) g_calloc (1, sizeof (CounterTimestamp
));
311 ctimestamp
->value
= timestamp
;
312 ctimestamp
->sections
= csection
;
313 ctimestamp
->sections_last
= csection
;
315 if (!counters_timestamps
) {
316 counters_timestamps
= ctimestamp
;
318 t
= counters_timestamps
;
321 t
->next
= ctimestamp
;
326 add_counter_value (int index
, CounterValue
*value
)
330 for (list
= counters
; list
; list
= list
->next
) {
331 if (list
->counter
->index
== index
) {
332 if (!list
->counter
->values
)
333 list
->counter
->values
= value
;
335 list
->counter
->values_last
->next
= value
;
336 list
->counter
->values_last
= value
;
338 if (counters_sort_mode
== COUNTERS_SORT_TIME
)
339 add_counter_to_timestamp (value
->timestamp
, list
->counter
);
347 section_name (int section
)
350 case MONO_COUNTER_JIT
: return "Mono JIT";
351 case MONO_COUNTER_GC
: return "Mono GC";
352 case MONO_COUNTER_METADATA
: return "Mono Metadata";
353 case MONO_COUNTER_GENERICS
: return "Mono Generics";
354 case MONO_COUNTER_SECURITY
: return "Mono Security";
355 case MONO_COUNTER_RUNTIME
: return "Mono Runtime";
356 case MONO_COUNTER_SYSTEM
: return "Mono System";
357 case MONO_COUNTER_PROFILER
: return "Mono Profiler";
358 default: return "<unknown>";
366 case MONO_COUNTER_INT
: return "Int";
367 case MONO_COUNTER_UINT
: return "UInt";
368 case MONO_COUNTER_WORD
: return "Word";
369 case MONO_COUNTER_LONG
: return "Long";
370 case MONO_COUNTER_ULONG
: return "ULong";
371 case MONO_COUNTER_DOUBLE
: return "Double";
372 case MONO_COUNTER_STRING
: return "String";
373 case MONO_COUNTER_TIME_INTERVAL
: return "Time Interval";
374 default: return "<unknown>";
382 case MONO_COUNTER_RAW
: return "Raw";
383 case MONO_COUNTER_BYTES
: return "Bytes";
384 case MONO_COUNTER_TIME
: return "Time";
385 case MONO_COUNTER_COUNT
: return "Count";
386 case MONO_COUNTER_PERCENTAGE
: return "Percentage";
387 default: return "<unknown>";
392 variance_name (int variance
)
395 case MONO_COUNTER_MONOTONIC
: return "Monotonic";
396 case MONO_COUNTER_CONSTANT
: return "Constant";
397 case MONO_COUNTER_VARIABLE
: return "Variable";
398 default: return "<unknown>";
403 dump_counters_value (Counter
*counter
, const char *key_format
, const char *key
, void *value
)
408 snprintf (format
, sizeof (format
), "%s : %%s\n", key_format
);
409 fprintf (outfile
, format
, key
, "<null>");
411 switch (counter
->type
) {
412 case MONO_COUNTER_INT
:
413 #if SIZEOF_VOID_P == 4
414 case MONO_COUNTER_WORD
:
416 snprintf (format
, sizeof (format
), "%s : %%d\n", key_format
);
417 fprintf (outfile
, format
, key
, *(int32_t*)value
);
419 case MONO_COUNTER_UINT
:
420 snprintf (format
, sizeof (format
), "%s : %%u\n", key_format
);
421 fprintf (outfile
, format
, key
, *(uint32_t*)value
);
423 case MONO_COUNTER_LONG
:
424 #if SIZEOF_VOID_P == 8
425 case MONO_COUNTER_WORD
:
427 case MONO_COUNTER_TIME_INTERVAL
:
428 if (counter
->type
== MONO_COUNTER_LONG
&& counter
->unit
== MONO_COUNTER_TIME
) {
429 snprintf (format
, sizeof (format
), "%s : %%0.3fms\n", key_format
);
430 fprintf (outfile
, format
, key
, (double)*(int64_t*)value
/ 10000.0);
431 } else if (counter
->type
== MONO_COUNTER_TIME_INTERVAL
) {
432 snprintf (format
, sizeof (format
), "%s : %%0.3fms\n", key_format
);
433 fprintf (outfile
, format
, key
, (double)*(int64_t*)value
/ 1000.0);
435 snprintf (format
, sizeof (format
), "%s : %%u\n", key_format
);
436 fprintf (outfile
, format
, key
, *(int64_t*)value
);
439 case MONO_COUNTER_ULONG
:
440 snprintf (format
, sizeof (format
), "%s : %%llu\n", key_format
);
441 fprintf (outfile
, format
, key
, *(uint64_t*)value
);
443 case MONO_COUNTER_DOUBLE
:
444 snprintf (format
, sizeof (format
), "%s : %%f\n", key_format
);
445 fprintf (outfile
, format
, key
, *(double*)value
);
447 case MONO_COUNTER_STRING
:
448 snprintf (format
, sizeof (format
), "%s : %%s\n", key_format
);
449 fprintf (outfile
, format
, key
, *(char*)value
);
459 CounterValue
*cvalue
;
460 CounterTimestamp
*ctimestamp
;
461 CounterSection
*csection
;
463 char strtimestamp
[17];
464 int i
, section_printed
;
466 fprintf (outfile
, "\nCounters:\n");
469 char counters_to_print
[][64] = {
471 "Methods JITted using mono JIT",
472 "Methods JITted using LLVM",
473 "Total time spent JITting (sec)",
481 "CPU Load Average - 1min",
482 "CPU Load Average - 5min",
483 "CPU Load Average - 15min",
487 for (csection
= counters_sections
; csection
; csection
= csection
->next
) {
490 for (clist
= csection
->counters
; clist
; clist
= clist
->next
) {
491 counter
= clist
->counter
;
492 if (!counter
->values_last
)
495 for (i
= 0; counters_to_print
[i
][0] != 0; i
++) {
496 if (strcmp (counters_to_print
[i
], counter
->name
) == 0) {
497 if (!section_printed
) {
498 fprintf (outfile
, "\t%s:\n", csection
->value
);
502 dump_counters_value (counter
, "\t\t%-30s", counter
->name
, counter
->values_last
->buffer
);
508 } else if (counters_sort_mode
== COUNTERS_SORT_TIME
) {
509 for (ctimestamp
= counters_timestamps
; ctimestamp
; ctimestamp
= ctimestamp
->next
) {
510 fprintf (outfile
, "\t%llu:%02llu:%02llu:%02llu.%03llu:\n",
511 (unsigned long long) (ctimestamp
->value
/ 1000 / 60 / 60 / 24 % 1000),
512 (unsigned long long) (ctimestamp
->value
/ 1000 / 60 / 60 % 24),
513 (unsigned long long) (ctimestamp
->value
/ 1000 / 60 % 60),
514 (unsigned long long) (ctimestamp
->value
/ 1000 % 60),
515 (unsigned long long) (ctimestamp
->value
% 1000));
517 for (csection
= ctimestamp
->sections
; csection
; csection
= csection
->next
) {
518 fprintf (outfile
, "\t\t%s:\n", csection
->value
);
520 for (clist
= csection
->counters
; clist
; clist
= clist
->next
) {
521 counter
= clist
->counter
;
522 for (cvalue
= counter
->values
; cvalue
; cvalue
= cvalue
->next
) {
523 if (cvalue
->timestamp
!= ctimestamp
->value
)
526 dump_counters_value (counter
, "\t\t\t%-30s", counter
->name
, cvalue
->buffer
);
531 } else if (counters_sort_mode
== COUNTERS_SORT_CATEGORY
) {
532 for (csection
= counters_sections
; csection
; csection
= csection
->next
) {
533 fprintf (outfile
, "\t%s:\n", csection
->value
);
535 for (clist
= csection
->counters
; clist
; clist
= clist
->next
) {
536 counter
= clist
->counter
;
537 fprintf (outfile
, "\t\t%s: [type: %s, unit: %s, variance: %s]\n",
538 counter
->name
, type_name (counter
->type
), unit_name (counter
->unit
), variance_name (counter
->variance
));
540 for (cvalue
= counter
->values
; cvalue
; cvalue
= cvalue
->next
) {
541 snprintf (strtimestamp
, sizeof (strtimestamp
), "%llu:%02llu:%02llu:%02llu.%03llu",
542 (unsigned long long) (cvalue
->timestamp
/ 1000 / 60 / 60 / 24 % 1000),
543 (unsigned long long) (cvalue
->timestamp
/ 1000 / 60 / 60 % 24),
544 (unsigned long long) (cvalue
->timestamp
/ 1000 / 60 % 60),
545 (unsigned long long) (cvalue
->timestamp
/ 1000 % 60),
546 (unsigned long long) (cvalue
->timestamp
% 1000));
548 dump_counters_value (counter
, "\t\t\t%s", strtimestamp
, cvalue
->buffer
);
555 static int num_images
;
556 typedef struct _ImageDesc ImageDesc
;
563 static ImageDesc
* image_hash
[SMALL_HASH_SIZE
] = {0};
566 add_image (intptr_t image
, char *name
)
568 int slot
= ((image
>> 2) & 0xffff) % SMALL_HASH_SIZE
;
569 ImageDesc
*cd
= (ImageDesc
*) g_malloc (sizeof (ImageDesc
));
571 cd
->filename
= pstrdup (name
);
572 cd
->next
= image_hash
[slot
];
573 image_hash
[slot
] = cd
;
577 static int num_assemblies
;
579 typedef struct _AssemblyDesc AssemblyDesc
;
580 struct _AssemblyDesc
{
586 static AssemblyDesc
* assembly_hash
[SMALL_HASH_SIZE
] = {0};
589 add_assembly (intptr_t assembly
, char *name
)
591 int slot
= ((assembly
>> 2) & 0xffff) % SMALL_HASH_SIZE
;
592 AssemblyDesc
*cd
= (AssemblyDesc
*) g_malloc (sizeof (AssemblyDesc
));
593 cd
->assembly
= assembly
;
594 cd
->asmname
= pstrdup (name
);
595 cd
->next
= assembly_hash
[slot
];
596 assembly_hash
[slot
] = cd
;
600 typedef struct _BackTrace BackTrace
;
612 typedef struct _ClassDesc ClassDesc
;
622 static ClassDesc
* class_hash
[HASH_SIZE
] = {0};
623 static int num_classes
= 0;
626 add_class (intptr_t klass
, const char *name
)
628 int slot
= ((klass
>> 2) & 0xffff) % HASH_SIZE
;
630 cd
= class_hash
[slot
];
631 while (cd
&& cd
->klass
!= klass
)
633 /* we resolved an unknown class (unless we had the code unloaded) */
635 /*printf ("resolved unknown: %s\n", name);*/
637 cd
->name
= pstrdup (name
);
640 cd
= (ClassDesc
*) g_calloc (sizeof (ClassDesc
), 1);
642 cd
->name
= pstrdup (name
);
643 cd
->next
= class_hash
[slot
];
646 cd
->traces
.count
= 0;
648 cd
->traces
.traces
= NULL
;
649 class_hash
[slot
] = cd
;
655 lookup_class (intptr_t klass
)
657 int slot
= ((klass
>> 2) & 0xffff) % HASH_SIZE
;
658 ClassDesc
*cd
= class_hash
[slot
];
659 while (cd
&& cd
->klass
!= klass
)
663 snprintf (buf
, sizeof (buf
), "unresolved class %p", (void*)klass
);
664 return add_class (klass
, buf
);
669 typedef struct _MethodDesc MethodDesc
;
678 int ignore_jit
; /* when this is set, we collect the metadata but don't count this method fot jit time and code size, when filtering events */
681 uint64_t callee_time
;
686 static MethodDesc
* method_hash
[HASH_SIZE
] = {0};
687 static int num_methods
= 0;
690 add_method (intptr_t method
, const char *name
, intptr_t code
, int len
)
692 int slot
= ((method
>> 2) & 0xffff) % HASH_SIZE
;
694 cd
= method_hash
[slot
];
695 while (cd
&& cd
->method
!= method
)
697 /* we resolved an unknown method (unless we had the code unloaded) */
701 /*printf ("resolved unknown: %s\n", name);*/
703 cd
->name
= pstrdup (name
);
706 cd
= (MethodDesc
*) g_calloc (sizeof (MethodDesc
), 1);
708 cd
->name
= pstrdup (name
);
713 cd
->traces
.count
= 0;
715 cd
->traces
.traces
= NULL
;
716 cd
->next
= method_hash
[slot
];
717 method_hash
[slot
] = cd
;
723 lookup_method (intptr_t method
)
725 int slot
= ((method
>> 2) & 0xffff) % HASH_SIZE
;
726 MethodDesc
*cd
= method_hash
[slot
];
727 while (cd
&& cd
->method
!= method
)
731 snprintf (buf
, sizeof (buf
), "unknown method %p", (void*)method
);
732 return add_method (method
, buf
, 0, 0);
737 static int num_stat_samples
= 0;
738 static int size_stat_samples
= 0;
739 uintptr_t *stat_samples
= NULL
;
740 int *stat_sample_desc
= NULL
;
743 add_stat_sample (int type
, uintptr_t ip
) {
744 if (num_stat_samples
== size_stat_samples
) {
745 size_stat_samples
*= 2;
746 if (!size_stat_samples
)
747 size_stat_samples
= 32;
748 stat_samples
= (uintptr_t *) g_realloc (stat_samples
, size_stat_samples
* sizeof (uintptr_t));
749 stat_sample_desc
= (int *) g_realloc (stat_sample_desc
, size_stat_samples
* sizeof (int));
751 stat_samples
[num_stat_samples
] = ip
;
752 stat_sample_desc
[num_stat_samples
++] = type
;
756 lookup_method_by_ip (uintptr_t ip
)
761 for (i
= 0; i
< HASH_SIZE
; ++i
) {
764 //printf ("checking %p against %p-%p\n", (void*)ip, (void*)(m->code), (void*)(m->code + m->len));
765 if (ip
>= (uintptr_t)m
->code
&& ip
< (uintptr_t)m
->code
+ m
->len
) {
775 compare_method_samples (const void *a
, const void *b
)
777 MethodDesc
*const *A
= (MethodDesc
*const *)a
;
778 MethodDesc
*const *B
= (MethodDesc
*const *)b
;
779 if ((*A
)->sample_hits
== (*B
)->sample_hits
)
781 if ((*B
)->sample_hits
< (*A
)->sample_hits
)
786 typedef struct _UnmanagedSymbol UnmanagedSymbol
;
787 struct _UnmanagedSymbol
{
788 UnmanagedSymbol
*parent
;
793 uintptr_t sample_hits
;
796 static UnmanagedSymbol
**usymbols
= NULL
;
797 static int usymbols_size
= 0;
798 static int usymbols_num
= 0;
801 compare_usymbol_addr (const void *a
, const void *b
)
803 UnmanagedSymbol
*const *A
= (UnmanagedSymbol
*const *)a
;
804 UnmanagedSymbol
*const *B
= (UnmanagedSymbol
*const *)b
;
805 if ((*B
)->addr
== (*A
)->addr
)
807 if ((*B
)->addr
> (*A
)->addr
)
813 compare_usymbol_samples (const void *a
, const void *b
)
815 UnmanagedSymbol
*const *A
= (UnmanagedSymbol
*const *)a
;
816 UnmanagedSymbol
*const *B
= (UnmanagedSymbol
*const *)b
;
817 if ((*B
)->sample_hits
== (*A
)->sample_hits
)
819 if ((*B
)->sample_hits
< (*A
)->sample_hits
)
825 add_unmanaged_symbol (uintptr_t addr
, char *name
, uintptr_t size
)
827 UnmanagedSymbol
*sym
;
828 if (usymbols_num
== usymbols_size
) {
829 int new_size
= usymbols_size
* 2;
832 usymbols
= (UnmanagedSymbol
**) g_realloc (usymbols
, sizeof (void*) * new_size
);
833 usymbols_size
= new_size
;
835 sym
= (UnmanagedSymbol
*) g_calloc (sizeof (UnmanagedSymbol
), 1);
839 usymbols
[usymbols_num
++] = sym
;
842 /* only valid after the symbols are sorted */
843 static UnmanagedSymbol
*
844 lookup_unmanaged_symbol (uintptr_t addr
)
846 int r
= usymbols_num
- 1;
848 UnmanagedSymbol
*sym
;
853 if (addr
== sym
->addr
)
855 if (addr
< sym
->addr
) {
857 } else if (addr
> sym
->addr
) {
862 if (last_best
>= 0 && (addr
- usymbols
[last_best
]->addr
) < 4096)
863 return usymbols
[last_best
];
867 /* we use the same structure for binaries */
868 static UnmanagedSymbol
**ubinaries
= NULL
;
869 static int ubinaries_size
= 0;
870 static int ubinaries_num
= 0;
873 add_unmanaged_binary (uintptr_t addr
, char *name
, uintptr_t size
)
875 UnmanagedSymbol
*sym
;
876 if (ubinaries_num
== ubinaries_size
) {
877 int new_size
= ubinaries_size
* 2;
880 ubinaries
= (UnmanagedSymbol
**) g_realloc (ubinaries
, sizeof (void*) * new_size
);
881 ubinaries_size
= new_size
;
883 sym
= (UnmanagedSymbol
*) g_calloc (sizeof (UnmanagedSymbol
), 1);
888 ubinaries
[ubinaries_num
++] = sym
;
891 static UnmanagedSymbol
*
892 lookup_unmanaged_binary (uintptr_t addr
)
895 for (i
= 0; i
< ubinaries_num
; ++i
) {
896 UnmanagedSymbol
*ubin
= ubinaries
[i
];
897 if (addr
>= ubin
->addr
&& addr
< ubin
->addr
+ ubin
->size
) {
905 sample_type_name (int type
)
908 case SAMPLE_CYCLES
: return "cycles";
909 case SAMPLE_INSTRUCTIONS
: return "instructions retired";
910 case SAMPLE_CACHE_MISSES
: return "cache misses";
911 case SAMPLE_CACHE_REFS
: return "cache references";
912 case SAMPLE_BRANCHES
: return "executed branches";
913 case SAMPLE_BRANCH_MISSES
: return "unpredicted branches";
919 set_usym_parent (UnmanagedSymbol
** cachedus
, int count
)
922 for (i
= 0; i
< count
; ++i
) {
923 UnmanagedSymbol
*ubin
= lookup_unmanaged_binary (cachedus
[i
]->addr
);
924 if (ubin
== cachedus
[i
])
926 cachedus
[i
]->parent
= ubin
;
931 print_usym (UnmanagedSymbol
* um
)
934 fprintf (outfile
, "\t%6zd %6.2f %-36s in %s\n", um
->sample_hits
, um
->sample_hits
*100.0/num_stat_samples
, um
->name
, um
->parent
->name
);
936 fprintf (outfile
, "\t%6zd %6.2f %s\n", um
->sample_hits
, um
->sample_hits
*100.0/num_stat_samples
, um
->name
);
940 sym_percent (uintptr_t sample_hits
)
945 pc
= sample_hits
*100.0/num_stat_samples
;
953 int count
= 0, msize
= 0;
954 int unmanaged_hits
= 0;
955 int unresolved_hits
= 0;
956 MethodDesc
** cachedm
= NULL
;
957 int ucount
= 0, usize
= 0;
958 UnmanagedSymbol
** cachedus
= NULL
;
959 if (!num_stat_samples
)
961 qsort (usymbols
, usymbols_num
, sizeof (UnmanagedSymbol
*), compare_usymbol_addr
);
962 for (i
= 0; i
< num_stat_samples
; ++i
) {
963 MethodDesc
*m
= lookup_method_by_ip (stat_samples
[i
]);
965 if (!m
->sample_hits
) {
966 if (count
== msize
) {
970 cachedm
= (MethodDesc
**) g_realloc (cachedm
, sizeof (void*) * msize
);
972 cachedm
[count
++] = m
;
976 UnmanagedSymbol
*usym
= lookup_unmanaged_symbol (stat_samples
[i
]);
979 //printf ("unmanaged hit at %p\n", (void*)stat_samples [i]);
980 usym
= lookup_unmanaged_binary (stat_samples
[i
]);
983 if (!usym
->sample_hits
) {
984 if (ucount
== usize
) {
988 cachedus
= (UnmanagedSymbol
**) g_realloc (cachedus
, sizeof (void*) * usize
);
990 cachedus
[ucount
++] = usym
;
997 qsort (cachedm
, count
, sizeof (MethodDesc
*), compare_method_samples
);
998 qsort (cachedus
, ucount
, sizeof (UnmanagedSymbol
*), compare_usymbol_samples
);
999 set_usym_parent (cachedus
, ucount
);
1000 fprintf (outfile
, "\nStatistical samples summary\n");
1001 fprintf (outfile
, "\tSample type: %s\n", sample_type_name (stat_sample_desc
[0]));
1002 fprintf (outfile
, "\tUnmanaged hits: %6d (%4.1f%%)\n", unmanaged_hits
, (100.0*unmanaged_hits
)/num_stat_samples
);
1003 fprintf (outfile
, "\tManaged hits: %6d (%4.1f%%)\n", num_stat_samples
- unmanaged_hits
, (100.0*(num_stat_samples
-unmanaged_hits
))/num_stat_samples
);
1004 fprintf (outfile
, "\tUnresolved hits: %6d (%4.1f%%)\n", unresolved_hits
, (100.0*unresolved_hits
)/num_stat_samples
);
1005 fprintf (outfile
, "\t%6s %6s %s\n", "Hits", "%", "Method name");
1008 while (i
< count
|| u
< ucount
) {
1010 MethodDesc
*m
= cachedm
[i
];
1012 UnmanagedSymbol
*um
= cachedus
[u
];
1013 if (um
->sample_hits
> m
->sample_hits
) {
1014 if (!sym_percent (um
->sample_hits
))
1021 if (!sym_percent (m
->sample_hits
))
1023 fprintf (outfile
, "\t%6d %6.2f %s\n", m
->sample_hits
, m
->sample_hits
*100.0/num_stat_samples
, m
->name
);
1028 UnmanagedSymbol
*um
= cachedus
[u
];
1029 if (!sym_percent (um
->sample_hits
))
1038 typedef struct _HeapClassDesc HeapClassDesc
;
1040 HeapClassDesc
*klass
;
1044 struct _HeapClassDesc
{
1048 HeapClassRevRef
*rev_hash
;
1051 uintptr_t pinned_references
;
1052 uintptr_t root_references
;
1056 add_rev_class_hashed (HeapClassRevRef
*rev_hash
, uintptr_t size
, HeapClassDesc
*hklass
, uint64_t value
)
1059 uintptr_t start_pos
;
1060 start_pos
= (hklass
->klass
->klass
>> 2) % size
;
1061 assert (start_pos
< size
);
1064 if (rev_hash
[i
].klass
== hklass
) {
1065 rev_hash
[i
].count
+= value
;
1067 } else if (!rev_hash
[i
].klass
) {
1068 rev_hash
[i
].klass
= hklass
;
1069 rev_hash
[i
].count
+= value
;
1071 for (i
= 0; i
< size
; ++i
)
1072 if (rev_hash
[i
].klass
&& rev_hash
[i
].klass
->klass
== hklass
->klass
)
1074 assert (start_pos
== 1);
1080 } while (i
!= start_pos
);
1081 /* should not happen */
1082 printf ("failed revref store\n");
1087 add_heap_class_rev (HeapClassDesc
*from
, HeapClassDesc
*to
)
1090 if (to
->rev_count
* 2 >= to
->rev_hash_size
) {
1092 uintptr_t old_size
= to
->rev_hash_size
;
1093 to
->rev_hash_size
*= 2;
1094 if (to
->rev_hash_size
== 0)
1095 to
->rev_hash_size
= 4;
1096 n
= (HeapClassRevRef
*) g_calloc (sizeof (HeapClassRevRef
) * to
->rev_hash_size
, 1);
1097 for (i
= 0; i
< old_size
; ++i
) {
1098 if (to
->rev_hash
[i
].klass
)
1099 add_rev_class_hashed (n
, to
->rev_hash_size
, to
->rev_hash
[i
].klass
, to
->rev_hash
[i
].count
);
1102 g_free (to
->rev_hash
);
1105 to
->rev_count
+= add_rev_class_hashed (to
->rev_hash
, to
->rev_hash_size
, from
, 1);
1110 HeapClassDesc
*hklass
;
1115 typedef struct _HeapShot HeapShot
;
1121 HeapClassDesc
**class_hash
;
1122 HeapClassDesc
**sorted
;
1123 HeapObjectDesc
**objects_hash
;
1124 uintptr_t objects_count
;
1125 uintptr_t objects_hash_size
;
1126 uintptr_t num_roots
;
1128 uintptr_t *roots_extra
;
1132 static HeapShot
*heap_shots
= NULL
;
1133 static int num_heap_shots
= 0;
1136 new_heap_shot (uint64_t timestamp
)
1138 HeapShot
*hs
= (HeapShot
*) g_calloc (sizeof (HeapShot
), 1);
1140 hs
->class_hash
= (HeapClassDesc
**) g_calloc (sizeof (void*), hs
->hash_size
);
1141 hs
->timestamp
= timestamp
;
1143 hs
->next
= heap_shots
;
1148 static HeapClassDesc
*
1149 heap_class_lookup (HeapShot
*hs
, ClassDesc
*klass
)
1152 unsigned int start_pos
;
1153 start_pos
= ((uintptr_t)klass
->klass
>> 2) % hs
->hash_size
;
1156 HeapClassDesc
* cd
= hs
->class_hash
[i
];
1159 if (cd
->klass
== klass
)
1162 if (++i
== hs
->hash_size
)
1164 } while (i
!= start_pos
);
1169 add_heap_hashed (HeapClassDesc
**hash
, HeapClassDesc
**retv
, uintptr_t hsize
, ClassDesc
*klass
, uint64_t size
, uint64_t count
)
1172 uintptr_t start_pos
;
1173 start_pos
= ((uintptr_t)klass
->klass
>> 2) % hsize
;
1176 if (hash
[i
] && hash
[i
]->klass
== klass
) {
1177 hash
[i
]->total_size
+= size
;
1178 hash
[i
]->count
+= count
;
1181 } else if (!hash
[i
]) {
1186 hash
[i
] = (HeapClassDesc
*) g_calloc (sizeof (HeapClassDesc
), 1);
1187 hash
[i
]->klass
= klass
;
1188 hash
[i
]->total_size
+= size
;
1189 hash
[i
]->count
+= count
;
1196 } while (i
!= start_pos
);
1197 /* should not happen */
1198 printf ("failed heap class store\n");
1202 static HeapClassDesc
*
1203 add_heap_shot_class (HeapShot
*hs
, ClassDesc
*klass
, uint64_t size
)
1207 if (hs
->class_count
* 2 >= hs
->hash_size
) {
1209 int old_size
= hs
->hash_size
;
1211 if (hs
->hash_size
== 0)
1213 n
= (HeapClassDesc
**) g_calloc (sizeof (void*) * hs
->hash_size
, 1);
1214 for (i
= 0; i
< old_size
; ++i
) {
1215 res
= hs
->class_hash
[i
];
1216 if (hs
->class_hash
[i
])
1217 add_heap_hashed (n
, &res
, hs
->hash_size
, hs
->class_hash
[i
]->klass
, hs
->class_hash
[i
]->total_size
, hs
->class_hash
[i
]->count
);
1220 g_free (hs
->class_hash
);
1224 hs
->class_count
+= add_heap_hashed (hs
->class_hash
, &res
, hs
->hash_size
, klass
, size
, 1);
1225 //if (res->count == 1)
1226 // printf ("added heap class: %s\n", res->klass->name);
1230 static HeapObjectDesc
*
1231 alloc_heap_obj (uintptr_t objaddr
, HeapClassDesc
*hklass
, uintptr_t num_refs
)
1233 HeapObjectDesc
* ho
= (HeapObjectDesc
*) g_calloc (sizeof (HeapObjectDesc
) + num_refs
* sizeof (uintptr_t), 1);
1234 ho
->objaddr
= objaddr
;
1235 ho
->hklass
= hklass
;
1236 ho
->num_refs
= num_refs
;
1241 heap_shot_find_obj_slot (HeapShot
*hs
, uintptr_t objaddr
)
1244 uintptr_t start_pos
;
1245 HeapObjectDesc
**hash
= hs
->objects_hash
;
1246 start_pos
= ((uintptr_t)objaddr
>> 3) % hs
->objects_hash_size
;
1249 if (hash
[i
] && hash
[i
]->objaddr
== objaddr
) {
1251 } else if (!hash
[i
]) {
1255 if (++i
== hs
->objects_hash_size
)
1257 } while (i
!= start_pos
);
1258 /* should not happen */
1259 //printf ("failed heap obj slot\n");
1263 static HeapObjectDesc
*
1264 heap_shot_obj_add_refs (HeapShot
*hs
, uintptr_t objaddr
, uintptr_t num
, uintptr_t *ref_offset
)
1266 HeapObjectDesc
**hash
= hs
->objects_hash
;
1267 uintptr_t i
= heap_shot_find_obj_slot (hs
, objaddr
);
1269 HeapObjectDesc
* ho
= alloc_heap_obj (objaddr
, hash
[i
]->hklass
, hash
[i
]->num_refs
+ num
);
1270 *ref_offset
= hash
[i
]->num_refs
;
1271 memcpy (ho
->refs
, hash
[i
]->refs
, hash
[i
]->num_refs
* sizeof (uintptr_t));
1276 /* should not happen */
1277 printf ("failed heap obj update\n");
1283 add_heap_hashed_obj (HeapObjectDesc
**hash
, uintptr_t hsize
, HeapObjectDesc
*obj
)
1286 uintptr_t start_pos
;
1287 start_pos
= ((uintptr_t)obj
->objaddr
>> 3) % hsize
;
1290 if (hash
[i
] && hash
[i
]->objaddr
== obj
->objaddr
) {
1291 printf ("duplicate object!\n");
1293 } else if (!hash
[i
]) {
1300 } while (i
!= start_pos
);
1301 /* should not happen */
1302 printf ("failed heap obj store\n");
1307 add_heap_shot_obj (HeapShot
*hs
, HeapObjectDesc
*obj
)
1310 if (hs
->objects_count
* 2 >= hs
->objects_hash_size
) {
1312 uintptr_t old_size
= hs
->objects_hash_size
;
1313 hs
->objects_hash_size
*= 2;
1314 if (hs
->objects_hash_size
== 0)
1315 hs
->objects_hash_size
= 4;
1316 n
= (HeapObjectDesc
**) g_calloc (sizeof (void*) * hs
->objects_hash_size
, 1);
1317 for (i
= 0; i
< old_size
; ++i
) {
1318 if (hs
->objects_hash
[i
])
1319 add_heap_hashed_obj (n
, hs
->objects_hash_size
, hs
->objects_hash
[i
]);
1321 if (hs
->objects_hash
)
1322 g_free (hs
->objects_hash
);
1323 hs
->objects_hash
= n
;
1325 hs
->objects_count
+= add_heap_hashed_obj (hs
->objects_hash
, hs
->objects_hash_size
, obj
);
1329 heap_shot_resolve_reverse_refs (HeapShot
*hs
)
1332 for (i
= 0; i
< hs
->objects_hash_size
; ++i
) {
1334 HeapObjectDesc
*ho
= hs
->objects_hash
[i
];
1337 for (r
= 0; r
< ho
->num_refs
; ++r
) {
1338 uintptr_t oi
= heap_shot_find_obj_slot (hs
, ho
->refs
[r
]);
1339 add_heap_class_rev (ho
->hklass
, hs
->objects_hash
[oi
]->hklass
);
1345 #define MARK_BLACK 2
1348 heap_shot_mark_objects (HeapShot
*hs
)
1351 unsigned char *marks
;
1352 HeapObjectDesc
*obj
, *ref
;
1354 uintptr_t num_marked
= 0, num_unmarked
;
1355 for (i
= 0; i
< hs
->num_roots
; ++i
) {
1357 oi
= heap_shot_find_obj_slot (hs
, hs
->roots
[i
]);
1361 obj
= hs
->objects_hash
[oi
];
1363 if (hs
->roots_types
[i
] & MONO_PROFILE_GC_ROOT_PINNING
)
1364 cd
->pinned_references
++;
1365 cd
->root_references
++;
1369 /* consistency checks: it seems not all the objects are walked in the heap in some cases */
1370 marks
= (unsigned char *) g_calloc (hs
->objects_hash_size
, 1);
1373 for (i
= 0; i
< hs
->num_roots
; ++i
) {
1374 oi
= heap_shot_find_obj_slot (hs
, hs
->roots
[i
]);
1376 fprintf (outfile
, "root type 0x%x for obj %p (%s) not found in heap\n", hs
->roots_types
[i
], (void*)hs
->roots
[i
], lookup_class (hs
->roots_extra
[i
])->name
);
1379 obj
= hs
->objects_hash
[oi
];
1381 marks
[oi
] = obj
->num_refs
? MARK_GRAY
: MARK_BLACK
;
1386 while (marked_some
) {
1388 for (i
= 0; i
< hs
->objects_hash_size
; ++i
) {
1389 if (marks
[i
] != MARK_GRAY
)
1391 marks
[i
] = MARK_BLACK
;
1392 obj
= hs
->objects_hash
[i
];
1393 for (r
= 0; r
< obj
->num_refs
; ++r
) {
1394 oi
= heap_shot_find_obj_slot (hs
, obj
->refs
[r
]);
1396 fprintf (outfile
, "referenced obj %p not found in heap\n", (void*)obj
->refs
[r
]);
1399 ref
= hs
->objects_hash
[oi
];
1401 marks
[oi
] = ref
->num_refs
? MARK_GRAY
: MARK_BLACK
;
1409 for (i
= 0; i
< hs
->objects_hash_size
; ++i
) {
1410 if (hs
->objects_hash
[i
] && !marks
[i
]) {
1412 fprintf (outfile
, "object %p (%s) unmarked\n", (void*)hs
->objects_hash
[i
], hs
->objects_hash
[i
]->hklass
->klass
->name
);
1415 fprintf (outfile
, "Total unmarked: %zd/%zd\n", num_unmarked
, hs
->objects_count
);
1420 heap_shot_free_objects (HeapShot
*hs
)
1423 for (i
= 0; i
< hs
->objects_hash_size
; ++i
) {
1424 HeapObjectDesc
*ho
= hs
->objects_hash
[i
];
1428 if (hs
->objects_hash
)
1429 g_free (hs
->objects_hash
);
1430 hs
->objects_hash
= NULL
;
1431 hs
->objects_hash_size
= 0;
1432 hs
->objects_count
= 0;
1441 MethodDesc
*methods
[1];
1444 static BackTrace
*backtrace_hash
[HASH_SIZE
];
1445 static BackTrace
**backtraces
= NULL
;
1446 static int num_backtraces
= 0;
1447 static int next_backtrace
= 0;
1450 hash_backtrace (int count
, MethodDesc
**methods
)
1454 for (i
= 0; i
< count
; ++i
) {
1455 hash
= (hash
<< 5) - hash
+ methods
[i
]->method
;
1461 compare_backtrace (BackTrace
*bt
, int count
, MethodDesc
**methods
)
1464 if (bt
->count
!= count
)
1466 for (i
= 0; i
< count
; ++i
)
1467 if (methods
[i
] != bt
->methods
[i
])
1473 add_backtrace (int count
, MethodDesc
**methods
)
1475 int hash
= hash_backtrace (count
, methods
);
1476 int slot
= (hash
& 0xffff) % HASH_SIZE
;
1477 BackTrace
*bt
= backtrace_hash
[slot
];
1479 if (bt
->hash
== hash
&& compare_backtrace (bt
, count
, methods
))
1483 bt
= (BackTrace
*) g_malloc (sizeof (BackTrace
) + ((count
- 1) * sizeof (void*)));
1484 bt
->next
= backtrace_hash
[slot
];
1485 backtrace_hash
[slot
] = bt
;
1486 if (next_backtrace
== num_backtraces
) {
1487 num_backtraces
*= 2;
1488 if (!num_backtraces
)
1489 num_backtraces
= 16;
1490 backtraces
= (BackTrace
**) g_realloc (backtraces
, sizeof (void*) * num_backtraces
);
1492 bt
->id
= next_backtrace
++;
1493 backtraces
[bt
->id
] = bt
;
1496 for (slot
= 0; slot
< count
; ++slot
)
1497 bt
->methods
[slot
] = methods
[slot
];
1502 typedef struct _MonitorDesc MonitorDesc
;
1503 typedef struct _ThreadContext ThreadContext
;
1504 typedef struct _DomainContext DomainContext
;
1505 typedef struct _RemCtxContext RemCtxContext
;
1509 #if defined (HAVE_SYS_ZLIB)
1523 uint64_t startup_time
;
1524 ThreadContext
*threads
;
1525 ThreadContext
*current_thread
;
1526 DomainContext
*domains
;
1527 DomainContext
*current_domain
;
1528 RemCtxContext
*remctxs
;
1529 RemCtxContext
*current_remctx
;
1532 struct _ThreadContext
{
1533 ThreadContext
*next
;
1536 /* emulated stack */
1538 uint64_t *time_stack
;
1539 uint64_t *callee_time_stack
;
1541 uint64_t contention_start
;
1542 MonitorDesc
*monitor
;
1545 HeapShot
*current_heap_shot
;
1546 uintptr_t num_roots
;
1547 uintptr_t size_roots
;
1549 uintptr_t *roots_extra
;
1551 uint64_t gc_start_times
[3];
1554 struct _DomainContext
{
1555 DomainContext
*next
;
1557 const char *friendly_name
;
1560 struct _RemCtxContext
{
1561 RemCtxContext
*next
;
1567 ensure_buffer (ProfContext
*ctx
, int size
)
1569 if (ctx
->size
< size
) {
1570 ctx
->buf
= (unsigned char *) g_realloc (ctx
->buf
, size
);
1576 load_data (ProfContext
*ctx
, int size
)
1578 ensure_buffer (ctx
, size
);
1579 #if defined (HAVE_SYS_ZLIB)
1581 int r
= gzread (ctx
->gzfile
, ctx
->buf
, size
);
1583 return size
== 0? 1: 0;
1588 int r
= fread (ctx
->buf
, size
, 1, ctx
->file
);
1590 return size
== 0? 1: 0;
1595 static ThreadContext
*
1596 get_thread (ProfContext
*ctx
, intptr_t thread_id
)
1598 ThreadContext
*thread
;
1599 if (ctx
->current_thread
&& ctx
->current_thread
->thread_id
== thread_id
)
1600 return ctx
->current_thread
;
1601 thread
= ctx
->threads
;
1603 if (thread
->thread_id
== thread_id
) {
1606 thread
= thread
->next
;
1608 thread
= (ThreadContext
*) g_calloc (sizeof (ThreadContext
), 1);
1609 thread
->next
= ctx
->threads
;
1610 ctx
->threads
= thread
;
1611 thread
->thread_id
= thread_id
;
1612 thread
->last_time
= 0;
1613 thread
->stack_id
= 0;
1614 thread
->stack_size
= 32;
1615 thread
->stack
= (MethodDesc
**) g_malloc (thread
->stack_size
* sizeof (void*));
1616 thread
->time_stack
= (uint64_t *) g_malloc (thread
->stack_size
* sizeof (uint64_t));
1617 thread
->callee_time_stack
= (uint64_t *) g_malloc (thread
->stack_size
* sizeof (uint64_t));
1621 static DomainContext
*
1622 get_domain (ProfContext
*ctx
, intptr_t domain_id
)
1624 if (ctx
->current_domain
&& ctx
->current_domain
->domain_id
== domain_id
)
1625 return ctx
->current_domain
;
1627 DomainContext
*domain
= ctx
->domains
;
1630 if (domain
->domain_id
== domain_id
)
1633 domain
= domain
->next
;
1636 domain
= (DomainContext
*) g_calloc (sizeof (DomainContext
), 1);
1637 domain
->next
= ctx
->domains
;
1638 ctx
->domains
= domain
;
1639 domain
->domain_id
= domain_id
;
1644 static RemCtxContext
*
1645 get_remctx (ProfContext
*ctx
, intptr_t remctx_id
)
1647 if (ctx
->current_remctx
&& ctx
->current_remctx
->remctx_id
== remctx_id
)
1648 return ctx
->current_remctx
;
1650 RemCtxContext
*remctx
= ctx
->remctxs
;
1653 if (remctx
->remctx_id
== remctx_id
)
1656 remctx
= remctx
->next
;
1659 remctx
= (RemCtxContext
*) g_calloc (sizeof (RemCtxContext
), 1);
1660 remctx
->next
= ctx
->remctxs
;
1661 ctx
->remctxs
= remctx
;
1662 remctx
->remctx_id
= remctx_id
;
1667 static ThreadContext
*
1668 load_thread (ProfContext
*ctx
, intptr_t thread_id
)
1670 ThreadContext
*thread
= get_thread (ctx
, thread_id
);
1671 ctx
->current_thread
= thread
;
1676 ensure_thread_stack (ThreadContext
*thread
)
1678 if (thread
->stack_id
== thread
->stack_size
) {
1679 thread
->stack_size
*= 2;
1680 thread
->stack
= (MethodDesc
**) g_realloc (thread
->stack
, thread
->stack_size
* sizeof (void*));
1681 thread
->time_stack
= (uint64_t *) g_realloc (thread
->time_stack
, thread
->stack_size
* sizeof (uint64_t));
1682 thread
->callee_time_stack
= (uint64_t *) g_realloc (thread
->callee_time_stack
, thread
->stack_size
* sizeof (uint64_t));
1687 add_trace_hashed (CallContext
*traces
, int size
, BackTrace
*bt
, uint64_t value
)
1690 unsigned int start_pos
;
1691 start_pos
= bt
->hash
% size
;
1694 if (traces
[i
].bt
== bt
) {
1695 traces
[i
].count
+= value
;
1697 } else if (!traces
[i
].bt
) {
1699 traces
[i
].count
+= value
;
1705 } while (i
!= start_pos
);
1706 /* should not happen */
1707 printf ("failed trace store\n");
1712 add_trace_bt (BackTrace
*bt
, TraceDesc
*trace
, uint64_t value
)
1715 if (!collect_traces
)
1717 if (trace
->count
* 2 >= trace
->size
) {
1719 int old_size
= trace
->size
;
1721 if (trace
->size
== 0)
1723 n
= (CallContext
*) g_calloc (sizeof (CallContext
) * trace
->size
, 1);
1724 for (i
= 0; i
< old_size
; ++i
) {
1725 if (trace
->traces
[i
].bt
)
1726 add_trace_hashed (n
, trace
->size
, trace
->traces
[i
].bt
, trace
->traces
[i
].count
);
1729 g_free (trace
->traces
);
1732 trace
->count
+= add_trace_hashed (trace
->traces
, trace
->size
, bt
, value
);
1736 add_trace_thread (ThreadContext
*thread
, TraceDesc
*trace
, uint64_t value
)
1739 int count
= thread
->stack_id
;
1740 if (!collect_traces
)
1742 if (count
> trace_max
)
1744 bt
= add_backtrace (count
, thread
->stack
+ thread
->stack_id
- count
);
1745 add_trace_bt (bt
, trace
, value
);
1750 add_trace_methods (MethodDesc
**methods
, int count
, TraceDesc
*trace
, uint64_t value
)
1753 if (!collect_traces
)
1755 if (count
> trace_max
)
1757 bt
= add_backtrace (count
, methods
);
1758 add_trace_bt (bt
, trace
, value
);
1763 thread_add_root (ThreadContext
*ctx
, uintptr_t obj
, int root_type
, uintptr_t extra_info
)
1765 if (ctx
->num_roots
== ctx
->size_roots
) {
1766 int new_size
= ctx
->size_roots
* 2;
1769 ctx
->roots
= (uintptr_t *) g_realloc (ctx
->roots
, new_size
* sizeof (uintptr_t));
1770 ctx
->roots_extra
= (uintptr_t *) g_realloc (ctx
->roots_extra
, new_size
* sizeof (uintptr_t));
1771 ctx
->roots_types
= (int *) g_realloc (ctx
->roots_types
, new_size
* sizeof (int));
1772 ctx
->size_roots
= new_size
;
1774 ctx
->roots_types
[ctx
->num_roots
] = root_type
;
1775 ctx
->roots_extra
[ctx
->num_roots
] = extra_info
;
1776 ctx
->roots
[ctx
->num_roots
++] = obj
;
1780 compare_callc (const void *a
, const void *b
)
1782 const CallContext
*A
= (const CallContext
*)a
;
1783 const CallContext
*B
= (const CallContext
*)b
;
1784 if (B
->count
== A
->count
)
1786 if (B
->count
< A
->count
)
1792 sort_context_array (TraceDesc
* traces
)
1795 for (i
= 0, j
= 0; i
< traces
->size
; ++i
) {
1796 if (traces
->traces
[i
].bt
) {
1797 traces
->traces
[j
].bt
= traces
->traces
[i
].bt
;
1798 traces
->traces
[j
].count
= traces
->traces
[i
].count
;
1802 qsort (traces
->traces
, traces
->count
, sizeof (CallContext
), compare_callc
);
1806 push_method (ThreadContext
*thread
, MethodDesc
*method
, uint64_t timestamp
)
1808 ensure_thread_stack (thread
);
1809 thread
->time_stack
[thread
->stack_id
] = timestamp
;
1810 thread
->callee_time_stack
[thread
->stack_id
] = 0;
1811 thread
->stack
[thread
->stack_id
++] = method
;
1812 method
->recurse_count
++;
1816 pop_method (ThreadContext
*thread
, MethodDesc
*method
, uint64_t timestamp
)
1818 method
->recurse_count
--;
1819 if (thread
->stack_id
> 0 && thread
->stack
[thread
->stack_id
- 1] == method
) {
1823 if (timestamp
< thread
->time_stack
[thread
->stack_id
])
1824 fprintf (outfile
, "time went backwards for %s\n", method
->name
);
1825 tdiff
= timestamp
- thread
->time_stack
[thread
->stack_id
];
1826 if (thread
->callee_time_stack
[thread
->stack_id
] > tdiff
)
1827 fprintf (outfile
, "callee time bigger for %s\n", method
->name
);
1828 method
->self_time
+= tdiff
- thread
->callee_time_stack
[thread
->stack_id
];
1829 method
->callee_time
+= thread
->callee_time_stack
[thread
->stack_id
];
1830 if (thread
->stack_id
)
1831 thread
->callee_time_stack
[thread
->stack_id
- 1] += tdiff
;
1832 //fprintf (outfile, "method %s took %d\n", method->name, (int)(tdiff/1000));
1834 fprintf (outfile
, "unmatched leave at stack pos: %d for method %s\n", thread
->stack_id
, method
->name
);
1839 uint64_t total_time
;
1843 static GCDesc gc_info
[3];
1844 static uint64_t max_heap_size
;
1845 static uint64_t gc_object_moves
;
1846 static int gc_resizes
;
1853 TraceDesc destroy_traces
;
1855 static HandleInfo handle_info
[4];
1858 gc_event_name (int ev
)
1861 case MONO_GC_EVENT_START
: return "start";
1862 case MONO_GC_EVENT_MARK_START
: return "mark start";
1863 case MONO_GC_EVENT_MARK_END
: return "mark end";
1864 case MONO_GC_EVENT_RECLAIM_START
: return "reclaim start";
1865 case MONO_GC_EVENT_RECLAIM_END
: return "reclaim end";
1866 case MONO_GC_EVENT_END
: return "end";
1867 case MONO_GC_EVENT_PRE_STOP_WORLD
: return "pre stop";
1868 case MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED
: return "pre stop lock";
1869 case MONO_GC_EVENT_POST_STOP_WORLD
: return "post stop";
1870 case MONO_GC_EVENT_PRE_START_WORLD
: return "pre start";
1871 case MONO_GC_EVENT_POST_START_WORLD
: return "post start";
1872 case MONO_GC_EVENT_POST_START_WORLD_UNLOCKED
: return "post start unlock";
1879 sync_point_name (int type
)
1882 case SYNC_POINT_PERIODIC
: return "periodic";
1883 case SYNC_POINT_WORLD_STOP
: return "world stop";
1884 case SYNC_POINT_WORLD_START
: return "world start";
1890 static uint64_t clause_summary
[MONO_EXCEPTION_CLAUSE_FAULT
+ 1];
1891 static uint64_t throw_count
= 0;
1892 static TraceDesc exc_traces
;
1895 clause_name (int type
)
1898 case MONO_EXCEPTION_CLAUSE_NONE
: return "catch";
1899 case MONO_EXCEPTION_CLAUSE_FILTER
: return "filter";
1900 case MONO_EXCEPTION_CLAUSE_FINALLY
: return "finally";
1901 case MONO_EXCEPTION_CLAUSE_FAULT
: return "fault";
1902 default: return "invalid";
1906 static uint64_t monitor_contention
;
1907 static uint64_t monitor_failed
;
1908 static uint64_t monitor_acquired
;
1910 struct _MonitorDesc
{
1913 uintptr_t contentions
;
1915 uint64_t max_wait_time
;
1919 static MonitorDesc
* monitor_hash
[SMALL_HASH_SIZE
] = {0};
1920 static int num_monitors
= 0;
1923 lookup_monitor (uintptr_t objid
)
1925 int slot
= ((objid
>> 3) & 0xffff) % SMALL_HASH_SIZE
;
1926 MonitorDesc
*cd
= monitor_hash
[slot
];
1927 while (cd
&& cd
->objid
!= objid
)
1930 cd
= (MonitorDesc
*) g_calloc (sizeof (MonitorDesc
), 1);
1932 cd
->next
= monitor_hash
[slot
];
1933 monitor_hash
[slot
] = cd
;
1940 monitor_ev_name (int ev
)
1943 case MONO_PROFILER_MONITOR_CONTENTION
: return "contended";
1944 case MONO_PROFILER_MONITOR_DONE
: return "acquired";
1945 case MONO_PROFILER_MONITOR_FAIL
: return "not taken";
1946 default: return "invalid";
1951 get_handle_name (int htype
)
1954 case 0: return "weak";
1955 case 1: return "weaktrack";
1956 case 2: return "normal";
1957 case 3: return "pinned";
1958 default: return "unknown";
1963 get_root_name (int rtype
)
1965 switch (rtype
& MONO_PROFILE_GC_ROOT_TYPEMASK
) {
1966 case MONO_PROFILE_GC_ROOT_STACK
: return "stack";
1967 case MONO_PROFILE_GC_ROOT_FINALIZER
: return "finalizer";
1968 case MONO_PROFILE_GC_ROOT_HANDLE
: return "handle";
1969 case MONO_PROFILE_GC_ROOT_OTHER
: return "other";
1970 case MONO_PROFILE_GC_ROOT_MISC
: return "misc";
1971 default: return "unknown";
1976 decode_uleb128 (uint8_t *buf
, uint8_t **endbuf
)
1983 res
|= (((uint64_t) (b
& 0x7f)) << shift
);
1997 decode_sleb128 (uint8_t *buf
, uint8_t **endbuf
)
2007 res
= res
| (((intptr_t) (b
& 0x7f)) << shift
);
2011 if (shift
< sizeof (intptr_t) * 8 && (b
& 0x40))
2012 res
|= - ((intptr_t) 1 << shift
);
2024 decode_bt (ProfContext
*ctx
, MethodDesc
** sframes
, int *size
, unsigned char *p
, unsigned char **endp
, intptr_t ptr_base
, intptr_t *method_base
)
2026 MethodDesc
**frames
;
2028 if (ctx
->data_version
< 13)
2029 decode_uleb128 (p
, &p
); /* flags */
2030 int count
= decode_uleb128 (p
, &p
);
2032 frames
= (MethodDesc
**) g_malloc (count
* sizeof (void*));
2035 for (i
= 0; i
< count
; ++i
) {
2036 intptr_t ptrdiff
= decode_sleb128 (p
, &p
);
2037 if (ctx
->data_version
> 12) {
2038 *method_base
+= ptrdiff
;
2039 frames
[i
] = lookup_method (*method_base
);
2041 frames
[i
] = lookup_method (ptr_base
+ ptrdiff
);
2050 tracked_creation (uintptr_t obj
, ClassDesc
*cd
, uint64_t size
, BackTrace
*bt
, uint64_t timestamp
)
2053 for (i
= 0; i
< num_tracked_objects
; ++i
) {
2054 if (tracked_objects
[i
] != obj
)
2056 fprintf (outfile
, "Object %p created (%s, %llu bytes) at %.3f secs.\n", (void*)obj
, cd
->name
, (unsigned long long) size
, (timestamp
- startup_time
)/1000000000.0);
2057 if (bt
&& bt
->count
) {
2059 for (k
= 0; k
< bt
->count
; ++k
)
2060 fprintf (outfile
, "\t%s\n", bt
->methods
[k
]->name
);
2066 track_handle (uintptr_t obj
, int htype
, uint32_t handle
, BackTrace
*bt
, uint64_t timestamp
)
2069 for (i
= 0; i
< num_tracked_objects
; ++i
) {
2070 if (tracked_objects
[i
] != obj
)
2072 fprintf (outfile
, "Object %p referenced from handle %u at %.3f secs.\n", (void*)obj
, handle
, (timestamp
- startup_time
) / 1000000000.0);
2073 if (bt
&& bt
->count
) {
2075 for (k
= 0; k
< bt
->count
; ++k
)
2076 fprintf (outfile
, "\t%s\n", bt
->methods
[k
]->name
);
2082 track_move (uintptr_t src
, uintptr_t dst
)
2085 for (i
= 0; i
< num_tracked_objects
; ++i
) {
2086 if (tracked_objects
[i
] == src
)
2087 fprintf (outfile
, "Object %p moved to %p\n", (void*)src
, (void*)dst
);
2088 else if (tracked_objects
[i
] == dst
)
2089 fprintf (outfile
, "Object %p moved from %p\n", (void*)dst
, (void*)src
);
2094 track_obj_reference (uintptr_t obj
, uintptr_t parent
, ClassDesc
*cd
)
2097 for (i
= 0; i
< num_tracked_objects
; ++i
) {
2098 if (tracked_objects
[i
] == obj
)
2099 fprintf (outfile
, "Object %p referenced from %p (%s).\n", (void*)obj
, (void*)parent
, cd
->name
);
2104 found_object (uintptr_t obj
)
2106 num_tracked_objects
++;
2107 tracked_objects
= (uintptr_t *) g_realloc (tracked_objects
, num_tracked_objects
* sizeof (tracked_objects
[0]));
2108 tracked_objects
[num_tracked_objects
- 1] = obj
;
2111 static int num_jit_helpers
= 0;
2112 static int jit_helpers_code_size
= 0;
2115 code_buffer_desc (int type
)
2118 case MONO_PROFILER_CODE_BUFFER_METHOD
:
2120 case MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE
:
2121 return "method trampoline";
2122 case MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE
:
2123 return "unbox trampoline";
2124 case MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE
:
2125 return "imt trampoline";
2126 case MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE
:
2127 return "generics trampoline";
2128 case MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
:
2129 return "specific trampoline";
2130 case MONO_PROFILER_CODE_BUFFER_HELPER
:
2131 return "misc helper";
2132 case MONO_PROFILER_CODE_BUFFER_MONITOR
:
2133 return "monitor/lock";
2134 case MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE
:
2135 return "delegate invoke";
2136 case MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING
:
2137 return "exception handling";
2139 return "unspecified";
2143 typedef struct _CoverageAssembly CoverageAssembly
;
2144 struct _CoverageAssembly
{
2148 int number_of_methods
;
2150 int partially_covered
;
2153 typedef struct _CoverageClass CoverageClass
;
2154 struct _CoverageClass
{
2155 char *assembly_name
;
2157 int number_of_methods
;
2159 int partially_covered
;
2162 typedef struct _CoverageCoverage CoverageCoverage
;
2163 struct _CoverageCoverage
{
2171 typedef struct _CoverageMethod CoverageMethod
;
2172 struct _CoverageMethod
{
2173 char *assembly_name
;
2176 char *method_signature
;
2181 GPtrArray
*coverage
;
2183 static GPtrArray
*coverage_assemblies
= NULL
;
2184 static GPtrArray
*coverage_methods
= NULL
;
2185 static GPtrArray
*coverage_statements
= NULL
;
2186 static GHashTable
*coverage_methods_hash
= NULL
;
2187 static GPtrArray
*coverage_classes
= NULL
;
2188 static GHashTable
*coverage_assembly_classes
= NULL
;
2191 gather_coverage_statements (void)
2193 for (guint i
= 0; i
< coverage_statements
->len
; i
++) {
2194 CoverageCoverage
*coverage
= (CoverageCoverage
*)coverage_statements
->pdata
[i
];
2195 CoverageMethod
*method
= (CoverageMethod
*)g_hash_table_lookup (coverage_methods_hash
, GINT_TO_POINTER (coverage
->method_id
));
2196 if (method
== NULL
) {
2197 fprintf (outfile
, "Cannot find method with ID: %d\n", coverage
->method_id
);
2201 g_ptr_array_add (method
->coverage
, coverage
);
2206 coverage_add_assembly (CoverageAssembly
*assembly
)
2208 if (coverage_assemblies
== NULL
)
2209 coverage_assemblies
= g_ptr_array_new ();
2211 g_ptr_array_add (coverage_assemblies
, assembly
);
2215 coverage_add_method (CoverageMethod
*method
)
2217 if (coverage_methods
== NULL
) {
2218 coverage_methods
= g_ptr_array_new ();
2219 coverage_methods_hash
= g_hash_table_new (NULL
, NULL
);
2222 g_ptr_array_add (coverage_methods
, method
);
2223 g_hash_table_insert (coverage_methods_hash
, GINT_TO_POINTER (method
->method_id
), method
);
2227 coverage_add_class (CoverageClass
*klass
)
2229 GPtrArray
*classes
= NULL
;
2231 if (coverage_classes
== NULL
) {
2232 coverage_classes
= g_ptr_array_new ();
2233 coverage_assembly_classes
= g_hash_table_new (g_str_hash
, g_str_equal
);
2236 g_ptr_array_add (coverage_classes
, klass
);
2237 classes
= (GPtrArray
*)g_hash_table_lookup (coverage_assembly_classes
, klass
->assembly_name
);
2238 if (classes
== NULL
) {
2239 classes
= g_ptr_array_new ();
2240 g_hash_table_insert (coverage_assembly_classes
, klass
->assembly_name
, classes
);
2242 g_ptr_array_add (classes
, klass
);
2246 coverage_add_coverage (CoverageCoverage
*coverage
)
2248 if (coverage_statements
== NULL
)
2249 coverage_statements
= g_ptr_array_new ();
2251 g_ptr_array_add (coverage_statements
, coverage
);
2254 #define OBJ_ADDR(diff) ((obj_base + diff) << 3)
2255 #define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
2259 #define BUFFER_HEADER_SIZE 48
2262 int count
, min_size
, max_size
, bytes
;
2265 static int buffer_count
;
2266 static EventStat stats
[256];
2269 record_event_stats (int type
, int size
)
2271 ++stats
[type
].count
;
2272 if (!stats
[type
].min_size
)
2273 stats
[type
].min_size
= size
;
2274 stats
[type
].min_size
= MIN (stats
[type
].min_size
, size
);
2275 stats
[type
].max_size
= MAX (stats
[type
].max_size
, size
);
2276 stats
[type
].bytes
+= size
;
2280 decode_buffer (ProfContext
*ctx
)
2287 intptr_t method_base
;
2289 uint64_t file_offset
;
2291 ThreadContext
*thread
;
2293 #ifdef HAVE_SYS_ZLIB
2295 file_offset
= gztell (ctx
->gzfile
);
2298 file_offset
= ftell (ctx
->file
);
2299 if (!load_data (ctx
, 48))
2302 if (read_int32 (p
) != BUF_ID
) {
2303 fprintf (outfile
, "Incorrect buffer id: 0x%x\n", read_int32 (p
));
2304 for (i
= 0; i
< 48; ++i
) {
2305 fprintf (outfile
, "0x%x%s", p
[i
], i
% 8?" ":"\n");
2309 len
= read_int32 (p
+ 4);
2310 time_base
= read_int64 (p
+ 8);
2311 ptr_base
= read_int64 (p
+ 16);
2312 obj_base
= read_int64 (p
+ 24);
2313 thread_id
= read_int64 (p
+ 32);
2314 method_base
= read_int64 (p
+ 40);
2316 fprintf (outfile
, "buf: thread:%zx, len: %d, time: %llu, file offset: %llu\n", thread_id
, len
, (unsigned long long) time_base
, (unsigned long long) file_offset
);
2317 thread
= load_thread (ctx
, thread_id
);
2318 if (!load_data (ctx
, len
))
2323 if (!startup_time
) {
2324 startup_time
= time_base
;
2325 if (use_time_filter
) {
2326 time_from
+= startup_time
;
2327 time_to
+= startup_time
;
2330 for (i
= 0; i
< thread
->stack_id
; ++i
)
2331 thread
->stack
[i
]->recurse_count
++;
2335 unsigned char *start
= p
;
2336 unsigned char event
= *p
;
2339 int subtype
= *p
& 0xf0;
2340 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2341 LOG_TIME (time_base
, tdiff
);
2343 if (subtype
== TYPE_GC_RESIZE
) {
2344 uint64_t new_size
= decode_uleb128 (p
, &p
);
2346 fprintf (outfile
, "gc heap resized to %llu\n", (unsigned long long) new_size
);
2348 if (new_size
> max_heap_size
)
2349 max_heap_size
= new_size
;
2350 } else if (subtype
== TYPE_GC_EVENT
) {
2352 if (ctx
->data_version
> 12)
2355 ev
= decode_uleb128 (p
, &p
);
2357 if (ctx
->data_version
> 12)
2360 gen
= decode_uleb128 (p
, &p
);
2362 fprintf (outfile
, "gc event for gen%d: %s at %llu (thread: 0x%zx)\n", gen
, gc_event_name (ev
), (unsigned long long) time_base
, thread
->thread_id
);
2364 fprintf (outfile
, "incorrect gc gen: %d\n", gen
);
2367 if (ev
== MONO_GC_EVENT_START
) {
2368 thread
->gc_start_times
[gen
] = time_base
;
2369 gc_info
[gen
].count
++;
2370 } else if (ev
== MONO_GC_EVENT_END
) {
2371 tdiff
= time_base
- thread
->gc_start_times
[gen
];
2372 gc_info
[gen
].total_time
+= tdiff
;
2373 if (tdiff
> gc_info
[gen
].max_time
)
2374 gc_info
[gen
].max_time
= tdiff
;
2376 } else if (subtype
== TYPE_GC_MOVE
) {
2377 int j
, num
= decode_uleb128 (p
, &p
);
2378 gc_object_moves
+= num
/ 2;
2379 for (j
= 0; j
< num
; j
+= 2) {
2380 intptr_t obj1diff
= decode_sleb128 (p
, &p
);
2381 intptr_t obj2diff
= decode_sleb128 (p
, &p
);
2382 if (num_tracked_objects
)
2383 track_move (OBJ_ADDR (obj1diff
), OBJ_ADDR (obj2diff
));
2385 fprintf (outfile
, "moved obj %p to %p\n", (void*)OBJ_ADDR (obj1diff
), (void*)OBJ_ADDR (obj2diff
));
2388 } else if (subtype
== TYPE_GC_HANDLE_CREATED
|| subtype
== TYPE_GC_HANDLE_CREATED_BT
) {
2389 int has_bt
= subtype
== TYPE_GC_HANDLE_CREATED_BT
;
2391 MethodDesc
*sframes
[8];
2392 MethodDesc
**frames
= sframes
;
2393 int htype
= decode_uleb128 (p
, &p
);
2394 uint32_t handle
= decode_uleb128 (p
, &p
);
2395 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2398 frames
= decode_bt (ctx
, sframes
, &num_bt
, p
, &p
, ptr_base
, &method_base
);
2400 fprintf (outfile
, "Cannot load backtrace\n");
2406 if ((thread_filter
&& thread_filter
== thread
->thread_id
) || (time_base
>= time_from
&& time_base
< time_to
)) {
2407 handle_info
[htype
].created
++;
2408 handle_info
[htype
].live
++;
2409 if (handle_info
[htype
].live
> handle_info
[htype
].max_live
)
2410 handle_info
[htype
].max_live
= handle_info
[htype
].live
;
2413 bt
= add_trace_methods (frames
, num_bt
, &handle_info
[htype
].traces
, 1);
2415 bt
= add_trace_thread (thread
, &handle_info
[htype
].traces
, 1);
2416 if (num_tracked_objects
)
2417 track_handle (OBJ_ADDR (objdiff
), htype
, handle
, bt
, time_base
);
2420 fprintf (outfile
, "handle (%s) %u created for object %p\n", get_handle_name (htype
), handle
, (void*)OBJ_ADDR (objdiff
));
2421 if (frames
!= sframes
)
2423 } else if (subtype
== TYPE_GC_HANDLE_DESTROYED
|| subtype
== TYPE_GC_HANDLE_DESTROYED_BT
) {
2424 int has_bt
= subtype
== TYPE_GC_HANDLE_DESTROYED_BT
;
2426 MethodDesc
*sframes
[8];
2427 MethodDesc
**frames
= sframes
;
2428 int htype
= decode_uleb128 (p
, &p
);
2429 uint32_t handle
= decode_uleb128 (p
, &p
);
2432 frames
= decode_bt (ctx
, sframes
, &num_bt
, p
, &p
, ptr_base
, &method_base
);
2434 fprintf (outfile
, "Cannot load backtrace\n");
2440 if ((thread_filter
&& thread_filter
== thread
->thread_id
) || (time_base
>= time_from
&& time_base
< time_to
)) {
2441 handle_info
[htype
].destroyed
++;
2442 handle_info
[htype
].live
--;
2445 bt
= add_trace_methods (frames
, num_bt
, &handle_info
[htype
].destroy_traces
, 1);
2447 bt
= add_trace_thread (thread
, &handle_info
[htype
].destroy_traces
, 1);
2448 /* TODO: track_handle_free () - would need to record and keep track of the associated object address... */
2451 fprintf (outfile
, "handle (%s) %u destroyed\n", get_handle_name (htype
), handle
);
2452 if (frames
!= sframes
)
2454 } else if (subtype
== TYPE_GC_FINALIZE_START
) {
2455 // TODO: Generate a finalizer report based on these events.
2457 fprintf (outfile
, "gc finalizer queue being processed at %llu\n", (unsigned long long) time_base
);
2458 } else if (subtype
== TYPE_GC_FINALIZE_END
) {
2460 fprintf (outfile
, "gc finalizer queue finished processing at %llu\n", (unsigned long long) time_base
);
2461 } else if (subtype
== TYPE_GC_FINALIZE_OBJECT_START
) {
2462 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2464 fprintf (outfile
, "gc finalizing object %p at %llu\n", (void *) OBJ_ADDR (objdiff
), (unsigned long long) time_base
);
2465 } else if (subtype
== TYPE_GC_FINALIZE_OBJECT_END
) {
2466 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2468 fprintf (outfile
, "gc finalized object %p at %llu\n", (void *) OBJ_ADDR (objdiff
), (unsigned long long) time_base
);
2472 case TYPE_METADATA
: {
2473 int subtype
= *p
& 0xf0;
2474 const char *load_str
= subtype
== TYPE_END_LOAD
? "loaded" : "unloaded";
2475 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2477 intptr_t ptrdiff
= decode_sleb128 (p
, &p
);
2478 LOG_TIME (time_base
, tdiff
);
2480 if (mtype
== TYPE_CLASS
) {
2481 intptr_t imptrdiff
= decode_sleb128 (p
, &p
);
2482 if (ctx
->data_version
< 13)
2483 decode_uleb128 (p
, &p
); /* flags */
2485 fprintf (outfile
, "%s class %p (%s in %p) at %llu\n", load_str
, (void*)(ptr_base
+ ptrdiff
), p
, (void*)(ptr_base
+ imptrdiff
), (unsigned long long) time_base
);
2486 if (subtype
== TYPE_END_LOAD
)
2487 add_class (ptr_base
+ ptrdiff
, (char*)p
);
2490 } else if (mtype
== TYPE_IMAGE
) {
2491 if (ctx
->data_version
< 13)
2492 decode_uleb128 (p
, &p
); /* flags */
2494 fprintf (outfile
, "%s image %p (%s) at %llu\n", load_str
, (void*)(ptr_base
+ ptrdiff
), p
, (unsigned long long) time_base
);
2495 if (subtype
== TYPE_END_LOAD
)
2496 add_image (ptr_base
+ ptrdiff
, (char*)p
);
2499 } else if (mtype
== TYPE_ASSEMBLY
) {
2500 if (ctx
->data_version
< 13)
2501 decode_uleb128 (p
, &p
); /* flags */
2503 fprintf (outfile
, "%s assembly %p (%s) at %llu\n", load_str
, (void*)(ptr_base
+ ptrdiff
), p
, (unsigned long long) time_base
);
2504 if (subtype
== TYPE_END_LOAD
)
2505 add_assembly (ptr_base
+ ptrdiff
, (char*)p
);
2508 } else if (mtype
== TYPE_DOMAIN
) {
2509 if (ctx
->data_version
< 13)
2510 decode_uleb128 (p
, &p
); /* flags */
2511 DomainContext
*nd
= get_domain (ctx
, ptr_base
+ ptrdiff
);
2512 /* no subtype means it's a name event, rather than start/stop */
2514 nd
->friendly_name
= pstrdup ((char *) p
);
2517 fprintf (outfile
, "domain %p named at %llu: %s\n", (void *) (ptr_base
+ ptrdiff
), (unsigned long long) time_base
, p
);
2519 fprintf (outfile
, "%s thread %p at %llu\n", load_str
, (void *) (ptr_base
+ ptrdiff
), (unsigned long long) time_base
);
2525 } else if (mtype
== TYPE_CONTEXT
) {
2526 if (ctx
->data_version
< 13)
2527 decode_uleb128 (p
, &p
); /* flags */
2528 intptr_t domaindiff
= decode_sleb128 (p
, &p
);
2530 fprintf (outfile
, "%s context %p (%p) at %llu\n", load_str
, (void*)(ptr_base
+ ptrdiff
), (void *) (ptr_base
+ domaindiff
), (unsigned long long) time_base
);
2531 if (subtype
== TYPE_END_LOAD
)
2532 get_remctx (ctx
, ptr_base
+ ptrdiff
)->domain_id
= ptr_base
+ domaindiff
;
2533 } else if (mtype
== TYPE_THREAD
) {
2534 if (ctx
->data_version
< 13)
2535 decode_uleb128 (p
, &p
); /* flags */
2536 ThreadContext
*nt
= get_thread (ctx
, ptr_base
+ ptrdiff
);
2537 /* no subtype means it's a name event, rather than start/stop */
2539 nt
->name
= pstrdup ((char*)p
);
2542 fprintf (outfile
, "thread %p named at %llu: %s\n", (void*)(ptr_base
+ ptrdiff
), (unsigned long long) time_base
, p
);
2544 fprintf (outfile
, "%s thread %p at %llu\n", load_str
, (void *) (ptr_base
+ ptrdiff
), (unsigned long long) time_base
);
2554 int has_bt
= *p
& TYPE_ALLOC_BT
;
2555 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2556 intptr_t ptrdiff
= decode_sleb128 (p
, &p
);
2557 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2560 MethodDesc
* sframes
[8];
2561 MethodDesc
** frames
= sframes
;
2562 ClassDesc
*cd
= lookup_class (ptr_base
+ ptrdiff
);
2563 len
= decode_uleb128 (p
, &p
);
2564 LOG_TIME (time_base
, tdiff
);
2567 fprintf (outfile
, "alloced object %p, size %llu (%s) at %llu\n", (void*)OBJ_ADDR (objdiff
), (unsigned long long) len
, lookup_class (ptr_base
+ ptrdiff
)->name
, (unsigned long long) time_base
);
2570 frames
= decode_bt (ctx
, sframes
, &num_bt
, p
, &p
, ptr_base
, &method_base
);
2572 fprintf (outfile
, "Cannot load backtrace\n");
2576 if ((thread_filter
&& thread_filter
== thread
->thread_id
) || (time_base
>= time_from
&& time_base
< time_to
)) {
2579 cd
->alloc_size
+= len
;
2581 bt
= add_trace_methods (frames
, num_bt
, &cd
->traces
, len
);
2583 bt
= add_trace_thread (thread
, &cd
->traces
, len
);
2584 if (find_size
&& len
>= find_size
) {
2585 if (!find_name
|| strstr (cd
->name
, find_name
))
2586 found_object (OBJ_ADDR (objdiff
));
2587 } else if (!find_size
&& find_name
&& strstr (cd
->name
, find_name
)) {
2588 found_object (OBJ_ADDR (objdiff
));
2590 if (num_tracked_objects
)
2591 tracked_creation (OBJ_ADDR (objdiff
), cd
, len
, bt
, time_base
);
2593 if (frames
!= sframes
)
2598 int subtype
= *p
& 0xf0;
2599 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2600 int64_t ptrdiff
= decode_sleb128 (p
, &p
);
2601 LOG_TIME (time_base
, tdiff
);
2603 method_base
+= ptrdiff
;
2604 if (subtype
== TYPE_JIT
) {
2605 intptr_t codediff
= decode_sleb128 (p
, &p
);
2606 int codelen
= decode_uleb128 (p
, &p
);
2607 MethodDesc
*jitted_method
;
2609 fprintf (outfile
, "jitted method %p (%s), size: %d, code: %p\n", (void*)(method_base
), p
, codelen
, (void*)(ptr_base
+ codediff
));
2610 jitted_method
= add_method (method_base
, (char*)p
, ptr_base
+ codediff
, codelen
);
2611 if (!(time_base
>= time_from
&& time_base
< time_to
))
2612 jitted_method
->ignore_jit
= 1;
2617 if ((thread_filter
&& thread_filter
!= thread
->thread_id
))
2619 if (!(time_base
>= time_from
&& time_base
< time_to
))
2621 method
= lookup_method (method_base
);
2622 if (subtype
== TYPE_ENTER
) {
2623 add_trace_thread (thread
, &method
->traces
, 1);
2624 push_method (thread
, method
, time_base
);
2626 pop_method (thread
, method
, time_base
);
2629 fprintf (outfile
, "%s method %s\n", subtype
== TYPE_ENTER
? "enter": subtype
== TYPE_EXC_LEAVE
? "exleave": "leave", method
->name
);
2634 int subtype
= *p
& 0xf0;
2635 if (subtype
== TYPE_HEAP_OBJECT
) {
2636 HeapObjectDesc
*ho
= NULL
;
2639 if (ctx
->data_version
> 12) {
2640 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2641 LOG_TIME (time_base
, tdiff
);
2643 objdiff
= decode_sleb128 (p
, &p
);
2645 objdiff
= decode_sleb128 (p
+ 1, &p
);
2646 intptr_t ptrdiff
= decode_sleb128 (p
, &p
);
2647 uint64_t size
= decode_uleb128 (p
, &p
);
2648 uintptr_t num
= decode_uleb128 (p
, &p
);
2649 uintptr_t ref_offset
= 0;
2650 uintptr_t last_obj_offset
= 0;
2651 ClassDesc
*cd
= lookup_class (ptr_base
+ ptrdiff
);
2653 HeapClassDesc
*hcd
= add_heap_shot_class (thread
->current_heap_shot
, cd
, size
);
2654 if (collect_traces
) {
2655 ho
= alloc_heap_obj (OBJ_ADDR (objdiff
), hcd
, num
);
2656 add_heap_shot_obj (thread
->current_heap_shot
, ho
);
2661 ho
= heap_shot_obj_add_refs (thread
->current_heap_shot
, OBJ_ADDR (objdiff
), num
, &ref_offset
);
2663 for (i
= 0; i
< num
; ++i
) {
2664 /* FIXME: use object distance to measure how good
2665 * the GC is at keeping related objects close
2667 uintptr_t offset
= ctx
->data_version
> 1? last_obj_offset
+ decode_uleb128 (p
, &p
): -1;
2668 intptr_t obj1diff
= decode_sleb128 (p
, &p
);
2669 last_obj_offset
= offset
;
2671 ho
->refs
[ref_offset
+ i
] = OBJ_ADDR (obj1diff
);
2672 if (num_tracked_objects
)
2673 track_obj_reference (OBJ_ADDR (obj1diff
), OBJ_ADDR (objdiff
), cd
);
2676 fprintf (outfile
, "traced object %p, size %llu (%s), refs: %zd\n", (void*)OBJ_ADDR (objdiff
), (unsigned long long) size
, cd
->name
, num
);
2677 } else if (subtype
== TYPE_HEAP_ROOT
) {
2679 if (ctx
->data_version
> 12) {
2680 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2681 LOG_TIME (time_base
, tdiff
);
2683 num
= decode_uleb128 (p
, &p
);
2685 num
= decode_uleb128 (p
+ 1, &p
);
2686 uintptr_t gc_num G_GNUC_UNUSED
= decode_uleb128 (p
, &p
);
2688 for (i
= 0; i
< num
; ++i
) {
2689 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2691 if (ctx
->data_version
> 12)
2694 root_type
= decode_uleb128 (p
, &p
);
2695 /* we just discard the extra info for now */
2696 uintptr_t extra_info
= decode_uleb128 (p
, &p
);
2698 fprintf (outfile
, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff
), get_root_name (root_type
));
2700 thread_add_root (thread
, OBJ_ADDR (objdiff
), root_type
, extra_info
);
2702 } else if (subtype
== TYPE_HEAP_END
) {
2703 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2704 LOG_TIME (time_base
, tdiff
);
2707 fprintf (outfile
, "heap shot end\n");
2708 if (collect_traces
) {
2709 HeapShot
*hs
= thread
->current_heap_shot
;
2710 if (hs
&& thread
->num_roots
) {
2711 /* transfer the root ownershipt to the heapshot */
2712 hs
->num_roots
= thread
->num_roots
;
2713 hs
->roots
= thread
->roots
;
2714 hs
->roots_extra
= thread
->roots_extra
;
2715 hs
->roots_types
= thread
->roots_types
;
2717 g_free (thread
->roots
);
2718 g_free (thread
->roots_extra
);
2719 g_free (thread
->roots_types
);
2721 thread
->num_roots
= 0;
2722 thread
->size_roots
= 0;
2723 thread
->roots
= NULL
;
2724 thread
->roots_extra
= NULL
;
2725 thread
->roots_types
= NULL
;
2726 heap_shot_resolve_reverse_refs (hs
);
2727 heap_shot_mark_objects (hs
);
2728 heap_shot_free_objects (hs
);
2730 thread
->current_heap_shot
= NULL
;
2731 } else if (subtype
== TYPE_HEAP_START
) {
2732 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2733 LOG_TIME (time_base
, tdiff
);
2736 fprintf (outfile
, "heap shot start\n");
2737 thread
->current_heap_shot
= new_heap_shot (time_base
);
2741 case TYPE_MONITOR
: {
2742 int event
= (*p
>> 4) & 0x3;
2743 int has_bt
= *p
& TYPE_MONITOR_BT
;
2744 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2745 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2746 MethodDesc
* sframes
[8];
2747 MethodDesc
** frames
= sframes
;
2750 LOG_TIME (time_base
, tdiff
);
2752 record
= (!thread_filter
|| thread_filter
== thread
->thread_id
);
2753 if (!(time_base
>= time_from
&& time_base
< time_to
))
2755 if (event
== MONO_PROFILER_MONITOR_CONTENTION
) {
2756 MonitorDesc
*mdesc
= lookup_monitor (OBJ_ADDR (objdiff
));
2758 monitor_contention
++;
2759 mdesc
->contentions
++;
2760 thread
->monitor
= mdesc
;
2761 thread
->contention_start
= time_base
;
2765 frames
= decode_bt (ctx
, sframes
, &num_bt
, p
, &p
, ptr_base
, &method_base
);
2767 fprintf (outfile
, "Cannot load backtrace\n");
2771 add_trace_methods (frames
, num_bt
, &mdesc
->traces
, 1);
2774 add_trace_thread (thread
, &mdesc
->traces
, 1);
2776 } else if (event
== MONO_PROFILER_MONITOR_FAIL
) {
2779 if (thread
->monitor
&& thread
->contention_start
) {
2780 uint64_t wait_time
= time_base
- thread
->contention_start
;
2781 if (wait_time
> thread
->monitor
->max_wait_time
)
2782 thread
->monitor
->max_wait_time
= wait_time
;
2783 thread
->monitor
->wait_time
+= wait_time
;
2784 thread
->monitor
= NULL
;
2785 thread
->contention_start
= 0;
2788 } else if (event
== MONO_PROFILER_MONITOR_DONE
) {
2791 if (thread
->monitor
&& thread
->contention_start
) {
2792 uint64_t wait_time
= time_base
- thread
->contention_start
;
2793 if (wait_time
> thread
->monitor
->max_wait_time
)
2794 thread
->monitor
->max_wait_time
= wait_time
;
2795 thread
->monitor
->wait_time
+= wait_time
;
2796 thread
->monitor
= NULL
;
2797 thread
->contention_start
= 0;
2802 fprintf (outfile
, "monitor %s for object %p\n", monitor_ev_name (event
), (void*)OBJ_ADDR (objdiff
));
2803 if (frames
!= sframes
)
2807 case TYPE_EXCEPTION
: {
2808 int subtype
= *p
& 0x70;
2809 int has_bt
= *p
& TYPE_THROW_BT
;
2810 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2811 MethodDesc
* sframes
[8];
2812 MethodDesc
** frames
= sframes
;
2814 LOG_TIME (time_base
, tdiff
);
2816 record
= (!thread_filter
|| thread_filter
== thread
->thread_id
);
2817 if (!(time_base
>= time_from
&& time_base
< time_to
))
2819 if (subtype
== TYPE_CLAUSE
) {
2821 if (ctx
->data_version
> 12)
2824 clause_type
= decode_uleb128 (p
, &p
);
2825 int clause_num
= decode_uleb128 (p
, &p
);
2826 int64_t ptrdiff
= decode_sleb128 (p
, &p
);
2827 method_base
+= ptrdiff
;
2829 clause_summary
[clause_type
]++;
2831 fprintf (outfile
, "clause %s (%d) in method %s\n", clause_name (clause_type
), clause_num
, lookup_method (method_base
)->name
);
2833 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2838 frames
= decode_bt (ctx
, sframes
, &has_bt
, p
, &p
, ptr_base
, &method_base
);
2840 fprintf (outfile
, "Cannot load backtrace\n");
2844 add_trace_methods (frames
, has_bt
, &exc_traces
, 1);
2847 add_trace_thread (thread
, &exc_traces
, 1);
2849 if (frames
!= sframes
)
2852 fprintf (outfile
, "throw %p\n", (void*)OBJ_ADDR (objdiff
));
2856 case TYPE_RUNTIME
: {
2857 int subtype
= *p
& 0xf0;
2858 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2859 LOG_TIME (time_base
, tdiff
);
2861 if (subtype
== TYPE_JITHELPER
) {
2863 if (ctx
->data_version
> 12)
2866 type
= decode_uleb128 (p
, &p
);
2867 intptr_t codediff
= decode_sleb128 (p
, &p
);
2868 int codelen
= decode_uleb128 (p
, &p
);
2870 if (type
== MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
) {
2871 name
= (const char *)p
;
2875 name
= code_buffer_desc (type
);
2878 jit_helpers_code_size
+= codelen
;
2880 fprintf (outfile
, "jit helper %s, size: %d, code: %p\n", name
, codelen
, (void*)(ptr_base
+ codediff
));
2885 int subtype
= *p
& 0xf0;
2886 if (subtype
== TYPE_SAMPLE_HIT
) {
2890 if (ctx
->data_version
> 12) {
2891 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2892 LOG_TIME (time_base
, tdiff
);
2897 sample_type
= decode_uleb128 (p
+ 1, &p
);
2898 tstamp
= decode_uleb128 (p
, &p
);
2900 void *tid
= (void *) thread_id
;
2901 if (ctx
->data_version
> 10)
2902 tid
= (void *) (ptr_base
+ decode_sleb128 (p
, &p
));
2903 int count
= decode_uleb128 (p
, &p
);
2904 for (i
= 0; i
< count
; ++i
) {
2905 uintptr_t ip
= ptr_base
+ decode_sleb128 (p
, &p
);
2906 if ((tstamp
>= time_from
&& tstamp
< time_to
))
2907 add_stat_sample (sample_type
, ip
);
2909 fprintf (outfile
, "sample hit, type: %d at %p for thread %p\n", sample_type
, (void*)ip
, tid
);
2911 if (ctx
->data_version
> 5) {
2912 count
= decode_uleb128 (p
, &p
);
2913 for (i
= 0; i
< count
; ++i
) {
2915 int64_t ptrdiff
= decode_sleb128 (p
, &p
);
2916 method_base
+= ptrdiff
;
2917 method
= lookup_method (method_base
);
2919 fprintf (outfile
, "sample hit bt %d: %s\n", i
, method
->name
);
2920 if (ctx
->data_version
< 13) {
2921 decode_sleb128 (p
, &p
); /* il offset */
2922 decode_sleb128 (p
, &p
); /* native offset */
2926 } else if (subtype
== TYPE_SAMPLE_USYM
) {
2927 /* un unmanaged symbol description */
2929 if (ctx
->data_version
> 12) {
2930 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2931 LOG_TIME (time_base
, tdiff
);
2933 addr
= ptr_base
+ decode_sleb128 (p
, &p
);
2935 addr
= ptr_base
+ decode_sleb128 (p
+ 1, &p
);
2936 uintptr_t size
= decode_uleb128 (p
, &p
);
2938 name
= pstrdup ((char*)p
);
2939 add_unmanaged_symbol (addr
, name
, size
);
2941 fprintf (outfile
, "unmanaged symbol %s at %p\n", name
, (void*)addr
);
2944 } else if (subtype
== TYPE_SAMPLE_UBIN
) {
2945 /* un unmanaged binary loaded in memory */
2946 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2947 uintptr_t addr
= decode_sleb128 (p
, &p
);
2948 uint64_t offset G_GNUC_UNUSED
= decode_uleb128 (p
, &p
);
2949 uintptr_t size
= decode_uleb128 (p
, &p
);
2951 LOG_TIME (time_base
, tdiff
);
2953 name
= pstrdup ((char*)p
);
2954 add_unmanaged_binary (addr
, name
, size
);
2956 fprintf (outfile
, "unmanaged binary %s at %p\n", name
, (void*)addr
);
2959 } else if (subtype
== TYPE_SAMPLE_COUNTERS_DESC
) {
2961 if (ctx
->data_version
> 12) {
2962 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2963 LOG_TIME (time_base
, tdiff
);
2965 len
= decode_uleb128 (p
, &p
);
2967 len
= decode_uleb128 (p
+ 1, &p
);
2968 for (i
= 0; i
< len
; i
++) {
2969 uint64_t type
, unit
, variance
, index
;
2970 uint64_t section
= decode_uleb128 (p
, &p
);
2971 char *section_str
, *name
;
2972 if (section
!= MONO_COUNTER_PERFCOUNTERS
) {
2973 section_str
= (char*) section_name (section
);
2975 section_str
= pstrdup ((char*)p
);
2978 name
= pstrdup ((char*)p
);
2980 if (ctx
->data_version
> 12) {
2985 type
= decode_uleb128 (p
, &p
);
2986 unit
= decode_uleb128 (p
, &p
);
2987 variance
= decode_uleb128 (p
, &p
);
2989 index
= decode_uleb128 (p
, &p
);
2990 add_counter (section_str
, name
, (int)type
, (int)unit
, (int)variance
, (int)index
);
2992 } else if (subtype
== TYPE_SAMPLE_COUNTERS
) {
2994 CounterValue
*value
, *previous
= NULL
;
2996 uint64_t timestamp
; // milliseconds since startup
2997 if (ctx
->data_version
> 12) {
2998 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2999 LOG_TIME (time_base
, tdiff
);
3001 timestamp
= (time_base
- startup_time
) / 1000 / 1000;
3003 timestamp
= decode_uleb128 (p
+ 1, &p
);
3004 uint64_t time_between
= timestamp
/ 1000 * 1000 * 1000 * 1000 + startup_time
;
3006 uint64_t type
, index
= decode_uleb128 (p
, &p
);
3010 for (list
= counters
; list
; list
= list
->next
) {
3011 if (list
->counter
->index
== (int)index
) {
3012 previous
= list
->counter
->values_last
;
3017 if (ctx
->data_version
> 12)
3020 type
= decode_uleb128 (p
, &p
);
3022 value
= (CounterValue
*) g_calloc (1, sizeof (CounterValue
));
3023 value
->timestamp
= timestamp
;
3026 case MONO_COUNTER_INT
:
3027 #if SIZEOF_VOID_P == 4
3028 case MONO_COUNTER_WORD
:
3030 value
->buffer
= (unsigned char *)g_malloc (sizeof (int32_t));
3031 *(int32_t*)value
->buffer
= (int32_t)decode_sleb128 (p
, &p
) + (previous
? (*(int32_t*)previous
->buffer
) : 0);
3033 case MONO_COUNTER_UINT
:
3034 value
->buffer
= (unsigned char *) g_malloc (sizeof (uint32_t));
3035 *(uint32_t*)value
->buffer
= (uint32_t)decode_uleb128 (p
, &p
) + (previous
? (*(uint32_t*)previous
->buffer
) : 0);
3037 case MONO_COUNTER_LONG
:
3038 #if SIZEOF_VOID_P == 8
3039 case MONO_COUNTER_WORD
:
3041 case MONO_COUNTER_TIME_INTERVAL
:
3042 value
->buffer
= (unsigned char *) g_malloc (sizeof (int64_t));
3043 *(int64_t*)value
->buffer
= (int64_t)decode_sleb128 (p
, &p
) + (previous
? (*(int64_t*)previous
->buffer
) : 0);
3045 case MONO_COUNTER_ULONG
:
3046 value
->buffer
= (unsigned char *) g_malloc (sizeof (uint64_t));
3047 *(uint64_t*)value
->buffer
= (uint64_t)decode_uleb128 (p
, &p
) + (previous
? (*(uint64_t*)previous
->buffer
) : 0);
3049 case MONO_COUNTER_DOUBLE
:
3050 value
->buffer
= (unsigned char *) g_malloc (sizeof (double));
3051 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
3052 for (i
= 0; i
< sizeof (double); i
++)
3054 for (i
= sizeof (double) - 1; i
>= 0; i
--)
3056 value
->buffer
[i
] = *p
++;
3058 case MONO_COUNTER_STRING
:
3060 value
->buffer
= NULL
;
3062 value
->buffer
= (unsigned char*) pstrdup ((char*)p
);
3067 if (time_between
>= time_from
&& time_between
<= time_to
)
3068 add_counter_value (index
, value
);
3075 case TYPE_COVERAGE
:{
3076 int subtype
= *p
& 0xf0;
3078 case TYPE_COVERAGE_METHOD
: {
3079 CoverageMethod
*method
= g_new0 (CoverageMethod
, 1);
3080 const char *assembly
, *klass
, *name
, *sig
, *filename
;
3081 int token
, n_offsets
, method_id
;
3085 if (ctx
->data_version
> 12) {
3086 uint64_t tdiff
= decode_uleb128 (p
, &p
);
3087 LOG_TIME (time_base
, tdiff
);
3091 assembly
= (const char *)p
; while (*p
) p
++; p
++;
3092 klass
= (const char *)p
; while (*p
) p
++; p
++;
3093 name
= (const char *)p
; while (*p
) p
++; p
++;
3094 sig
= (const char *)p
; while (*p
) p
++; p
++;
3095 filename
= (const char *)p
; while (*p
) p
++; p
++;
3097 token
= decode_uleb128 (p
, &p
);
3098 method_id
= decode_uleb128 (p
, &p
);
3099 n_offsets
= decode_uleb128 (p
, &p
);
3101 method
->assembly_name
= g_strdup (assembly
);
3102 method
->class_name
= g_strdup (klass
);
3103 method
->method_name
= g_strdup (name
);
3104 method
->method_signature
= g_strdup (sig
);
3105 method
->filename
= g_strdup (filename
);
3106 method
->token
= token
;
3107 method
->n_statements
= n_offsets
;
3108 method
->coverage
= g_ptr_array_new ();
3109 method
->method_id
= method_id
;
3111 coverage_add_method (method
);
3115 case TYPE_COVERAGE_STATEMENT
: {
3116 CoverageCoverage
*coverage
= g_new0 (CoverageCoverage
, 1);
3117 int offset
, count
, line
, column
, method_id
;
3121 if (ctx
->data_version
> 12) {
3122 uint64_t tdiff
= decode_uleb128 (p
, &p
);
3123 LOG_TIME (time_base
, tdiff
);
3127 method_id
= decode_uleb128 (p
, &p
);
3128 offset
= decode_uleb128 (p
, &p
);
3129 count
= decode_uleb128 (p
, &p
);
3130 line
= decode_uleb128 (p
, &p
);
3131 column
= decode_uleb128 (p
, &p
);
3133 coverage
->method_id
= method_id
;
3134 coverage
->offset
= offset
;
3135 coverage
->count
= count
;
3136 coverage
->line
= line
;
3137 coverage
->column
= column
;
3139 coverage_add_coverage (coverage
);
3142 case TYPE_COVERAGE_ASSEMBLY
: {
3143 CoverageAssembly
*assembly
= g_new0 (CoverageAssembly
, 1);
3144 char *name
, *guid
, *filename
;
3145 int number_of_methods
, fully_covered
, partially_covered
;
3148 if (ctx
->data_version
> 12) {
3149 uint64_t tdiff
= decode_uleb128 (p
, &p
);
3150 LOG_TIME (time_base
, tdiff
);
3154 name
= (char *)p
; while (*p
) p
++; p
++;
3155 guid
= (char *)p
; while (*p
) p
++; p
++;
3156 filename
= (char *)p
; while (*p
) p
++; p
++;
3157 number_of_methods
= decode_uleb128 (p
, &p
);
3158 fully_covered
= decode_uleb128 (p
, &p
);
3159 partially_covered
= decode_uleb128 (p
, &p
);
3161 assembly
->name
= g_strdup (name
);
3162 assembly
->guid
= g_strdup (guid
);
3163 assembly
->filename
= g_strdup (filename
);
3164 assembly
->number_of_methods
= number_of_methods
;
3165 assembly
->fully_covered
= fully_covered
;
3166 assembly
->partially_covered
= partially_covered
;
3168 coverage_add_assembly (assembly
);
3171 case TYPE_COVERAGE_CLASS
: {
3172 CoverageClass
*klass
= g_new0 (CoverageClass
, 1);
3173 char *assembly_name
, *class_name
;
3174 int number_of_methods
, fully_covered
, partially_covered
;
3177 if (ctx
->data_version
> 12) {
3178 uint64_t tdiff
= decode_uleb128 (p
, &p
);
3179 LOG_TIME (time_base
, tdiff
);
3183 assembly_name
= (char *)p
; while (*p
) p
++; p
++;
3184 class_name
= (char *)p
; while (*p
) p
++; p
++;
3185 number_of_methods
= decode_uleb128 (p
, &p
);
3186 fully_covered
= decode_uleb128 (p
, &p
);
3187 partially_covered
= decode_uleb128 (p
, &p
);
3189 klass
->assembly_name
= g_strdup (assembly_name
);
3190 klass
->class_name
= g_strdup (class_name
);
3191 klass
->number_of_methods
= number_of_methods
;
3192 klass
->fully_covered
= fully_covered
;
3193 klass
->partially_covered
= partially_covered
;
3195 coverage_add_class (klass
);
3205 int subtype
= *p
& 0xf0;
3206 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
3207 LOG_TIME (time_base
, tdiff
);
3209 if (subtype
== TYPE_SYNC_POINT
) {
3212 fprintf (outfile
, "sync point %i (%s)\n", type
, sync_point_name (type
));
3217 fprintf (outfile
, "unhandled profiler event: 0x%x at file offset: %llu + %lld (len: %d\n)\n", *p
, (unsigned long long) file_offset
, (long long) (p
- ctx
->buf
), len
);
3220 record_event_stats (event
, p
- start
);
3222 thread
->last_time
= time_base
;
3223 for (i
= 0; i
< thread
->stack_id
; ++i
)
3224 thread
->stack
[i
]->recurse_count
= 0;
3229 read_header_string (ProfContext
*ctx
, char **field
)
3231 if (!load_data (ctx
, 4))
3234 if (!load_data (ctx
, read_int32 (ctx
->buf
)))
3237 *field
= pstrdup ((const char *) ctx
->buf
);
3243 load_file (char *name
)
3246 ProfContext
*ctx
= (ProfContext
*) g_calloc (sizeof (ProfContext
), 1);
3247 if (strcmp (name
, "-") == 0)
3250 ctx
->file
= fopen (name
, "rb");
3252 printf ("Cannot open file: %s\n", name
);
3255 #if defined (HAVE_SYS_ZLIB)
3256 if (ctx
->file
!= stdin
)
3257 ctx
->gzfile
= gzdopen (fileno (ctx
->file
), "rb");
3259 if (!load_data (ctx
, 30))
3262 if (read_int32 (p
) != LOG_HEADER_ID
|| p
[6] > LOG_DATA_VERSION
)
3264 ctx
->version_major
= p
[4];
3265 ctx
->version_minor
= p
[5];
3266 ctx
->data_version
= p
[6];
3267 /* reading 64 bit files on 32 bit systems not supported yet */
3268 if (p
[7] > sizeof (void*))
3270 if (read_int32 (p
+ 20)) /* flags must be 0 */
3272 ctx
->startup_time
= read_int64 (p
+ 8);
3273 ctx
->timer_overhead
= read_int32 (p
+ 16);
3274 ctx
->pid
= read_int32 (p
+ 24);
3275 ctx
->port
= read_int16 (p
+ 28);
3276 if (ctx
->version_major
>= 1) {
3277 if (!read_header_string (ctx
, &ctx
->args
))
3279 if (!read_header_string (ctx
, &ctx
->arch
))
3281 if (!read_header_string (ctx
, &ctx
->os
))
3284 if (!load_data (ctx
, 2)) /* old opsys field, was never used */
3294 static int alloc_sort_mode
= ALLOC_SORT_BYTES
;
3297 compare_class (const void *a
, const void *b
)
3299 ClassDesc
*const *A
= (ClassDesc
*const *)a
;
3300 ClassDesc
*const *B
= (ClassDesc
*const *)b
;
3301 uint64_t vala
, valb
;
3302 if (alloc_sort_mode
== ALLOC_SORT_BYTES
) {
3303 vala
= (*A
)->alloc_size
;
3304 valb
= (*B
)->alloc_size
;
3306 vala
= (*A
)->allocs
;
3307 valb
= (*B
)->allocs
;
3317 dump_header (ProfContext
*ctx
)
3319 time_t st
= ctx
->startup_time
/ 1000;
3320 char *t
= ctime (&st
);
3321 fprintf (outfile
, "\nMono log profiler data\n");
3322 fprintf (outfile
, "\tProfiler version: %d.%d\n", ctx
->version_major
, ctx
->version_minor
);
3323 fprintf (outfile
, "\tData version: %d\n", ctx
->data_version
);
3324 if (ctx
->version_major
>= 1) {
3325 fprintf (outfile
, "\tArguments: %s\n", ctx
->args
);
3326 fprintf (outfile
, "\tArchitecture: %s\n", ctx
->arch
);
3327 fprintf (outfile
, "\tOperating system: %s\n", ctx
->os
);
3329 fprintf (outfile
, "\tMean timer overhead: %d nanoseconds\n", ctx
->timer_overhead
);
3330 fprintf (outfile
, "\tProgram startup: %s", t
);
3332 fprintf (outfile
, "\tProgram ID: %d\n", ctx
->pid
);
3334 fprintf (outfile
, "\tServer listening on: %d\n", ctx
->port
);
3338 dump_traces (TraceDesc
*traces
, const char *desc
)
3345 sort_context_array (traces
);
3346 for (j
= 0; j
< traces
->count
; ++j
) {
3349 bt
= traces
->traces
[j
].bt
;
3352 fprintf (outfile
, "\t%llu %s from:\n", (unsigned long long) traces
->traces
[j
].count
, desc
);
3353 for (k
= 0; k
< bt
->count
; ++k
)
3354 fprintf (outfile
, "\t\t%s\n", bt
->methods
[k
]->name
);
3359 dump_threads (ProfContext
*ctx
)
3361 ThreadContext
*thread
;
3362 fprintf (outfile
, "\nThread summary\n");
3363 for (thread
= ctx
->threads
; thread
; thread
= thread
->next
) {
3364 if (thread
->thread_id
) {
3365 fprintf (outfile
, "\tThread: %p, name: \"%s\"\n", (void*)thread
->thread_id
, thread
->name
? thread
->name
: "");
3371 dump_domains (ProfContext
*ctx
)
3373 fprintf (outfile
, "\nDomain summary\n");
3375 for (DomainContext
*domain
= ctx
->domains
; domain
; domain
= domain
->next
)
3376 fprintf (outfile
, "\tDomain: %p, friendly name: \"%s\"\n", (void *) domain
->domain_id
, domain
->friendly_name
);
3380 dump_remctxs (ProfContext
*ctx
)
3382 fprintf (outfile
, "\nContext summary\n");
3384 for (RemCtxContext
*remctx
= ctx
->remctxs
; remctx
; remctx
= remctx
->next
)
3385 fprintf (outfile
, "\tContext: %p, domain: %p\n", (void *) remctx
->remctx_id
, (void *) remctx
->domain_id
);
3389 dump_exceptions (void)
3392 fprintf (outfile
, "\nException summary\n");
3393 fprintf (outfile
, "\tThrows: %llu\n", (unsigned long long) throw_count
);
3394 dump_traces (&exc_traces
, "throws");
3395 for (i
= 0; i
<= MONO_EXCEPTION_CLAUSE_FAULT
; ++i
) {
3396 if (!clause_summary
[i
])
3398 fprintf (outfile
, "\tExecuted %s clauses: %llu\n", clause_name (i
), (unsigned long long) clause_summary
[i
]);
3403 compare_monitor (const void *a
, const void *b
)
3405 MonitorDesc
*const *A
= (MonitorDesc
*const *)a
;
3406 MonitorDesc
*const *B
= (MonitorDesc
*const *)b
;
3407 if ((*B
)->wait_time
== (*A
)->wait_time
)
3409 if ((*B
)->wait_time
< (*A
)->wait_time
)
3415 dump_monitors (void)
3417 MonitorDesc
**monitors
;
3421 monitors
= (MonitorDesc
**) g_malloc (sizeof (void*) * num_monitors
);
3422 for (i
= 0, j
= 0; i
< SMALL_HASH_SIZE
; ++i
) {
3423 MonitorDesc
*mdesc
= monitor_hash
[i
];
3425 monitors
[j
++] = mdesc
;
3426 mdesc
= mdesc
->next
;
3429 qsort (monitors
, num_monitors
, sizeof (void*), compare_monitor
);
3430 fprintf (outfile
, "\nMonitor lock summary\n");
3431 for (i
= 0; i
< num_monitors
; ++i
) {
3432 MonitorDesc
*mdesc
= monitors
[i
];
3433 fprintf (outfile
, "\tLock object %p: %d contentions\n", (void*)mdesc
->objid
, (int)mdesc
->contentions
);
3434 fprintf (outfile
, "\t\t%.6f secs total wait time, %.6f max, %.6f average\n",
3435 mdesc
->wait_time
/1000000000.0, mdesc
->max_wait_time
/1000000000.0, mdesc
->wait_time
/1000000000.0/mdesc
->contentions
);
3436 dump_traces (&mdesc
->traces
, "contentions");
3438 fprintf (outfile
, "\tLock contentions: %llu\n", (unsigned long long) monitor_contention
);
3439 fprintf (outfile
, "\tLock acquired: %llu\n", (unsigned long long) monitor_acquired
);
3440 fprintf (outfile
, "\tLock failures: %llu\n", (unsigned long long) monitor_failed
);
3447 fprintf (outfile
, "\nGC summary\n");
3448 fprintf (outfile
, "\tGC resizes: %d\n", gc_resizes
);
3449 fprintf (outfile
, "\tMax heap size: %llu\n", (unsigned long long) max_heap_size
);
3450 fprintf (outfile
, "\tObject moves: %llu\n", (unsigned long long) gc_object_moves
);
3451 for (i
= 0; i
< 3; ++i
) {
3452 if (!gc_info
[i
].count
)
3454 fprintf (outfile
, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
3455 i
, gc_info
[i
].count
,
3456 (unsigned long long) (gc_info
[i
].max_time
/ 1000),
3457 (unsigned long long) (gc_info
[i
].total_time
/ 1000),
3458 (unsigned long long) (gc_info
[i
].total_time
/ gc_info
[i
].count
/ 1000));
3460 for (i
= 0; i
< 3; ++i
) {
3461 if (!handle_info
[i
].max_live
)
3463 fprintf (outfile
, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
3464 get_handle_name (i
),
3465 (unsigned long long) (handle_info
[i
].created
),
3466 (unsigned long long) (handle_info
[i
].destroyed
),
3467 (unsigned long long) (handle_info
[i
].max_live
));
3468 dump_traces (&handle_info
[i
].traces
, "created");
3469 dump_traces (&handle_info
[i
].destroy_traces
, "destroyed");
3478 int compiled_methods
= 0;
3480 fprintf (outfile
, "\nJIT summary\n");
3481 for (i
= 0; i
< HASH_SIZE
; ++i
) {
3482 m
= method_hash
[i
];
3483 for (m
= method_hash
[i
]; m
; m
= m
->next
) {
3484 if (!m
->code
|| m
->ignore_jit
)
3487 code_size
+= m
->len
;
3490 fprintf (outfile
, "\tCompiled methods: %d\n", compiled_methods
);
3491 fprintf (outfile
, "\tGenerated code size: %d\n", code_size
);
3492 fprintf (outfile
, "\tJIT helpers: %d\n", num_jit_helpers
);
3493 fprintf (outfile
, "\tJIT helpers code size: %d\n", jit_helpers_code_size
);
3497 dump_allocations (void)
3500 intptr_t allocs
= 0;
3502 int header_done
= 0;
3503 ClassDesc
**classes
= (ClassDesc
**) g_malloc (num_classes
* sizeof (void*));
3506 for (i
= 0; i
< HASH_SIZE
; ++i
) {
3507 cd
= class_hash
[i
];
3513 qsort (classes
, num_classes
, sizeof (void*), compare_class
);
3514 for (i
= 0; i
< num_classes
; ++i
) {
3518 allocs
+= cd
->allocs
;
3519 size
+= cd
->alloc_size
;
3520 if (!header_done
++) {
3521 fprintf (outfile
, "\nAllocation summary\n");
3522 fprintf (outfile
, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
3524 fprintf (outfile
, "%10llu %10zd %8llu %s\n",
3525 (unsigned long long) (cd
->alloc_size
),
3527 (unsigned long long) (cd
->alloc_size
/ cd
->allocs
),
3529 dump_traces (&cd
->traces
, "bytes");
3532 fprintf (outfile
, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size
, allocs
);
3541 static int method_sort_mode
= METHOD_SORT_TOTAL
;
3544 compare_method (const void *a
, const void *b
)
3546 MethodDesc
*const *A
= (MethodDesc
*const *)a
;
3547 MethodDesc
*const *B
= (MethodDesc
*const *)b
;
3548 uint64_t vala
, valb
;
3549 if (method_sort_mode
== METHOD_SORT_SELF
) {
3550 vala
= (*A
)->self_time
;
3551 valb
= (*B
)->self_time
;
3552 } else if (method_sort_mode
== METHOD_SORT_CALLS
) {
3556 vala
= (*A
)->total_time
;
3557 valb
= (*B
)->total_time
;
3567 dump_metadata (void)
3569 fprintf (outfile
, "\nMetadata summary\n");
3570 fprintf (outfile
, "\tLoaded images: %d\n", num_images
);
3574 for (i
= 0; i
< SMALL_HASH_SIZE
; ++i
) {
3575 image
= image_hash
[i
];
3577 fprintf (outfile
, "\t\t%s\n", image
->filename
);
3578 image
= image
->next
;
3582 fprintf (outfile
, "\tLoaded assemblies: %d\n", num_assemblies
);
3584 AssemblyDesc
*assembly
;
3586 for (i
= 0; i
< SMALL_HASH_SIZE
; ++i
) {
3587 assembly
= assembly_hash
[i
];
3589 fprintf (outfile
, "\t\t%s\n", assembly
->asmname
);
3590 assembly
= assembly
->next
;
3601 int header_done
= 0;
3602 MethodDesc
**methods
= (MethodDesc
**) g_malloc (num_methods
* sizeof (void*));
3605 for (i
= 0; i
< HASH_SIZE
; ++i
) {
3606 cd
= method_hash
[i
];
3608 cd
->total_time
= cd
->self_time
+ cd
->callee_time
;
3613 qsort (methods
, num_methods
, sizeof (void*), compare_method
);
3614 for (i
= 0; i
< num_methods
; ++i
) {
3621 msecs
= cd
->total_time
/ 1000000;
3622 smsecs
= (cd
->total_time
- cd
->callee_time
) / 1000000;
3623 if (!msecs
&& !verbose
)
3625 if (!header_done
++) {
3626 fprintf (outfile
, "\nMethod call summary\n");
3627 fprintf (outfile
, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
3629 fprintf (outfile
, "%8llu %8llu %10llu %s\n",
3630 (unsigned long long) (msecs
),
3631 (unsigned long long) (smsecs
),
3632 (unsigned long long) (cd
->calls
),
3634 dump_traces (&cd
->traces
, "calls");
3637 fprintf (outfile
, "Total calls: %llu\n", (unsigned long long) calls
);
3641 compare_heap_class (const void *a
, const void *b
)
3643 HeapClassDesc
*const *A
= (HeapClassDesc
*const *)a
;
3644 HeapClassDesc
*const *B
= (HeapClassDesc
*const *)b
;
3645 uint64_t vala
, valb
;
3646 if (alloc_sort_mode
== ALLOC_SORT_BYTES
) {
3647 vala
= (*A
)->total_size
;
3648 valb
= (*B
)->total_size
;
3661 compare_rev_class (const void *a
, const void *b
)
3663 const HeapClassRevRef
*A
= (const HeapClassRevRef
*)a
;
3664 const HeapClassRevRef
*B
= (const HeapClassRevRef
*)b
;
3665 if (B
->count
== A
->count
)
3667 if (B
->count
< A
->count
)
3673 dump_rev_claases (HeapClassRevRef
*revs
, int count
)
3680 for (j
= 0; j
< count
; ++j
) {
3681 HeapClassDesc
*cd
= revs
[j
].klass
;
3682 fprintf (outfile
, "\t\t%llu references from: %s\n",
3683 (unsigned long long) (revs
[j
].count
),
3689 heap_shot_summary (HeapShot
*hs
, int hs_num
, HeapShot
*last_hs
)
3696 HeapClassDesc
**sorted
;
3697 sorted
= (HeapClassDesc
**) g_malloc (sizeof (void*) * hs
->class_count
);
3698 for (i
= 0; i
< hs
->hash_size
; ++i
) {
3699 cd
= hs
->class_hash
[i
];
3703 size
+= cd
->total_size
;
3704 sorted
[ccount
++] = cd
;
3706 hs
->sorted
= sorted
;
3707 qsort (sorted
, ccount
, sizeof (void*), compare_heap_class
);
3708 fprintf (outfile
, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %zd\n",
3710 (hs
->timestamp
- startup_time
)/1000000000.0,
3711 (unsigned long long) (size
),
3712 (unsigned long long) (count
),
3713 ccount
, hs
->num_roots
);
3714 if (!verbose
&& ccount
> 30)
3716 fprintf (outfile
, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
3717 for (i
= 0; i
< ccount
; ++i
) {
3718 HeapClassRevRef
*rev_sorted
;
3720 HeapClassDesc
*ocd
= NULL
;
3723 ocd
= heap_class_lookup (last_hs
, cd
->klass
);
3724 fprintf (outfile
, "\t%10llu %10llu %8llu %s",
3725 (unsigned long long) (cd
->total_size
),
3726 (unsigned long long) (cd
->count
),
3727 (unsigned long long) (cd
->total_size
/ cd
->count
),
3730 int64_t bdiff
= cd
->total_size
- ocd
->total_size
;
3731 int64_t cdiff
= cd
->count
- ocd
->count
;
3732 fprintf (outfile
, " (bytes: %+lld, count: %+lld)\n", (long long) bdiff
, (long long) cdiff
);
3734 fprintf (outfile
, "\n");
3736 if (!collect_traces
)
3738 rev_sorted
= (HeapClassRevRef
*) g_malloc (cd
->rev_count
* sizeof (HeapClassRevRef
));
3740 for (j
= 0; j
< cd
->rev_hash_size
; ++j
) {
3741 if (cd
->rev_hash
[j
].klass
)
3742 rev_sorted
[k
++] = cd
->rev_hash
[j
];
3744 assert (cd
->rev_count
== k
);
3745 qsort (rev_sorted
, cd
->rev_count
, sizeof (HeapClassRevRef
), compare_rev_class
);
3746 if (cd
->root_references
)
3747 fprintf (outfile
, "\t\t%zd root references (%zd pinning)\n", cd
->root_references
, cd
->pinned_references
);
3748 dump_rev_claases (rev_sorted
, cd
->rev_count
);
3749 g_free (rev_sorted
);
3755 compare_heap_shots (const void *a
, const void *b
)
3757 HeapShot
*const *A
= (HeapShot
*const *)a
;
3758 HeapShot
*const *B
= (HeapShot
*const *)b
;
3759 if ((*B
)->timestamp
== (*A
)->timestamp
)
3761 if ((*B
)->timestamp
> (*A
)->timestamp
)
3767 dump_heap_shots (void)
3769 HeapShot
**hs_sorted
;
3771 HeapShot
*last_hs
= NULL
;
3775 hs_sorted
= (HeapShot
**) g_malloc (num_heap_shots
* sizeof (void*));
3776 fprintf (outfile
, "\nHeap shot summary\n");
3778 for (hs
= heap_shots
; hs
; hs
= hs
->next
)
3779 hs_sorted
[i
++] = hs
;
3780 qsort (hs_sorted
, num_heap_shots
, sizeof (void*), compare_heap_shots
);
3781 for (i
= 0; i
< num_heap_shots
; ++i
) {
3783 heap_shot_summary (hs
, i
, last_hs
);
3788 /* This is a very basic escape function that escapes < > and &
3789 Ideally we'd use g_markup_escape_string but that function isn't
3790 available in Mono's eglib. This was written without looking at the
3791 source of that function in glib. */
3793 escape_string_for_xml (const char *string
)
3795 GString
*string_builder
= g_string_new (NULL
);
3796 const char *start
, *p
;
3800 while (*p
&& *p
!= '&' && *p
!= '<' && *p
!= '>')
3803 g_string_append_len (string_builder
, start
, p
- start
);
3810 g_string_append (string_builder
, "<");
3814 g_string_append (string_builder
, ">");
3818 g_string_append (string_builder
, "&");
3829 return g_string_free (string_builder
, FALSE
);
3833 sort_assemblies (gconstpointer a
, gconstpointer b
)
3835 CoverageAssembly
*assembly_a
= *(CoverageAssembly
**)a
;
3836 CoverageAssembly
*assembly_b
= *(CoverageAssembly
**)b
;
3838 if (assembly_a
->name
== NULL
&& assembly_b
->name
== NULL
)
3840 else if (assembly_a
->name
== NULL
)
3842 else if (assembly_b
->name
== NULL
)
3845 return strcmp (assembly_a
->name
, assembly_b
->name
);
3849 dump_coverage (void)
3851 if (!coverage_methods
&& !coverage_assemblies
)
3854 gather_coverage_statements ();
3855 fprintf (outfile
, "\nCoverage Summary:\n");
3857 if (coverage_outfile
) {
3858 fprintf (coverage_outfile
, "<?xml version=\"1.0\"?>\n");
3859 fprintf (coverage_outfile
, "<coverage version=\"0.3\">\n");
3862 g_ptr_array_sort (coverage_assemblies
, sort_assemblies
);
3864 for (guint i
= 0; i
< coverage_assemblies
->len
; i
++) {
3865 CoverageAssembly
*assembly
= (CoverageAssembly
*)coverage_assemblies
->pdata
[i
];
3868 if (assembly
->number_of_methods
!= 0) {
3869 int percentage
= ((assembly
->fully_covered
+ assembly
->partially_covered
) * 100) / assembly
->number_of_methods
;
3870 fprintf (outfile
, "\t%s (%s) %d%% covered (%d methods - %d covered)\n", assembly
->name
, assembly
->filename
, percentage
, assembly
->number_of_methods
, assembly
->fully_covered
);
3872 fprintf (outfile
, "\t%s (%s) ?%% covered (%d methods - %d covered)\n", assembly
->name
, assembly
->filename
, assembly
->number_of_methods
, assembly
->fully_covered
);
3874 if (coverage_outfile
) {
3875 char *escaped_name
, *escaped_filename
;
3876 escaped_name
= escape_string_for_xml (assembly
->name
);
3877 escaped_filename
= escape_string_for_xml (assembly
->filename
);
3879 fprintf (coverage_outfile
, "\t<assembly name=\"%s\" guid=\"%s\" filename=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n", escaped_name
, assembly
->guid
, escaped_filename
, assembly
->number_of_methods
, assembly
->fully_covered
, assembly
->partially_covered
);
3881 g_free (escaped_name
);
3882 g_free (escaped_filename
);
3885 classes
= (GPtrArray
*)g_hash_table_lookup (coverage_assembly_classes
, assembly
->name
);
3887 for (guint j
= 0; j
< classes
->len
; j
++) {
3888 CoverageClass
*klass
= (CoverageClass
*)classes
->pdata
[j
];
3890 if (klass
->number_of_methods
> 0) {
3891 int percentage
= ((klass
->fully_covered
+ klass
->partially_covered
) * 100) / klass
->number_of_methods
;
3892 fprintf (outfile
, "\t\t%s %d%% covered (%d methods - %d covered)\n", klass
->class_name
, percentage
, klass
->number_of_methods
, klass
->fully_covered
);
3894 fprintf (outfile
, "\t\t%s ?%% covered (%d methods - %d covered)\n", klass
->class_name
, klass
->number_of_methods
, klass
->fully_covered
);
3896 if (coverage_outfile
) {
3898 escaped_name
= escape_string_for_xml (klass
->class_name
);
3900 fprintf (coverage_outfile
, "\t\t<class name=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n", escaped_name
, klass
->number_of_methods
, klass
->fully_covered
, klass
->partially_covered
);
3901 g_free (escaped_name
);
3907 for (guint i
= 0; i
< coverage_methods
->len
; i
++) {
3908 CoverageMethod
*method
= (CoverageMethod
*)coverage_methods
->pdata
[i
];
3910 if (coverage_outfile
) {
3911 char *escaped_assembly
, *escaped_class
, *escaped_method
, *escaped_sig
, *escaped_filename
;
3913 escaped_assembly
= escape_string_for_xml (method
->assembly_name
);
3914 escaped_class
= escape_string_for_xml (method
->class_name
);
3915 escaped_method
= escape_string_for_xml (method
->method_name
);
3916 escaped_sig
= escape_string_for_xml (method
->method_signature
);
3917 escaped_filename
= escape_string_for_xml (method
->filename
);
3919 fprintf (coverage_outfile
, "\t<method assembly=\"%s\" class=\"%s\" name=\"%s (%s)\" filename=\"%s\" token=\"%d\">\n", escaped_assembly
, escaped_class
, escaped_method
, escaped_sig
, escaped_filename
, method
->token
);
3921 g_free (escaped_assembly
);
3922 g_free (escaped_class
);
3923 g_free (escaped_method
);
3924 g_free (escaped_sig
);
3925 g_free (escaped_filename
);
3927 for (guint j
= 0; j
< method
->coverage
->len
; j
++) {
3928 CoverageCoverage
*coverage
= (CoverageCoverage
*)method
->coverage
->pdata
[j
];
3929 fprintf (coverage_outfile
, "\t\t<statement offset=\"%d\" counter=\"%d\" line=\"%d\" column=\"%d\"/>\n", coverage
->offset
, coverage
->count
, coverage
->line
, coverage
->column
);
3931 fprintf (coverage_outfile
, "\t</method>\n");
3935 if (coverage_outfile
) {
3936 fprintf (coverage_outfile
, "</coverage>\n");
3937 fclose (coverage_outfile
);
3938 coverage_outfile
= NULL
;
3942 #define DUMP_EVENT_STAT(EVENT,SUBTYPE) dump_event (#EVENT, #SUBTYPE, EVENT, SUBTYPE);
3945 dump_event (const char *event_name
, const char *subtype_name
, int event
, int subtype
)
3947 int idx
= event
| subtype
;
3948 EventStat evt
= stats
[idx
];
3952 fprintf (outfile
, "\t%16s\t%26s\tcount %6d\tmin %3d\tmax %6d\tbytes %d\n", event_name
, subtype_name
, evt
.count
, evt
.min_size
, evt
.max_size
, evt
.bytes
);
3958 fprintf (outfile
, "\nMlpd statistics\n");
3959 fprintf (outfile
, "\tBuffer count %d\toverhead %d (%d bytes per header)\n", buffer_count
, buffer_count
* BUFFER_HEADER_SIZE
, BUFFER_HEADER_SIZE
);
3960 fprintf (outfile
, "\nEvent details:\n");
3962 DUMP_EVENT_STAT (TYPE_ALLOC
, TYPE_ALLOC_NO_BT
);
3963 DUMP_EVENT_STAT (TYPE_ALLOC
, TYPE_ALLOC_BT
);
3965 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_EVENT
);
3966 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_RESIZE
);
3967 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_MOVE
);
3968 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_HANDLE_CREATED
);
3969 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_HANDLE_DESTROYED
);
3970 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_HANDLE_CREATED_BT
);
3971 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_HANDLE_DESTROYED_BT
);
3973 DUMP_EVENT_STAT (TYPE_METADATA
, TYPE_END_LOAD
);
3974 DUMP_EVENT_STAT (TYPE_METADATA
, TYPE_END_UNLOAD
);
3976 DUMP_EVENT_STAT (TYPE_METHOD
, TYPE_LEAVE
);
3977 DUMP_EVENT_STAT (TYPE_METHOD
, TYPE_ENTER
);
3978 DUMP_EVENT_STAT (TYPE_METHOD
, TYPE_EXC_LEAVE
);
3979 DUMP_EVENT_STAT (TYPE_METHOD
, TYPE_JIT
);
3981 DUMP_EVENT_STAT (TYPE_EXCEPTION
, TYPE_THROW_NO_BT
);
3982 DUMP_EVENT_STAT (TYPE_EXCEPTION
, TYPE_THROW_BT
);
3983 DUMP_EVENT_STAT (TYPE_EXCEPTION
, TYPE_CLAUSE
);
3985 DUMP_EVENT_STAT (TYPE_MONITOR
, TYPE_MONITOR_NO_BT
);
3986 DUMP_EVENT_STAT (TYPE_MONITOR
, TYPE_MONITOR_BT
);
3988 DUMP_EVENT_STAT (TYPE_HEAP
, TYPE_HEAP_START
);
3989 DUMP_EVENT_STAT (TYPE_HEAP
, TYPE_HEAP_END
);
3990 DUMP_EVENT_STAT (TYPE_HEAP
, TYPE_HEAP_OBJECT
);
3991 DUMP_EVENT_STAT (TYPE_HEAP
, TYPE_HEAP_ROOT
);
3993 DUMP_EVENT_STAT (TYPE_SAMPLE
, TYPE_SAMPLE_HIT
);
3994 DUMP_EVENT_STAT (TYPE_SAMPLE
, TYPE_SAMPLE_USYM
);
3995 DUMP_EVENT_STAT (TYPE_SAMPLE
, TYPE_SAMPLE_UBIN
);
3996 DUMP_EVENT_STAT (TYPE_SAMPLE
, TYPE_SAMPLE_COUNTERS_DESC
);
3997 DUMP_EVENT_STAT (TYPE_SAMPLE
, TYPE_SAMPLE_COUNTERS
);
3999 DUMP_EVENT_STAT (TYPE_RUNTIME
, TYPE_JITHELPER
);
4001 DUMP_EVENT_STAT (TYPE_COVERAGE
, TYPE_COVERAGE_ASSEMBLY
);
4002 DUMP_EVENT_STAT (TYPE_COVERAGE
, TYPE_COVERAGE_METHOD
);
4003 DUMP_EVENT_STAT (TYPE_COVERAGE
, TYPE_COVERAGE_STATEMENT
);
4004 DUMP_EVENT_STAT (TYPE_COVERAGE
, TYPE_COVERAGE_CLASS
);
4006 DUMP_EVENT_STAT (TYPE_META
, TYPE_SYNC_POINT
);
4012 flush_context (ProfContext
*ctx
)
4014 ThreadContext
*thread
;
4015 /* FIXME: sometimes there are leftovers: indagate */
4016 for (thread
= ctx
->threads
; thread
; thread
= thread
->next
) {
4017 while (thread
->stack_id
) {
4019 fprintf (outfile
, "thread %p has %d items on stack\n", (void*)thread
->thread_id
, thread
->stack_id
);
4020 pop_method (thread
, thread
->stack
[thread
->stack_id
- 1], thread
->last_time
);
4025 static const char *reports
= "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,heapshot,counters,coverage";
4028 match_option (const char *p
, const char *opt
)
4030 int len
= strlen (opt
);
4031 if (strncmp (p
, opt
, len
) == 0) {
4040 print_reports (ProfContext
*ctx
, const char *reps
, int parse_only
)
4044 for (p
= reps
; *p
; p
= opt
) {
4045 if ((opt
= match_option (p
, "header")) != p
) {
4050 if ((opt
= match_option (p
, "thread")) != p
) {
4055 if ((opt
= match_option (p
, "domain")) != p
) {
4060 if ((opt
= match_option (p
, "context")) != p
) {
4065 if ((opt
= match_option (p
, "gc")) != p
) {
4070 if ((opt
= match_option (p
, "jit")) != p
) {
4075 if ((opt
= match_option (p
, "alloc")) != p
) {
4077 dump_allocations ();
4080 if ((opt
= match_option (p
, "call")) != p
) {
4085 if ((opt
= match_option (p
, "metadata")) != p
) {
4090 if ((opt
= match_option (p
, "exception")) != p
) {
4095 if ((opt
= match_option (p
, "monitor")) != p
) {
4100 if ((opt
= match_option (p
, "heapshot")) != p
) {
4105 if ((opt
= match_option (p
, "sample")) != p
) {
4110 if ((opt
= match_option (p
, "counters")) != p
) {
4115 if ((opt
= match_option (p
, "coverage")) != p
) {
4120 if ((opt
= match_option (p
, "stats")) != p
) {
4131 add_find_spec (const char *p
)
4133 if (p
[0] == 'S' && p
[1] == ':') {
4135 find_size
= strtoul (p
+ 2, &vale
, 10);
4137 } else if (p
[0] == 'T' && p
[1] == ':') {
4147 printf ("Mono log profiler report version %d.%d\n", LOG_VERSION_MAJOR
, LOG_VERSION_MINOR
);
4148 printf ("Usage: mprof-report [OPTIONS] FILENAME\n");
4149 printf ("FILENAME can be '-' to read from standard input.\n");
4150 printf ("Options:\n");
4151 printf ("\t--help display this help\n");
4152 printf ("\t--out=FILE write to FILE instead of stdout\n");
4153 printf ("\t--traces collect and show backtraces\n");
4154 printf ("\t--maxframes=NUM limit backtraces to NUM entries\n");
4155 printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
4156 printf ("\t %s\n", reports
);
4157 printf ("\t--method-sort=MODE sort methods according to MODE: total, self, calls\n");
4158 printf ("\t--alloc-sort=MODE sort allocations according to MODE: bytes, count\n");
4159 printf ("\t--counters-sort=MODE sort counters according to MODE: time, category\n");
4160 printf ("\t only accessible in verbose mode\n");
4161 printf ("\t--track=OB1[,OB2...] track what happens to objects OBJ1, O2 etc.\n");
4162 printf ("\t--find=FINDSPEC find and track objects matching FINFSPEC, where FINDSPEC is:\n");
4163 printf ("\t S:minimum_size or T:partial_name\n");
4164 printf ("\t--thread=THREADID consider just the data for thread THREADID\n");
4165 printf ("\t--time=FROM-TO consider data FROM seconds from startup up to TO seconds\n");
4166 printf ("\t--verbose increase verbosity level\n");
4167 printf ("\t--debug display decoding debug info for mprof-report devs\n");
4168 printf ("\t--coverage-out=FILE write the coverage info to FILE as XML\n");
4172 main (int argc
, char *argv
[])
4177 for (i
= 1; i
< argc
; ++i
) {
4178 if (strcmp ("--debug", argv
[i
]) == 0) {
4180 } else if (strcmp ("--help", argv
[i
]) == 0) {
4183 } else if (strncmp ("--alloc-sort=", argv
[i
], 13) == 0) {
4184 const char *val
= argv
[i
] + 13;
4185 if (strcmp (val
, "bytes") == 0) {
4186 alloc_sort_mode
= ALLOC_SORT_BYTES
;
4187 } else if (strcmp (val
, "count") == 0) {
4188 alloc_sort_mode
= ALLOC_SORT_COUNT
;
4193 } else if (strncmp ("--method-sort=", argv
[i
], 14) == 0) {
4194 const char *val
= argv
[i
] + 14;
4195 if (strcmp (val
, "total") == 0) {
4196 method_sort_mode
= METHOD_SORT_TOTAL
;
4197 } else if (strcmp (val
, "self") == 0) {
4198 method_sort_mode
= METHOD_SORT_SELF
;
4199 } else if (strcmp (val
, "calls") == 0) {
4200 method_sort_mode
= METHOD_SORT_CALLS
;
4205 } else if (strncmp ("--counters-sort=", argv
[i
], 16) == 0) {
4206 const char *val
= argv
[i
] + 16;
4207 if (strcmp (val
, "time") == 0) {
4208 counters_sort_mode
= COUNTERS_SORT_TIME
;
4209 } else if (strcmp (val
, "category") == 0) {
4210 counters_sort_mode
= COUNTERS_SORT_CATEGORY
;
4215 } else if (strncmp ("--reports=", argv
[i
], 10) == 0) {
4216 const char *val
= argv
[i
] + 10;
4217 if (!print_reports (NULL
, val
, 1)) {
4222 } else if (strncmp ("--out=", argv
[i
], 6) == 0) {
4223 const char *val
= argv
[i
] + 6;
4224 outfile
= fopen (val
, "w");
4226 printf ("Cannot open output file: %s\n", val
);
4229 } else if (strncmp ("--maxframes=", argv
[i
], 12) == 0) {
4230 const char *val
= argv
[i
] + 12;
4232 trace_max
= strtoul (val
, &vale
, 10);
4233 } else if (strncmp ("--find=", argv
[i
], 7) == 0) {
4234 const char *val
= argv
[i
] + 7;
4235 if (!add_find_spec (val
)) {
4239 } else if (strncmp ("--track=", argv
[i
], 8) == 0) {
4240 const char *val
= argv
[i
] + 8;
4243 uintptr_t tracked_obj
;
4248 tracked_obj
= strtoul (val
, &vale
, 0);
4249 found_object (tracked_obj
);
4252 } else if (strncmp ("--thread=", argv
[i
], 9) == 0) {
4253 const char *val
= argv
[i
] + 9;
4255 thread_filter
= strtoul (val
, &vale
, 0);
4256 } else if (strncmp ("--time=", argv
[i
], 7) == 0) {
4257 char *val
= pstrdup (argv
[i
] + 7);
4258 double from_secs
, to_secs
;
4259 char *top
= strchr (val
, '-');
4265 from_secs
= atof (val
);
4266 to_secs
= atof (top
);
4268 if (from_secs
> to_secs
) {
4272 time_from
= from_secs
* 1000000000;
4273 time_to
= to_secs
* 1000000000;
4274 use_time_filter
= 1;
4275 } else if (strcmp ("--verbose", argv
[i
]) == 0) {
4277 } else if (strcmp ("--traces", argv
[i
]) == 0) {
4280 } else if (strncmp ("--coverage-out=", argv
[i
], 15) == 0) {
4281 const char *val
= argv
[i
] + 15;
4282 coverage_outfile
= fopen (val
, "w");
4283 if (!coverage_outfile
) {
4284 printf ("Cannot open output file: %s\n", val
);
4295 ctx
= load_file (argv
[i
]);
4297 printf ("Not a log profiler data file (or unsupported version).\n");
4300 while (decode_buffer (ctx
));
4301 flush_context (ctx
);
4302 if (num_tracked_objects
)
4304 print_reports (ctx
, reports
, 0);