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.
18 #if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
23 #if defined (HAVE_SYS_ZLIB)
27 #include <mono/metadata/profiler.h>
28 #include <mono/metadata/object.h>
29 #include <mono/metadata/debug-helpers.h>
30 #include <mono/utils/mono-counters.h>
32 #define HASH_SIZE 9371
33 #define SMALL_HASH_SIZE 31
35 /* Version < 14 root type enum */
38 MONO_PROFILER_GC_ROOT_PINNING
= 1 << 8,
39 MONO_PROFILER_GC_ROOT_WEAKREF
= 2 << 8,
40 MONO_PROFILER_GC_ROOT_INTERIOR
= 4 << 8,
42 /* Lower 2 bytes (flags). */
43 MONO_PROFILER_GC_ROOT_STACK
= 1 << 0,
44 MONO_PROFILER_GC_ROOT_FINALIZER
= 1 << 1,
45 MONO_PROFILER_GC_ROOT_HANDLE
= 1 << 2,
46 MONO_PROFILER_GC_ROOT_OTHER
= 1 << 3,
47 MONO_PROFILER_GC_ROOT_MISC
= 1 << 4,
49 MONO_PROFILER_GC_ROOT_TYPEMASK
= 0xff,
50 } MonoProfilerGCRootType
;
53 static int collect_traces
= 0;
54 static int show_traces
= 0;
55 static int trace_max
= 6;
56 static int verbose
= 0;
57 static uintptr_t *tracked_objects
= 0;
58 static int num_tracked_objects
= 0;
59 static uintptr_t thread_filter
= 0;
60 static uint64_t find_size
= 0;
61 static const char* find_name
= NULL
;
62 static uint64_t time_from
= 0;
63 static uint64_t time_to
= 0xffffffffffffffffULL
;
64 static int use_time_filter
= 0;
65 static uint64_t startup_time
= 0;
66 static FILE* outfile
= NULL
;
69 read_int16 (unsigned char *p
)
77 read_int32 (unsigned char *p
)
81 value
|= (*p
++) << 16;
82 value
|= (uint32_t)(*p
++) << 24;
87 read_int64 (unsigned char *p
)
89 uint64_t value
= *p
++;
91 value
|= (*p
++) << 16;
92 value
|= (uint64_t)(*p
++) << 24;
93 value
|= (uint64_t)(*p
++) << 32;
94 value
|= (uint64_t)(*p
++) << 40;
95 value
|= (uint64_t)(*p
++) << 48;
96 value
|= (uint64_t)(*p
++) << 54;
101 pstrdup (const char *s
)
103 int len
= strlen (s
) + 1;
104 char *p
= (char *) g_malloc (len
);
109 typedef struct _CounterValue CounterValue
;
110 struct _CounterValue
{
112 unsigned char *buffer
;
116 typedef struct _Counter Counter
;
124 CounterValue
*values
;
125 CounterValue
*values_last
;
128 typedef struct _CounterList CounterList
;
129 struct _CounterList
{
134 typedef struct _CounterSection CounterSection
;
135 struct _CounterSection
{
137 CounterList
*counters
;
138 CounterList
*counters_last
;
139 CounterSection
*next
;
142 typedef struct _CounterTimestamp CounterTimestamp
;
143 struct _CounterTimestamp
{
145 CounterSection
*sections
;
146 CounterSection
*sections_last
;
147 CounterTimestamp
*next
;
150 static CounterList
*counters
= NULL
;
151 static CounterSection
*counters_sections
= NULL
;
152 static CounterTimestamp
*counters_timestamps
= NULL
;
156 COUNTERS_SORT_CATEGORY
159 static int counters_sort_mode
= COUNTERS_SORT_TIME
;
162 add_counter_to_section (Counter
*counter
)
164 CounterSection
*csection
, *s
;
167 clist
= (CounterList
*) g_calloc (1, sizeof (CounterList
));
168 clist
->counter
= counter
;
170 for (csection
= counters_sections
; csection
; csection
= csection
->next
) {
171 if (strcmp (csection
->value
, counter
->section
) == 0) {
172 /* If section exist */
173 if (!csection
->counters
)
174 csection
->counters
= clist
;
176 csection
->counters_last
->next
= clist
;
177 csection
->counters_last
= clist
;
182 /* If section does not exist */
183 csection
= (CounterSection
*) g_calloc (1, sizeof (CounterSection
));
184 csection
->value
= counter
->section
;
185 csection
->counters
= clist
;
186 csection
->counters_last
= clist
;
188 if (!counters_sections
) {
189 counters_sections
= csection
;
191 s
= counters_sections
;
199 add_counter (const char *section
, const char *name
, int type
, int unit
, int variance
, int index
)
201 CounterList
*list
, *l
;
204 for (list
= counters
; list
; list
= list
->next
)
205 if (list
->counter
->index
== index
)
208 counter
= (Counter
*) g_calloc (1, sizeof (Counter
));
209 counter
->section
= section
;
210 counter
->name
= name
;
211 counter
->type
= type
;
212 counter
->unit
= unit
;
213 counter
->variance
= variance
;
214 counter
->index
= index
;
216 list
= (CounterList
*) g_calloc (1, sizeof (CounterList
));
217 list
->counter
= counter
;
228 if (counters_sort_mode
== COUNTERS_SORT_CATEGORY
|| !verbose
)
229 add_counter_to_section (counter
);
233 add_counter_to_timestamp (uint64_t timestamp
, Counter
*counter
)
235 CounterTimestamp
*ctimestamp
, *t
;
236 CounterSection
*csection
;
239 clist
= (CounterList
*) g_calloc (1, sizeof (CounterList
));
240 clist
->counter
= counter
;
242 for (ctimestamp
= counters_timestamps
; ctimestamp
; ctimestamp
= ctimestamp
->next
) {
243 if (ctimestamp
->value
== timestamp
) {
244 for (csection
= ctimestamp
->sections
; csection
; csection
= csection
->next
) {
245 if (strcmp (csection
->value
, counter
->section
) == 0) {
246 /* if timestamp exist and section exist */
247 if (!csection
->counters
)
248 csection
->counters
= clist
;
250 csection
->counters_last
->next
= clist
;
251 csection
->counters_last
= clist
;
256 /* if timestamp exist and section does not exist */
257 csection
= (CounterSection
*) g_calloc (1, sizeof (CounterSection
));
258 csection
->value
= counter
->section
;
259 csection
->counters
= clist
;
260 csection
->counters_last
= clist
;
262 if (!ctimestamp
->sections
)
263 ctimestamp
->sections
= csection
;
265 ctimestamp
->sections_last
->next
= csection
;
266 ctimestamp
->sections_last
= csection
;
271 /* If timestamp do not exist and section does not exist */
272 csection
= (CounterSection
*) g_calloc (1, sizeof (CounterSection
));
273 csection
->value
= counter
->section
;
274 csection
->counters
= clist
;
275 csection
->counters_last
= clist
;
277 ctimestamp
= (CounterTimestamp
*) g_calloc (1, sizeof (CounterTimestamp
));
278 ctimestamp
->value
= timestamp
;
279 ctimestamp
->sections
= csection
;
280 ctimestamp
->sections_last
= csection
;
282 if (!counters_timestamps
) {
283 counters_timestamps
= ctimestamp
;
285 t
= counters_timestamps
;
288 t
->next
= ctimestamp
;
293 add_counter_value (int index
, CounterValue
*value
)
297 for (list
= counters
; list
; list
= list
->next
) {
298 if (list
->counter
->index
== index
) {
299 if (!list
->counter
->values
)
300 list
->counter
->values
= value
;
302 list
->counter
->values_last
->next
= value
;
303 list
->counter
->values_last
= value
;
305 if (counters_sort_mode
== COUNTERS_SORT_TIME
)
306 add_counter_to_timestamp (value
->timestamp
, list
->counter
);
314 section_name (int section
)
317 case MONO_COUNTER_JIT
: return "Mono JIT";
318 case MONO_COUNTER_GC
: return "Mono GC";
319 case MONO_COUNTER_METADATA
: return "Mono Metadata";
320 case MONO_COUNTER_GENERICS
: return "Mono Generics";
321 case MONO_COUNTER_SECURITY
: return "Mono Security";
322 case MONO_COUNTER_RUNTIME
: return "Mono Runtime";
323 case MONO_COUNTER_SYSTEM
: return "Mono System";
324 case MONO_COUNTER_PROFILER
: return "Mono Profiler";
325 default: return "<unknown>";
333 case MONO_COUNTER_INT
: return "Int";
334 case MONO_COUNTER_UINT
: return "UInt";
335 case MONO_COUNTER_WORD
: return "Word";
336 case MONO_COUNTER_LONG
: return "Long";
337 case MONO_COUNTER_ULONG
: return "ULong";
338 case MONO_COUNTER_DOUBLE
: return "Double";
339 case MONO_COUNTER_STRING
: return "String";
340 case MONO_COUNTER_TIME_INTERVAL
: return "Time Interval";
341 default: return "<unknown>";
349 case MONO_COUNTER_RAW
: return "Raw";
350 case MONO_COUNTER_BYTES
: return "Bytes";
351 case MONO_COUNTER_TIME
: return "Time";
352 case MONO_COUNTER_COUNT
: return "Count";
353 case MONO_COUNTER_PERCENTAGE
: return "Percentage";
354 default: return "<unknown>";
359 variance_name (int variance
)
362 case MONO_COUNTER_MONOTONIC
: return "Monotonic";
363 case MONO_COUNTER_CONSTANT
: return "Constant";
364 case MONO_COUNTER_VARIABLE
: return "Variable";
365 default: return "<unknown>";
370 dump_counters_value (Counter
*counter
, const char *key_format
, const char *key
, void *value
)
375 snprintf (format
, sizeof (format
), "%s : %%s\n", key_format
);
376 fprintf (outfile
, format
, key
, "<null>");
378 switch (counter
->type
) {
379 case MONO_COUNTER_INT
:
380 #if SIZEOF_VOID_P == 4
381 case MONO_COUNTER_WORD
:
383 snprintf (format
, sizeof (format
), "%s : %%d\n", key_format
);
384 fprintf (outfile
, format
, key
, *(int32_t*)value
);
386 case MONO_COUNTER_UINT
:
387 snprintf (format
, sizeof (format
), "%s : %%u\n", key_format
);
388 fprintf (outfile
, format
, key
, *(uint32_t*)value
);
390 case MONO_COUNTER_LONG
:
391 #if SIZEOF_VOID_P == 8
392 case MONO_COUNTER_WORD
:
394 case MONO_COUNTER_TIME_INTERVAL
:
395 if (counter
->type
== MONO_COUNTER_LONG
&& counter
->unit
== MONO_COUNTER_TIME
) {
396 snprintf (format
, sizeof (format
), "%s : %%0.3fms\n", key_format
);
397 fprintf (outfile
, format
, key
, (double)*(int64_t*)value
/ 10000.0);
398 } else if (counter
->type
== MONO_COUNTER_TIME_INTERVAL
) {
399 snprintf (format
, sizeof (format
), "%s : %%0.3fms\n", key_format
);
400 fprintf (outfile
, format
, key
, (double)*(int64_t*)value
/ 1000.0);
402 snprintf (format
, sizeof (format
), "%s : %%u\n", key_format
);
403 fprintf (outfile
, format
, key
, *(int64_t*)value
);
406 case MONO_COUNTER_ULONG
:
407 snprintf (format
, sizeof (format
), "%s : %%llu\n", key_format
);
408 fprintf (outfile
, format
, key
, *(uint64_t*)value
);
410 case MONO_COUNTER_DOUBLE
:
411 snprintf (format
, sizeof (format
), "%s : %%f\n", key_format
);
412 fprintf (outfile
, format
, key
, *(double*)value
);
414 case MONO_COUNTER_STRING
:
415 snprintf (format
, sizeof (format
), "%s : %%s\n", key_format
);
416 fprintf (outfile
, format
, key
, *(char*)value
);
426 CounterValue
*cvalue
;
427 CounterTimestamp
*ctimestamp
;
428 CounterSection
*csection
;
430 char strtimestamp
[17];
431 int i
, section_printed
;
433 fprintf (outfile
, "\nCounters:\n");
436 char counters_to_print
[][64] = {
438 "Methods JITted using mono JIT",
439 "Methods JITted using LLVM",
440 "Total time spent JITting (sec)",
448 "CPU Load Average - 1min",
449 "CPU Load Average - 5min",
450 "CPU Load Average - 15min",
454 for (csection
= counters_sections
; csection
; csection
= csection
->next
) {
457 for (clist
= csection
->counters
; clist
; clist
= clist
->next
) {
458 counter
= clist
->counter
;
459 if (!counter
->values_last
)
462 for (i
= 0; counters_to_print
[i
][0] != 0; i
++) {
463 if (strcmp (counters_to_print
[i
], counter
->name
) == 0) {
464 if (!section_printed
) {
465 fprintf (outfile
, "\t%s:\n", csection
->value
);
469 dump_counters_value (counter
, "\t\t%-30s", counter
->name
, counter
->values_last
->buffer
);
475 } else if (counters_sort_mode
== COUNTERS_SORT_TIME
) {
476 for (ctimestamp
= counters_timestamps
; ctimestamp
; ctimestamp
= ctimestamp
->next
) {
477 fprintf (outfile
, "\t%llu:%02llu:%02llu:%02llu.%03llu:\n",
478 (unsigned long long) (ctimestamp
->value
/ 1000 / 60 / 60 / 24 % 1000),
479 (unsigned long long) (ctimestamp
->value
/ 1000 / 60 / 60 % 24),
480 (unsigned long long) (ctimestamp
->value
/ 1000 / 60 % 60),
481 (unsigned long long) (ctimestamp
->value
/ 1000 % 60),
482 (unsigned long long) (ctimestamp
->value
% 1000));
484 for (csection
= ctimestamp
->sections
; csection
; csection
= csection
->next
) {
485 fprintf (outfile
, "\t\t%s:\n", csection
->value
);
487 for (clist
= csection
->counters
; clist
; clist
= clist
->next
) {
488 counter
= clist
->counter
;
489 for (cvalue
= counter
->values
; cvalue
; cvalue
= cvalue
->next
) {
490 if (cvalue
->timestamp
!= ctimestamp
->value
)
493 dump_counters_value (counter
, "\t\t\t%-30s", counter
->name
, cvalue
->buffer
);
498 } else if (counters_sort_mode
== COUNTERS_SORT_CATEGORY
) {
499 for (csection
= counters_sections
; csection
; csection
= csection
->next
) {
500 fprintf (outfile
, "\t%s:\n", csection
->value
);
502 for (clist
= csection
->counters
; clist
; clist
= clist
->next
) {
503 counter
= clist
->counter
;
504 fprintf (outfile
, "\t\t%s: [type: %s, unit: %s, variance: %s]\n",
505 counter
->name
, type_name (counter
->type
), unit_name (counter
->unit
), variance_name (counter
->variance
));
507 for (cvalue
= counter
->values
; cvalue
; cvalue
= cvalue
->next
) {
508 snprintf (strtimestamp
, sizeof (strtimestamp
), "%llu:%02llu:%02llu:%02llu.%03llu",
509 (unsigned long long) (cvalue
->timestamp
/ 1000 / 60 / 60 / 24 % 1000),
510 (unsigned long long) (cvalue
->timestamp
/ 1000 / 60 / 60 % 24),
511 (unsigned long long) (cvalue
->timestamp
/ 1000 / 60 % 60),
512 (unsigned long long) (cvalue
->timestamp
/ 1000 % 60),
513 (unsigned long long) (cvalue
->timestamp
% 1000));
515 dump_counters_value (counter
, "\t\t\t%s", strtimestamp
, cvalue
->buffer
);
522 static int num_images
;
523 typedef struct _ImageDesc ImageDesc
;
530 static ImageDesc
* image_hash
[SMALL_HASH_SIZE
] = {0};
533 add_image (intptr_t image
, char *name
)
535 int slot
= ((image
>> 2) & 0xffff) % SMALL_HASH_SIZE
;
536 ImageDesc
*cd
= (ImageDesc
*) g_malloc (sizeof (ImageDesc
));
538 cd
->filename
= pstrdup (name
);
539 cd
->next
= image_hash
[slot
];
540 image_hash
[slot
] = cd
;
544 static int num_assemblies
;
546 typedef struct _AssemblyDesc AssemblyDesc
;
547 struct _AssemblyDesc
{
553 static AssemblyDesc
* assembly_hash
[SMALL_HASH_SIZE
] = {0};
556 add_assembly (intptr_t assembly
, char *name
)
558 int slot
= ((assembly
>> 2) & 0xffff) % SMALL_HASH_SIZE
;
559 AssemblyDesc
*cd
= (AssemblyDesc
*) g_malloc (sizeof (AssemblyDesc
));
560 cd
->assembly
= assembly
;
561 cd
->asmname
= pstrdup (name
);
562 cd
->next
= assembly_hash
[slot
];
563 assembly_hash
[slot
] = cd
;
567 typedef struct _BackTrace BackTrace
;
579 typedef struct _ClassDesc ClassDesc
;
589 static ClassDesc
* class_hash
[HASH_SIZE
] = {0};
590 static int num_classes
= 0;
593 add_class (intptr_t klass
, const char *name
)
595 int slot
= ((klass
>> 2) & 0xffff) % HASH_SIZE
;
597 cd
= class_hash
[slot
];
598 while (cd
&& cd
->klass
!= klass
)
600 /* we resolved an unknown class (unless we had the code unloaded) */
602 /*printf ("resolved unknown: %s\n", name);*/
604 cd
->name
= pstrdup (name
);
607 cd
= (ClassDesc
*) g_calloc (sizeof (ClassDesc
), 1);
609 cd
->name
= pstrdup (name
);
610 cd
->next
= class_hash
[slot
];
613 cd
->traces
.count
= 0;
615 cd
->traces
.traces
= NULL
;
616 class_hash
[slot
] = cd
;
622 lookup_class (intptr_t klass
)
624 int slot
= ((klass
>> 2) & 0xffff) % HASH_SIZE
;
625 ClassDesc
*cd
= class_hash
[slot
];
626 while (cd
&& cd
->klass
!= klass
)
630 snprintf (buf
, sizeof (buf
), "unresolved class %p", (void*)klass
);
631 return add_class (klass
, buf
);
636 typedef struct _VTableDesc VTableDesc
;
643 static VTableDesc
* vtable_hash
[HASH_SIZE
] = {0};
646 add_vtable (intptr_t vtable
, intptr_t klass
)
648 int slot
= ((vtable
>> 2) & 0xffff) % HASH_SIZE
;
650 VTableDesc
*vt
= vtable_hash
[slot
];
652 while (vt
&& vt
->vtable
!= vtable
)
658 vt
= (VTableDesc
*) g_calloc (sizeof (VTableDesc
), 1);
661 vt
->klass
= lookup_class (klass
);
662 vt
->next
= vtable_hash
[slot
];
664 vtable_hash
[slot
] = vt
;
670 lookup_vtable (intptr_t vtable
)
672 int slot
= ((vtable
>> 2) & 0xffff) % HASH_SIZE
;
673 VTableDesc
*vt
= vtable_hash
[slot
];
675 while (vt
&& vt
->vtable
!= vtable
)
679 return add_vtable (vtable
, 0);
684 typedef struct _MethodDesc MethodDesc
;
693 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 */
696 uint64_t callee_time
;
701 static MethodDesc
* method_hash
[HASH_SIZE
] = {0};
702 static int num_methods
= 0;
705 add_method (intptr_t method
, const char *name
, intptr_t code
, int len
)
707 int slot
= ((method
>> 2) & 0xffff) % HASH_SIZE
;
709 cd
= method_hash
[slot
];
710 while (cd
&& cd
->method
!= method
)
712 /* we resolved an unknown method (unless we had the code unloaded) */
716 /*printf ("resolved unknown: %s\n", name);*/
718 cd
->name
= pstrdup (name
);
721 cd
= (MethodDesc
*) g_calloc (sizeof (MethodDesc
), 1);
723 cd
->name
= pstrdup (name
);
728 cd
->traces
.count
= 0;
730 cd
->traces
.traces
= NULL
;
731 cd
->next
= method_hash
[slot
];
732 method_hash
[slot
] = cd
;
738 lookup_method (intptr_t method
)
740 int slot
= ((method
>> 2) & 0xffff) % HASH_SIZE
;
741 MethodDesc
*cd
= method_hash
[slot
];
742 while (cd
&& cd
->method
!= method
)
746 snprintf (buf
, sizeof (buf
), "unknown method %p", (void*)method
);
747 return add_method (method
, buf
, 0, 0);
752 static int num_stat_samples
= 0;
753 static int size_stat_samples
= 0;
754 uintptr_t *stat_samples
= NULL
;
755 int *stat_sample_desc
= NULL
;
758 add_stat_sample (int type
, uintptr_t ip
) {
759 if (num_stat_samples
== size_stat_samples
) {
760 size_stat_samples
*= 2;
761 if (!size_stat_samples
)
762 size_stat_samples
= 32;
763 stat_samples
= (uintptr_t *) g_realloc (stat_samples
, size_stat_samples
* sizeof (uintptr_t));
764 stat_sample_desc
= (int *) g_realloc (stat_sample_desc
, size_stat_samples
* sizeof (int));
766 stat_samples
[num_stat_samples
] = ip
;
767 stat_sample_desc
[num_stat_samples
++] = type
;
771 lookup_method_by_ip (uintptr_t ip
)
776 for (i
= 0; i
< HASH_SIZE
; ++i
) {
779 //printf ("checking %p against %p-%p\n", (void*)ip, (void*)(m->code), (void*)(m->code + m->len));
780 if (ip
>= (uintptr_t)m
->code
&& ip
< (uintptr_t)m
->code
+ m
->len
) {
790 compare_method_samples (const void *a
, const void *b
)
792 MethodDesc
*const *A
= (MethodDesc
*const *)a
;
793 MethodDesc
*const *B
= (MethodDesc
*const *)b
;
794 if ((*A
)->sample_hits
== (*B
)->sample_hits
)
796 if ((*B
)->sample_hits
< (*A
)->sample_hits
)
801 typedef struct _UnmanagedSymbol UnmanagedSymbol
;
802 struct _UnmanagedSymbol
{
803 UnmanagedSymbol
*parent
;
808 uintptr_t sample_hits
;
811 static UnmanagedSymbol
**usymbols
= NULL
;
812 static int usymbols_size
= 0;
813 static int usymbols_num
= 0;
816 compare_usymbol_addr (const void *a
, const void *b
)
818 UnmanagedSymbol
*const *A
= (UnmanagedSymbol
*const *)a
;
819 UnmanagedSymbol
*const *B
= (UnmanagedSymbol
*const *)b
;
820 if ((*B
)->addr
== (*A
)->addr
)
822 if ((*B
)->addr
> (*A
)->addr
)
828 compare_usymbol_samples (const void *a
, const void *b
)
830 UnmanagedSymbol
*const *A
= (UnmanagedSymbol
*const *)a
;
831 UnmanagedSymbol
*const *B
= (UnmanagedSymbol
*const *)b
;
832 if ((*B
)->sample_hits
== (*A
)->sample_hits
)
834 if ((*B
)->sample_hits
< (*A
)->sample_hits
)
840 add_unmanaged_symbol (uintptr_t addr
, char *name
, uintptr_t size
)
842 UnmanagedSymbol
*sym
;
843 if (usymbols_num
== usymbols_size
) {
844 int new_size
= usymbols_size
* 2;
847 usymbols
= (UnmanagedSymbol
**) g_realloc (usymbols
, sizeof (void*) * new_size
);
848 usymbols_size
= new_size
;
850 sym
= (UnmanagedSymbol
*) g_calloc (sizeof (UnmanagedSymbol
), 1);
854 usymbols
[usymbols_num
++] = sym
;
857 /* only valid after the symbols are sorted */
858 static UnmanagedSymbol
*
859 lookup_unmanaged_symbol (uintptr_t addr
)
861 int r
= usymbols_num
- 1;
863 UnmanagedSymbol
*sym
;
868 if (addr
== sym
->addr
)
870 if (addr
< sym
->addr
) {
872 } else if (addr
> sym
->addr
) {
877 if (last_best
>= 0 && (addr
- usymbols
[last_best
]->addr
) < 4096)
878 return usymbols
[last_best
];
882 /* we use the same structure for binaries */
883 static UnmanagedSymbol
**ubinaries
= NULL
;
884 static int ubinaries_size
= 0;
885 static int ubinaries_num
= 0;
888 add_unmanaged_binary (uintptr_t addr
, char *name
, uintptr_t size
)
890 UnmanagedSymbol
*sym
;
891 if (ubinaries_num
== ubinaries_size
) {
892 int new_size
= ubinaries_size
* 2;
895 ubinaries
= (UnmanagedSymbol
**) g_realloc (ubinaries
, sizeof (void*) * new_size
);
896 ubinaries_size
= new_size
;
898 sym
= (UnmanagedSymbol
*) g_calloc (sizeof (UnmanagedSymbol
), 1);
903 ubinaries
[ubinaries_num
++] = sym
;
906 static UnmanagedSymbol
*
907 lookup_unmanaged_binary (uintptr_t addr
)
910 for (i
= 0; i
< ubinaries_num
; ++i
) {
911 UnmanagedSymbol
*ubin
= ubinaries
[i
];
912 if (addr
>= ubin
->addr
&& addr
< ubin
->addr
+ ubin
->size
) {
919 // For backwards compatibility.
921 TYPE_SAMPLE_UBIN
= 2 << 4,
925 TYPE_COVERAGE_ASSEMBLY
= 0 << 4,
926 TYPE_COVERAGE_METHOD
= 1 << 4,
927 TYPE_COVERAGE_STATEMENT
= 2 << 4,
928 TYPE_COVERAGE_CLASS
= 3 << 4,
937 SAMPLE_BRANCH_MISSES
,
941 MONO_GC_EVENT_MARK_START
= 1,
942 MONO_GC_EVENT_MARK_END
= 2,
943 MONO_GC_EVENT_RECLAIM_START
= 3,
944 MONO_GC_EVENT_RECLAIM_END
= 4,
948 sample_type_name (int type
)
951 case SAMPLE_CYCLES
: return "cycles";
952 case SAMPLE_INSTRUCTIONS
: return "instructions retired";
953 case SAMPLE_CACHE_MISSES
: return "cache misses";
954 case SAMPLE_CACHE_REFS
: return "cache references";
955 case SAMPLE_BRANCHES
: return "executed branches";
956 case SAMPLE_BRANCH_MISSES
: return "unpredicted branches";
962 set_usym_parent (UnmanagedSymbol
** cachedus
, int count
)
965 for (i
= 0; i
< count
; ++i
) {
966 UnmanagedSymbol
*ubin
= lookup_unmanaged_binary (cachedus
[i
]->addr
);
967 if (ubin
== cachedus
[i
])
969 cachedus
[i
]->parent
= ubin
;
974 print_usym (UnmanagedSymbol
* um
)
977 fprintf (outfile
, "\t%6zd %6.2f %-36s in %s\n", um
->sample_hits
, um
->sample_hits
*100.0/num_stat_samples
, um
->name
, um
->parent
->name
);
979 fprintf (outfile
, "\t%6zd %6.2f %s\n", um
->sample_hits
, um
->sample_hits
*100.0/num_stat_samples
, um
->name
);
983 sym_percent (uintptr_t sample_hits
)
988 pc
= sample_hits
*100.0/num_stat_samples
;
996 int count
= 0, msize
= 0;
997 int unmanaged_hits
= 0;
998 int unresolved_hits
= 0;
999 MethodDesc
** cachedm
= NULL
;
1000 int ucount
= 0, usize
= 0;
1001 UnmanagedSymbol
** cachedus
= NULL
;
1002 if (!num_stat_samples
)
1004 qsort (usymbols
, usymbols_num
, sizeof (UnmanagedSymbol
*), compare_usymbol_addr
);
1005 for (i
= 0; i
< num_stat_samples
; ++i
) {
1006 MethodDesc
*m
= lookup_method_by_ip (stat_samples
[i
]);
1008 if (!m
->sample_hits
) {
1009 if (count
== msize
) {
1013 cachedm
= (MethodDesc
**) g_realloc (cachedm
, sizeof (void*) * msize
);
1015 cachedm
[count
++] = m
;
1019 UnmanagedSymbol
*usym
= lookup_unmanaged_symbol (stat_samples
[i
]);
1022 //printf ("unmanaged hit at %p\n", (void*)stat_samples [i]);
1023 usym
= lookup_unmanaged_binary (stat_samples
[i
]);
1026 if (!usym
->sample_hits
) {
1027 if (ucount
== usize
) {
1031 cachedus
= (UnmanagedSymbol
**) g_realloc (cachedus
, sizeof (void*) * usize
);
1033 cachedus
[ucount
++] = usym
;
1035 usym
->sample_hits
++;
1040 qsort (cachedm
, count
, sizeof (MethodDesc
*), compare_method_samples
);
1041 qsort (cachedus
, ucount
, sizeof (UnmanagedSymbol
*), compare_usymbol_samples
);
1042 set_usym_parent (cachedus
, ucount
);
1043 fprintf (outfile
, "\nStatistical samples summary\n");
1044 fprintf (outfile
, "\tSample type: %s\n", sample_type_name (stat_sample_desc
[0]));
1045 fprintf (outfile
, "\tUnmanaged hits: %6d (%4.1f%%)\n", unmanaged_hits
, (100.0*unmanaged_hits
)/num_stat_samples
);
1046 fprintf (outfile
, "\tManaged hits: %6d (%4.1f%%)\n", num_stat_samples
- unmanaged_hits
, (100.0*(num_stat_samples
-unmanaged_hits
))/num_stat_samples
);
1047 fprintf (outfile
, "\tUnresolved hits: %6d (%4.1f%%)\n", unresolved_hits
, (100.0*unresolved_hits
)/num_stat_samples
);
1048 fprintf (outfile
, "\t%6s %6s %s\n", "Hits", "%", "Method name");
1051 while (i
< count
|| u
< ucount
) {
1053 MethodDesc
*m
= cachedm
[i
];
1055 UnmanagedSymbol
*um
= cachedus
[u
];
1056 if (um
->sample_hits
> m
->sample_hits
) {
1057 if (!sym_percent (um
->sample_hits
))
1064 if (!sym_percent (m
->sample_hits
))
1066 fprintf (outfile
, "\t%6d %6.2f %s\n", m
->sample_hits
, m
->sample_hits
*100.0/num_stat_samples
, m
->name
);
1071 UnmanagedSymbol
*um
= cachedus
[u
];
1072 if (!sym_percent (um
->sample_hits
))
1081 typedef struct _HeapClassDesc HeapClassDesc
;
1083 HeapClassDesc
*klass
;
1087 struct _HeapClassDesc
{
1091 HeapClassRevRef
*rev_hash
;
1094 uintptr_t pinned_references
;
1095 uintptr_t root_references
;
1099 add_rev_class_hashed (HeapClassRevRef
*rev_hash
, uintptr_t size
, HeapClassDesc
*hklass
, uint64_t value
)
1102 uintptr_t start_pos
;
1103 start_pos
= (hklass
->klass
->klass
>> 2) % size
;
1104 assert (start_pos
< size
);
1107 if (rev_hash
[i
].klass
== hklass
) {
1108 rev_hash
[i
].count
+= value
;
1110 } else if (!rev_hash
[i
].klass
) {
1111 rev_hash
[i
].klass
= hklass
;
1112 rev_hash
[i
].count
+= value
;
1114 for (i
= 0; i
< size
; ++i
)
1115 if (rev_hash
[i
].klass
&& rev_hash
[i
].klass
->klass
== hklass
->klass
)
1117 assert (start_pos
== 1);
1123 } while (i
!= start_pos
);
1124 /* should not happen */
1125 printf ("failed revref store\n");
1130 add_heap_class_rev (HeapClassDesc
*from
, HeapClassDesc
*to
)
1133 if (to
->rev_count
* 2 >= to
->rev_hash_size
) {
1135 uintptr_t old_size
= to
->rev_hash_size
;
1136 to
->rev_hash_size
*= 2;
1137 if (to
->rev_hash_size
== 0)
1138 to
->rev_hash_size
= 4;
1139 n
= (HeapClassRevRef
*) g_calloc (sizeof (HeapClassRevRef
) * to
->rev_hash_size
, 1);
1140 for (i
= 0; i
< old_size
; ++i
) {
1141 if (to
->rev_hash
[i
].klass
)
1142 add_rev_class_hashed (n
, to
->rev_hash_size
, to
->rev_hash
[i
].klass
, to
->rev_hash
[i
].count
);
1145 g_free (to
->rev_hash
);
1148 to
->rev_count
+= add_rev_class_hashed (to
->rev_hash
, to
->rev_hash_size
, from
, 1);
1153 HeapClassDesc
*hklass
;
1158 typedef struct _HeapShot HeapShot
;
1164 HeapClassDesc
**class_hash
;
1165 HeapClassDesc
**sorted
;
1166 HeapObjectDesc
**objects_hash
;
1167 uintptr_t objects_count
;
1168 uintptr_t objects_hash_size
;
1169 uintptr_t num_roots
;
1171 uintptr_t *roots_extra
;
1175 static HeapShot
*heap_shots
= NULL
;
1176 static int num_heap_shots
= 0;
1179 new_heap_shot (uint64_t timestamp
)
1181 HeapShot
*hs
= (HeapShot
*) g_calloc (sizeof (HeapShot
), 1);
1183 hs
->class_hash
= (HeapClassDesc
**) g_calloc (sizeof (void*), hs
->hash_size
);
1184 hs
->timestamp
= timestamp
;
1186 hs
->next
= heap_shots
;
1191 static HeapClassDesc
*
1192 heap_class_lookup (HeapShot
*hs
, ClassDesc
*klass
)
1195 unsigned int start_pos
;
1196 start_pos
= ((uintptr_t)klass
->klass
>> 2) % hs
->hash_size
;
1199 HeapClassDesc
* cd
= hs
->class_hash
[i
];
1202 if (cd
->klass
== klass
)
1205 if (++i
== hs
->hash_size
)
1207 } while (i
!= start_pos
);
1212 add_heap_hashed (HeapClassDesc
**hash
, HeapClassDesc
**retv
, uintptr_t hsize
, ClassDesc
*klass
, uint64_t size
, uint64_t count
)
1215 uintptr_t start_pos
;
1216 start_pos
= ((uintptr_t)klass
->klass
>> 2) % hsize
;
1219 if (hash
[i
] && hash
[i
]->klass
== klass
) {
1220 hash
[i
]->total_size
+= size
;
1221 hash
[i
]->count
+= count
;
1224 } else if (!hash
[i
]) {
1229 hash
[i
] = (HeapClassDesc
*) g_calloc (sizeof (HeapClassDesc
), 1);
1230 hash
[i
]->klass
= klass
;
1231 hash
[i
]->total_size
+= size
;
1232 hash
[i
]->count
+= count
;
1239 } while (i
!= start_pos
);
1240 /* should not happen */
1241 printf ("failed heap class store\n");
1245 static HeapClassDesc
*
1246 add_heap_shot_class (HeapShot
*hs
, ClassDesc
*klass
, uint64_t size
)
1250 if (hs
->class_count
* 2 >= hs
->hash_size
) {
1252 int old_size
= hs
->hash_size
;
1254 if (hs
->hash_size
== 0)
1256 n
= (HeapClassDesc
**) g_calloc (sizeof (void*) * hs
->hash_size
, 1);
1257 for (i
= 0; i
< old_size
; ++i
) {
1258 res
= hs
->class_hash
[i
];
1259 if (hs
->class_hash
[i
])
1260 add_heap_hashed (n
, &res
, hs
->hash_size
, hs
->class_hash
[i
]->klass
, hs
->class_hash
[i
]->total_size
, hs
->class_hash
[i
]->count
);
1263 g_free (hs
->class_hash
);
1267 hs
->class_count
+= add_heap_hashed (hs
->class_hash
, &res
, hs
->hash_size
, klass
, size
, 1);
1268 //if (res->count == 1)
1269 // printf ("added heap class: %s\n", res->klass->name);
1273 static HeapObjectDesc
*
1274 alloc_heap_obj (uintptr_t objaddr
, HeapClassDesc
*hklass
, uintptr_t num_refs
)
1276 HeapObjectDesc
* ho
= (HeapObjectDesc
*) g_calloc (sizeof (HeapObjectDesc
) + num_refs
* sizeof (uintptr_t), 1);
1277 ho
->objaddr
= objaddr
;
1278 ho
->hklass
= hklass
;
1279 ho
->num_refs
= num_refs
;
1284 heap_shot_find_obj_slot (HeapShot
*hs
, uintptr_t objaddr
)
1287 uintptr_t start_pos
;
1288 HeapObjectDesc
**hash
= hs
->objects_hash
;
1289 if (hs
->objects_hash_size
== 0)
1291 start_pos
= ((uintptr_t)objaddr
>> 3) % hs
->objects_hash_size
;
1294 if (hash
[i
] && hash
[i
]->objaddr
== objaddr
) {
1296 } else if (!hash
[i
]) {
1300 if (++i
== hs
->objects_hash_size
)
1302 } while (i
!= start_pos
);
1303 /* should not happen */
1304 //printf ("failed heap obj slot\n");
1308 static HeapObjectDesc
*
1309 heap_shot_obj_add_refs (HeapShot
*hs
, uintptr_t objaddr
, uintptr_t num
, uintptr_t *ref_offset
)
1311 HeapObjectDesc
**hash
= hs
->objects_hash
;
1312 uintptr_t i
= heap_shot_find_obj_slot (hs
, objaddr
);
1314 HeapObjectDesc
* ho
= alloc_heap_obj (objaddr
, hash
[i
]->hklass
, hash
[i
]->num_refs
+ num
);
1315 *ref_offset
= hash
[i
]->num_refs
;
1316 memcpy (ho
->refs
, hash
[i
]->refs
, hash
[i
]->num_refs
* sizeof (uintptr_t));
1321 /* should not happen */
1322 printf ("failed heap obj update\n");
1328 add_heap_hashed_obj (HeapObjectDesc
**hash
, uintptr_t hsize
, HeapObjectDesc
*obj
)
1331 uintptr_t start_pos
;
1332 start_pos
= ((uintptr_t)obj
->objaddr
>> 3) % hsize
;
1335 if (hash
[i
] && hash
[i
]->objaddr
== obj
->objaddr
) {
1336 printf ("duplicate object!\n");
1338 } else if (!hash
[i
]) {
1345 } while (i
!= start_pos
);
1346 /* should not happen */
1347 printf ("failed heap obj store\n");
1352 add_heap_shot_obj (HeapShot
*hs
, HeapObjectDesc
*obj
)
1355 if (hs
->objects_count
* 2 >= hs
->objects_hash_size
) {
1357 uintptr_t old_size
= hs
->objects_hash_size
;
1358 hs
->objects_hash_size
*= 2;
1359 if (hs
->objects_hash_size
== 0)
1360 hs
->objects_hash_size
= 4;
1361 n
= (HeapObjectDesc
**) g_calloc (sizeof (void*) * hs
->objects_hash_size
, 1);
1362 for (i
= 0; i
< old_size
; ++i
) {
1363 if (hs
->objects_hash
[i
])
1364 add_heap_hashed_obj (n
, hs
->objects_hash_size
, hs
->objects_hash
[i
]);
1366 if (hs
->objects_hash
)
1367 g_free (hs
->objects_hash
);
1368 hs
->objects_hash
= n
;
1370 hs
->objects_count
+= add_heap_hashed_obj (hs
->objects_hash
, hs
->objects_hash_size
, obj
);
1374 heap_shot_resolve_reverse_refs (HeapShot
*hs
)
1377 for (i
= 0; i
< hs
->objects_hash_size
; ++i
) {
1379 HeapObjectDesc
*ho
= hs
->objects_hash
[i
];
1382 for (r
= 0; r
< ho
->num_refs
; ++r
) {
1383 uintptr_t oi
= heap_shot_find_obj_slot (hs
, ho
->refs
[r
]);
1384 add_heap_class_rev (ho
->hklass
, hs
->objects_hash
[oi
]->hklass
);
1390 #define MARK_BLACK 2
1393 heap_shot_mark_objects (HeapShot
*hs
)
1396 unsigned char *marks
;
1397 HeapObjectDesc
*obj
, *ref
;
1399 uintptr_t num_marked
= 0, num_unmarked
;
1400 for (i
= 0; i
< hs
->num_roots
; ++i
) {
1402 oi
= heap_shot_find_obj_slot (hs
, hs
->roots
[i
]);
1406 obj
= hs
->objects_hash
[oi
];
1408 if (hs
->roots_types
[i
] & MONO_PROFILER_GC_ROOT_PINNING
)
1409 cd
->pinned_references
++;
1410 cd
->root_references
++;
1414 /* consistency checks: it seems not all the objects are walked in the heap in some cases */
1415 marks
= (unsigned char *) g_calloc (hs
->objects_hash_size
, 1);
1418 for (i
= 0; i
< hs
->num_roots
; ++i
) {
1419 oi
= heap_shot_find_obj_slot (hs
, hs
->roots
[i
]);
1421 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
);
1424 obj
= hs
->objects_hash
[oi
];
1426 marks
[oi
] = obj
->num_refs
? MARK_GRAY
: MARK_BLACK
;
1431 while (marked_some
) {
1433 for (i
= 0; i
< hs
->objects_hash_size
; ++i
) {
1434 if (marks
[i
] != MARK_GRAY
)
1436 marks
[i
] = MARK_BLACK
;
1437 obj
= hs
->objects_hash
[i
];
1438 for (r
= 0; r
< obj
->num_refs
; ++r
) {
1439 oi
= heap_shot_find_obj_slot (hs
, obj
->refs
[r
]);
1441 fprintf (outfile
, "referenced obj %p not found in heap\n", (void*)obj
->refs
[r
]);
1444 ref
= hs
->objects_hash
[oi
];
1446 marks
[oi
] = ref
->num_refs
? MARK_GRAY
: MARK_BLACK
;
1454 for (i
= 0; i
< hs
->objects_hash_size
; ++i
) {
1455 if (hs
->objects_hash
[i
] && !marks
[i
]) {
1457 fprintf (outfile
, "object %p (%s) unmarked\n", (void*)hs
->objects_hash
[i
], hs
->objects_hash
[i
]->hklass
->klass
->name
);
1460 fprintf (outfile
, "Total unmarked: %zd/%zd\n", num_unmarked
, hs
->objects_count
);
1465 heap_shot_free_objects (HeapShot
*hs
)
1468 for (i
= 0; i
< hs
->objects_hash_size
; ++i
) {
1469 HeapObjectDesc
*ho
= hs
->objects_hash
[i
];
1473 if (hs
->objects_hash
)
1474 g_free (hs
->objects_hash
);
1475 hs
->objects_hash
= NULL
;
1476 hs
->objects_hash_size
= 0;
1477 hs
->objects_count
= 0;
1486 MethodDesc
*methods
[1];
1489 static BackTrace
*backtrace_hash
[HASH_SIZE
];
1490 static BackTrace
**backtraces
= NULL
;
1491 static int num_backtraces
= 0;
1492 static int next_backtrace
= 0;
1495 hash_backtrace (int count
, MethodDesc
**methods
)
1499 for (i
= 0; i
< count
; ++i
) {
1500 hash
= (hash
<< 5) - hash
+ methods
[i
]->method
;
1506 compare_backtrace (BackTrace
*bt
, int count
, MethodDesc
**methods
)
1509 if (bt
->count
!= count
)
1511 for (i
= 0; i
< count
; ++i
)
1512 if (methods
[i
] != bt
->methods
[i
])
1518 add_backtrace (int count
, MethodDesc
**methods
)
1520 int hash
= hash_backtrace (count
, methods
);
1521 int slot
= (hash
& 0xffff) % HASH_SIZE
;
1522 BackTrace
*bt
= backtrace_hash
[slot
];
1524 if (bt
->hash
== hash
&& compare_backtrace (bt
, count
, methods
))
1528 bt
= (BackTrace
*) g_malloc (sizeof (BackTrace
) + ((count
- 1) * sizeof (void*)));
1529 bt
->next
= backtrace_hash
[slot
];
1530 backtrace_hash
[slot
] = bt
;
1531 if (next_backtrace
== num_backtraces
) {
1532 num_backtraces
*= 2;
1533 if (!num_backtraces
)
1534 num_backtraces
= 16;
1535 backtraces
= (BackTrace
**) g_realloc (backtraces
, sizeof (void*) * num_backtraces
);
1537 bt
->id
= next_backtrace
++;
1538 backtraces
[bt
->id
] = bt
;
1541 for (slot
= 0; slot
< count
; ++slot
)
1542 bt
->methods
[slot
] = methods
[slot
];
1547 typedef struct _MonitorDesc MonitorDesc
;
1548 typedef struct _ThreadContext ThreadContext
;
1549 typedef struct _DomainContext DomainContext
;
1550 typedef struct _RemCtxContext RemCtxContext
;
1554 #if defined (HAVE_SYS_ZLIB)
1568 uint64_t startup_time
;
1569 ThreadContext
*threads
;
1570 ThreadContext
*current_thread
;
1571 DomainContext
*domains
;
1572 DomainContext
*current_domain
;
1573 RemCtxContext
*remctxs
;
1574 RemCtxContext
*current_remctx
;
1577 struct _ThreadContext
{
1578 ThreadContext
*next
;
1581 /* emulated stack */
1583 uint64_t *time_stack
;
1584 uint64_t *callee_time_stack
;
1586 uint64_t contention_start
;
1587 MonitorDesc
*monitor
;
1590 HeapShot
*current_heap_shot
;
1591 uintptr_t num_roots
;
1592 uintptr_t size_roots
;
1594 uintptr_t *roots_extra
;
1596 uint64_t gc_start_times
[3];
1599 struct _DomainContext
{
1600 DomainContext
*next
;
1602 const char *friendly_name
;
1605 struct _RemCtxContext
{
1606 RemCtxContext
*next
;
1612 ensure_buffer (ProfContext
*ctx
, int size
)
1614 if (ctx
->size
< size
) {
1615 ctx
->buf
= (unsigned char *) g_realloc (ctx
->buf
, size
);
1621 load_data (ProfContext
*ctx
, int size
)
1623 ensure_buffer (ctx
, size
);
1624 #if defined (HAVE_SYS_ZLIB)
1626 int r
= gzread (ctx
->gzfile
, ctx
->buf
, size
);
1628 return size
== 0? 1: 0;
1633 int r
= fread (ctx
->buf
, size
, 1, ctx
->file
);
1635 return size
== 0? 1: 0;
1640 static ThreadContext
*
1641 get_thread (ProfContext
*ctx
, intptr_t thread_id
)
1643 ThreadContext
*thread
;
1644 if (ctx
->current_thread
&& ctx
->current_thread
->thread_id
== thread_id
)
1645 return ctx
->current_thread
;
1646 thread
= ctx
->threads
;
1648 if (thread
->thread_id
== thread_id
) {
1651 thread
= thread
->next
;
1653 thread
= (ThreadContext
*) g_calloc (sizeof (ThreadContext
), 1);
1654 thread
->next
= ctx
->threads
;
1655 ctx
->threads
= thread
;
1656 thread
->thread_id
= thread_id
;
1657 thread
->last_time
= 0;
1658 thread
->stack_id
= 0;
1659 thread
->stack_size
= 32;
1660 thread
->stack
= (MethodDesc
**) g_malloc (thread
->stack_size
* sizeof (void*));
1661 thread
->time_stack
= (uint64_t *) g_malloc (thread
->stack_size
* sizeof (uint64_t));
1662 thread
->callee_time_stack
= (uint64_t *) g_malloc (thread
->stack_size
* sizeof (uint64_t));
1666 static DomainContext
*
1667 get_domain (ProfContext
*ctx
, intptr_t domain_id
)
1669 if (ctx
->current_domain
&& ctx
->current_domain
->domain_id
== domain_id
)
1670 return ctx
->current_domain
;
1672 DomainContext
*domain
= ctx
->domains
;
1675 if (domain
->domain_id
== domain_id
)
1678 domain
= domain
->next
;
1681 domain
= (DomainContext
*) g_calloc (sizeof (DomainContext
), 1);
1682 domain
->next
= ctx
->domains
;
1683 ctx
->domains
= domain
;
1684 domain
->domain_id
= domain_id
;
1689 static RemCtxContext
*
1690 get_remctx (ProfContext
*ctx
, intptr_t remctx_id
)
1692 if (ctx
->current_remctx
&& ctx
->current_remctx
->remctx_id
== remctx_id
)
1693 return ctx
->current_remctx
;
1695 RemCtxContext
*remctx
= ctx
->remctxs
;
1698 if (remctx
->remctx_id
== remctx_id
)
1701 remctx
= remctx
->next
;
1704 remctx
= (RemCtxContext
*) g_calloc (sizeof (RemCtxContext
), 1);
1705 remctx
->next
= ctx
->remctxs
;
1706 ctx
->remctxs
= remctx
;
1707 remctx
->remctx_id
= remctx_id
;
1712 static ThreadContext
*
1713 load_thread (ProfContext
*ctx
, intptr_t thread_id
)
1715 ThreadContext
*thread
= get_thread (ctx
, thread_id
);
1716 ctx
->current_thread
= thread
;
1721 ensure_thread_stack (ThreadContext
*thread
)
1723 if (thread
->stack_id
== thread
->stack_size
) {
1724 thread
->stack_size
*= 2;
1725 thread
->stack
= (MethodDesc
**) g_realloc (thread
->stack
, thread
->stack_size
* sizeof (void*));
1726 thread
->time_stack
= (uint64_t *) g_realloc (thread
->time_stack
, thread
->stack_size
* sizeof (uint64_t));
1727 thread
->callee_time_stack
= (uint64_t *) g_realloc (thread
->callee_time_stack
, thread
->stack_size
* sizeof (uint64_t));
1732 add_trace_hashed (CallContext
*traces
, int size
, BackTrace
*bt
, uint64_t value
)
1735 unsigned int start_pos
;
1736 start_pos
= bt
->hash
% size
;
1739 if (traces
[i
].bt
== bt
) {
1740 traces
[i
].count
+= value
;
1742 } else if (!traces
[i
].bt
) {
1744 traces
[i
].count
+= value
;
1750 } while (i
!= start_pos
);
1751 /* should not happen */
1752 printf ("failed trace store\n");
1757 add_trace_bt (BackTrace
*bt
, TraceDesc
*trace
, uint64_t value
)
1760 if (!collect_traces
)
1762 if (trace
->count
* 2 >= trace
->size
) {
1764 int old_size
= trace
->size
;
1766 if (trace
->size
== 0)
1768 n
= (CallContext
*) g_calloc (sizeof (CallContext
) * trace
->size
, 1);
1769 for (i
= 0; i
< old_size
; ++i
) {
1770 if (trace
->traces
[i
].bt
)
1771 add_trace_hashed (n
, trace
->size
, trace
->traces
[i
].bt
, trace
->traces
[i
].count
);
1774 g_free (trace
->traces
);
1777 trace
->count
+= add_trace_hashed (trace
->traces
, trace
->size
, bt
, value
);
1781 add_trace_thread (ThreadContext
*thread
, TraceDesc
*trace
, uint64_t value
)
1784 int count
= thread
->stack_id
;
1785 if (!collect_traces
)
1787 if (count
> trace_max
)
1789 bt
= add_backtrace (count
, thread
->stack
+ thread
->stack_id
- count
);
1790 add_trace_bt (bt
, trace
, value
);
1795 add_trace_methods (MethodDesc
**methods
, int count
, TraceDesc
*trace
, uint64_t value
)
1798 if (!collect_traces
)
1800 if (count
> trace_max
)
1802 bt
= add_backtrace (count
, methods
);
1803 add_trace_bt (bt
, trace
, value
);
1808 thread_add_root (ThreadContext
*ctx
, uintptr_t obj
, int root_type
, uintptr_t extra_info
)
1810 if (ctx
->num_roots
== ctx
->size_roots
) {
1811 int new_size
= ctx
->size_roots
* 2;
1814 ctx
->roots
= (uintptr_t *) g_realloc (ctx
->roots
, new_size
* sizeof (uintptr_t));
1815 ctx
->roots_extra
= (uintptr_t *) g_realloc (ctx
->roots_extra
, new_size
* sizeof (uintptr_t));
1816 ctx
->roots_types
= (int *) g_realloc (ctx
->roots_types
, new_size
* sizeof (int));
1817 ctx
->size_roots
= new_size
;
1819 ctx
->roots_types
[ctx
->num_roots
] = root_type
;
1820 ctx
->roots_extra
[ctx
->num_roots
] = extra_info
;
1821 ctx
->roots
[ctx
->num_roots
++] = obj
;
1825 compare_callc (const void *a
, const void *b
)
1827 const CallContext
*A
= (const CallContext
*)a
;
1828 const CallContext
*B
= (const CallContext
*)b
;
1829 if (B
->count
== A
->count
)
1831 if (B
->count
< A
->count
)
1837 sort_context_array (TraceDesc
* traces
)
1840 for (i
= 0, j
= 0; i
< traces
->size
; ++i
) {
1841 if (traces
->traces
[i
].bt
) {
1842 traces
->traces
[j
].bt
= traces
->traces
[i
].bt
;
1843 traces
->traces
[j
].count
= traces
->traces
[i
].count
;
1847 qsort (traces
->traces
, traces
->count
, sizeof (CallContext
), compare_callc
);
1851 push_method (ThreadContext
*thread
, MethodDesc
*method
, uint64_t timestamp
)
1853 ensure_thread_stack (thread
);
1854 thread
->time_stack
[thread
->stack_id
] = timestamp
;
1855 thread
->callee_time_stack
[thread
->stack_id
] = 0;
1856 thread
->stack
[thread
->stack_id
++] = method
;
1857 method
->recurse_count
++;
1861 pop_method (ThreadContext
*thread
, MethodDesc
*method
, uint64_t timestamp
)
1863 method
->recurse_count
--;
1864 if (thread
->stack_id
> 0 && thread
->stack
[thread
->stack_id
- 1] == method
) {
1868 if (timestamp
< thread
->time_stack
[thread
->stack_id
])
1869 fprintf (outfile
, "time went backwards for %s\n", method
->name
);
1870 tdiff
= timestamp
- thread
->time_stack
[thread
->stack_id
];
1871 if (thread
->callee_time_stack
[thread
->stack_id
] > tdiff
)
1872 fprintf (outfile
, "callee time bigger for %s\n", method
->name
);
1873 method
->self_time
+= tdiff
- thread
->callee_time_stack
[thread
->stack_id
];
1874 method
->callee_time
+= thread
->callee_time_stack
[thread
->stack_id
];
1875 if (thread
->stack_id
)
1876 thread
->callee_time_stack
[thread
->stack_id
- 1] += tdiff
;
1877 //fprintf (outfile, "method %s took %d\n", method->name, (int)(tdiff/1000));
1879 fprintf (outfile
, "unmatched leave at stack pos: %d for method %s\n", thread
->stack_id
, method
->name
);
1884 uint64_t total_time
;
1888 static GCDesc gc_info
[3];
1889 static uint64_t max_heap_size
;
1890 static uint64_t gc_object_moves
;
1891 static int gc_resizes
;
1898 TraceDesc destroy_traces
;
1900 static HandleInfo handle_info
[4];
1903 gc_event_name (int ev
)
1906 case MONO_GC_EVENT_START
: return "start";
1907 case MONO_GC_EVENT_MARK_START
: return "mark start";
1908 case MONO_GC_EVENT_MARK_END
: return "mark end";
1909 case MONO_GC_EVENT_RECLAIM_START
: return "reclaim start";
1910 case MONO_GC_EVENT_RECLAIM_END
: return "reclaim end";
1911 case MONO_GC_EVENT_END
: return "end";
1912 case MONO_GC_EVENT_PRE_STOP_WORLD
: return "pre stop";
1913 case MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED
: return "pre stop lock";
1914 case MONO_GC_EVENT_POST_STOP_WORLD
: return "post stop";
1915 case MONO_GC_EVENT_PRE_START_WORLD
: return "pre start";
1916 case MONO_GC_EVENT_POST_START_WORLD
: return "post start";
1917 case MONO_GC_EVENT_POST_START_WORLD_UNLOCKED
: return "post start unlock";
1924 sync_point_name (int type
)
1927 case SYNC_POINT_PERIODIC
: return "periodic";
1928 case SYNC_POINT_WORLD_STOP
: return "world stop";
1929 case SYNC_POINT_WORLD_START
: return "world start";
1935 static uint64_t clause_summary
[MONO_EXCEPTION_CLAUSE_FAULT
+ 1];
1936 static uint64_t throw_count
= 0;
1937 static TraceDesc exc_traces
;
1940 clause_name (int type
)
1943 case MONO_EXCEPTION_CLAUSE_NONE
: return "catch";
1944 case MONO_EXCEPTION_CLAUSE_FILTER
: return "filter";
1945 case MONO_EXCEPTION_CLAUSE_FINALLY
: return "finally";
1946 case MONO_EXCEPTION_CLAUSE_FAULT
: return "fault";
1947 default: return "invalid";
1951 static uint64_t monitor_contention
;
1952 static uint64_t monitor_failed
;
1953 static uint64_t monitor_acquired
;
1955 struct _MonitorDesc
{
1958 uintptr_t contentions
;
1960 uint64_t max_wait_time
;
1964 static MonitorDesc
* monitor_hash
[SMALL_HASH_SIZE
] = {0};
1965 static int num_monitors
= 0;
1968 lookup_monitor (uintptr_t objid
)
1970 int slot
= ((objid
>> 3) & 0xffff) % SMALL_HASH_SIZE
;
1971 MonitorDesc
*cd
= monitor_hash
[slot
];
1972 while (cd
&& cd
->objid
!= objid
)
1975 cd
= (MonitorDesc
*) g_calloc (sizeof (MonitorDesc
), 1);
1977 cd
->next
= monitor_hash
[slot
];
1978 monitor_hash
[slot
] = cd
;
1985 monitor_ev_name (int ev
)
1988 case MONO_PROFILER_MONITOR_CONTENTION
: return "contended";
1989 case MONO_PROFILER_MONITOR_DONE
: return "acquired";
1990 case MONO_PROFILER_MONITOR_FAIL
: return "not taken";
1991 default: return "invalid";
1996 get_handle_name (int htype
)
1999 case 0: return "weak";
2000 case 1: return "weaktrack";
2001 case 2: return "normal";
2002 case 3: return "pinned";
2003 default: return "unknown";
2008 get_root_name (int rtype
)
2010 switch (rtype
& MONO_PROFILER_GC_ROOT_TYPEMASK
) {
2011 case MONO_PROFILER_GC_ROOT_STACK
: return "stack";
2012 case MONO_PROFILER_GC_ROOT_FINALIZER
: return "finalizer";
2013 case MONO_PROFILER_GC_ROOT_HANDLE
: return "handle";
2014 case MONO_PROFILER_GC_ROOT_OTHER
: return "other";
2015 case MONO_PROFILER_GC_ROOT_MISC
: return "misc";
2016 default: return "unknown";
2021 decode_uleb128 (uint8_t *buf
, uint8_t **endbuf
)
2028 res
|= (((uint64_t) (b
& 0x7f)) << shift
);
2042 decode_sleb128 (uint8_t *buf
, uint8_t **endbuf
)
2052 res
= res
| (((intptr_t) (b
& 0x7f)) << shift
);
2056 if (shift
< sizeof (intptr_t) * 8 && (b
& 0x40))
2057 res
|= - ((intptr_t) 1 << shift
);
2069 decode_bt (ProfContext
*ctx
, MethodDesc
** sframes
, int *size
, unsigned char *p
, unsigned char **endp
, intptr_t ptr_base
, intptr_t *method_base
)
2071 MethodDesc
**frames
;
2073 if (ctx
->data_version
< 13)
2074 decode_uleb128 (p
, &p
); /* flags */
2075 int count
= decode_uleb128 (p
, &p
);
2077 frames
= (MethodDesc
**) g_malloc (count
* sizeof (void*));
2080 for (i
= 0; i
< count
; ++i
) {
2081 intptr_t ptrdiff
= decode_sleb128 (p
, &p
);
2082 if (ctx
->data_version
> 12) {
2083 *method_base
+= ptrdiff
;
2084 frames
[i
] = lookup_method (*method_base
);
2086 frames
[i
] = lookup_method (ptr_base
+ ptrdiff
);
2095 tracked_creation (uintptr_t obj
, ClassDesc
*cd
, uint64_t size
, BackTrace
*bt
, uint64_t timestamp
)
2098 for (i
= 0; i
< num_tracked_objects
; ++i
) {
2099 if (tracked_objects
[i
] != obj
)
2101 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);
2102 if (bt
&& bt
->count
) {
2104 for (k
= 0; k
< bt
->count
; ++k
)
2105 fprintf (outfile
, "\t%s\n", bt
->methods
[k
]->name
);
2111 track_handle (uintptr_t obj
, int htype
, uint32_t handle
, BackTrace
*bt
, uint64_t timestamp
)
2114 for (i
= 0; i
< num_tracked_objects
; ++i
) {
2115 if (tracked_objects
[i
] != obj
)
2117 fprintf (outfile
, "Object %p referenced from handle %u at %.3f secs.\n", (void*)obj
, handle
, (timestamp
- startup_time
) / 1000000000.0);
2118 if (bt
&& bt
->count
) {
2120 for (k
= 0; k
< bt
->count
; ++k
)
2121 fprintf (outfile
, "\t%s\n", bt
->methods
[k
]->name
);
2127 track_move (uintptr_t src
, uintptr_t dst
)
2130 for (i
= 0; i
< num_tracked_objects
; ++i
) {
2131 if (tracked_objects
[i
] == src
)
2132 fprintf (outfile
, "Object %p moved to %p\n", (void*)src
, (void*)dst
);
2133 else if (tracked_objects
[i
] == dst
)
2134 fprintf (outfile
, "Object %p moved from %p\n", (void*)dst
, (void*)src
);
2139 track_obj_reference (uintptr_t obj
, uintptr_t parent
, ClassDesc
*cd
)
2142 for (i
= 0; i
< num_tracked_objects
; ++i
) {
2143 if (tracked_objects
[i
] == obj
)
2144 fprintf (outfile
, "Object %p referenced from %p (%s).\n", (void*)obj
, (void*)parent
, cd
->name
);
2149 found_object (uintptr_t obj
)
2151 num_tracked_objects
++;
2152 tracked_objects
= (uintptr_t *) g_realloc (tracked_objects
, num_tracked_objects
* sizeof (tracked_objects
[0]));
2153 tracked_objects
[num_tracked_objects
- 1] = obj
;
2156 static int num_jit_helpers
= 0;
2157 static int jit_helpers_code_size
= 0;
2160 code_buffer_desc (int type
)
2163 case MONO_PROFILER_CODE_BUFFER_METHOD
:
2165 case MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE
:
2166 return "method trampoline";
2167 case MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE
:
2168 return "unbox trampoline";
2169 case MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE
:
2170 return "imt trampoline";
2171 case MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE
:
2172 return "generics trampoline";
2173 case MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
:
2174 return "specific trampoline";
2175 case MONO_PROFILER_CODE_BUFFER_HELPER
:
2176 return "misc helper";
2177 case MONO_PROFILER_CODE_BUFFER_MONITOR
:
2178 return "monitor/lock";
2179 case MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE
:
2180 return "delegate invoke";
2181 case MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING
:
2182 return "exception handling";
2184 return "unspecified";
2188 #define OBJ_ADDR(diff) ((obj_base + diff) << 3)
2189 #define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
2193 #define BUFFER_HEADER_SIZE 48
2196 int count
, min_size
, max_size
, bytes
;
2199 static int buffer_count
;
2200 static EventStat stats
[256];
2203 record_event_stats (int type
, int size
)
2205 ++stats
[type
].count
;
2206 if (!stats
[type
].min_size
)
2207 stats
[type
].min_size
= size
;
2208 stats
[type
].min_size
= MIN (stats
[type
].min_size
, size
);
2209 stats
[type
].max_size
= MAX (stats
[type
].max_size
, size
);
2210 stats
[type
].bytes
+= size
;
2214 decode_buffer (ProfContext
*ctx
)
2221 intptr_t method_base
;
2223 uint64_t file_offset
;
2225 ThreadContext
*thread
;
2227 #ifdef HAVE_SYS_ZLIB
2229 file_offset
= gztell (ctx
->gzfile
);
2232 file_offset
= ftell (ctx
->file
);
2233 if (!load_data (ctx
, 48))
2236 if (read_int32 (p
) != BUF_ID
) {
2237 fprintf (outfile
, "Incorrect buffer id: 0x%x\n", read_int32 (p
));
2238 for (i
= 0; i
< 48; ++i
) {
2239 fprintf (outfile
, "0x%x%s", p
[i
], i
% 8?" ":"\n");
2243 len
= read_int32 (p
+ 4);
2244 time_base
= read_int64 (p
+ 8);
2245 ptr_base
= read_int64 (p
+ 16);
2246 obj_base
= read_int64 (p
+ 24);
2247 thread_id
= read_int64 (p
+ 32);
2248 method_base
= read_int64 (p
+ 40);
2250 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
);
2251 thread
= load_thread (ctx
, thread_id
);
2252 if (!load_data (ctx
, len
))
2257 if (!startup_time
) {
2258 startup_time
= time_base
;
2259 if (use_time_filter
) {
2260 time_from
+= startup_time
;
2261 time_to
+= startup_time
;
2264 for (i
= 0; i
< thread
->stack_id
; ++i
)
2265 thread
->stack
[i
]->recurse_count
++;
2269 unsigned char *start
= p
;
2270 unsigned char event
= *p
;
2273 int subtype
= *p
& 0xf0;
2274 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2275 LOG_TIME (time_base
, tdiff
);
2277 if (subtype
== TYPE_GC_RESIZE
) {
2278 uint64_t new_size
= decode_uleb128 (p
, &p
);
2280 fprintf (outfile
, "gc heap resized to %llu\n", (unsigned long long) new_size
);
2282 if (new_size
> max_heap_size
)
2283 max_heap_size
= new_size
;
2284 } else if (subtype
== TYPE_GC_EVENT
) {
2286 if (ctx
->data_version
> 12)
2289 ev
= decode_uleb128 (p
, &p
);
2291 if (ctx
->data_version
> 12)
2294 gen
= decode_uleb128 (p
, &p
);
2296 fprintf (outfile
, "gc event for gen%d: %s at %llu (thread: 0x%zx)\n", gen
, gc_event_name (ev
), (unsigned long long) time_base
, thread
->thread_id
);
2298 fprintf (outfile
, "incorrect gc gen: %d\n", gen
);
2301 if (ev
== MONO_GC_EVENT_START
) {
2302 thread
->gc_start_times
[gen
] = time_base
;
2303 gc_info
[gen
].count
++;
2304 } else if (ev
== MONO_GC_EVENT_END
) {
2305 tdiff
= time_base
- thread
->gc_start_times
[gen
];
2306 gc_info
[gen
].total_time
+= tdiff
;
2307 if (tdiff
> gc_info
[gen
].max_time
)
2308 gc_info
[gen
].max_time
= tdiff
;
2310 } else if (subtype
== TYPE_GC_MOVE
) {
2311 int j
, num
= decode_uleb128 (p
, &p
);
2312 gc_object_moves
+= num
/ 2;
2313 for (j
= 0; j
< num
; j
+= 2) {
2314 intptr_t obj1diff
= decode_sleb128 (p
, &p
);
2315 intptr_t obj2diff
= decode_sleb128 (p
, &p
);
2316 if (num_tracked_objects
)
2317 track_move (OBJ_ADDR (obj1diff
), OBJ_ADDR (obj2diff
));
2319 fprintf (outfile
, "moved obj %p to %p\n", (void*)OBJ_ADDR (obj1diff
), (void*)OBJ_ADDR (obj2diff
));
2322 } else if (subtype
== TYPE_GC_HANDLE_CREATED
|| subtype
== TYPE_GC_HANDLE_CREATED_BT
) {
2323 int has_bt
= subtype
== TYPE_GC_HANDLE_CREATED_BT
;
2325 MethodDesc
*sframes
[8];
2326 MethodDesc
**frames
= sframes
;
2327 int htype
= decode_uleb128 (p
, &p
);
2328 uint32_t handle
= decode_uleb128 (p
, &p
);
2329 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2332 frames
= decode_bt (ctx
, sframes
, &num_bt
, p
, &p
, ptr_base
, &method_base
);
2334 fprintf (outfile
, "Cannot load backtrace\n");
2340 if ((thread_filter
&& thread_filter
== thread
->thread_id
) || (time_base
>= time_from
&& time_base
< time_to
)) {
2341 handle_info
[htype
].created
++;
2342 handle_info
[htype
].live
++;
2343 if (handle_info
[htype
].live
> handle_info
[htype
].max_live
)
2344 handle_info
[htype
].max_live
= handle_info
[htype
].live
;
2347 bt
= add_trace_methods (frames
, num_bt
, &handle_info
[htype
].traces
, 1);
2349 bt
= add_trace_thread (thread
, &handle_info
[htype
].traces
, 1);
2350 if (num_tracked_objects
)
2351 track_handle (OBJ_ADDR (objdiff
), htype
, handle
, bt
, time_base
);
2354 fprintf (outfile
, "handle (%s) %u created for object %p\n", get_handle_name (htype
), handle
, (void*)OBJ_ADDR (objdiff
));
2355 if (frames
!= sframes
)
2357 } else if (subtype
== TYPE_GC_HANDLE_DESTROYED
|| subtype
== TYPE_GC_HANDLE_DESTROYED_BT
) {
2358 int has_bt
= subtype
== TYPE_GC_HANDLE_DESTROYED_BT
;
2360 MethodDesc
*sframes
[8];
2361 MethodDesc
**frames
= sframes
;
2362 int htype
= decode_uleb128 (p
, &p
);
2363 uint32_t handle
= decode_uleb128 (p
, &p
);
2366 frames
= decode_bt (ctx
, sframes
, &num_bt
, p
, &p
, ptr_base
, &method_base
);
2368 fprintf (outfile
, "Cannot load backtrace\n");
2374 if ((thread_filter
&& thread_filter
== thread
->thread_id
) || (time_base
>= time_from
&& time_base
< time_to
)) {
2375 handle_info
[htype
].destroyed
++;
2376 handle_info
[htype
].live
--;
2379 bt
= add_trace_methods (frames
, num_bt
, &handle_info
[htype
].destroy_traces
, 1);
2381 bt
= add_trace_thread (thread
, &handle_info
[htype
].destroy_traces
, 1);
2382 /* TODO: track_handle_free () - would need to record and keep track of the associated object address... */
2385 fprintf (outfile
, "handle (%s) %u destroyed\n", get_handle_name (htype
), handle
);
2386 if (frames
!= sframes
)
2388 } else if (subtype
== TYPE_GC_FINALIZE_START
) {
2389 // TODO: Generate a finalizer report based on these events.
2391 fprintf (outfile
, "gc finalizer queue being processed at %llu\n", (unsigned long long) time_base
);
2392 } else if (subtype
== TYPE_GC_FINALIZE_END
) {
2394 fprintf (outfile
, "gc finalizer queue finished processing at %llu\n", (unsigned long long) time_base
);
2395 } else if (subtype
== TYPE_GC_FINALIZE_OBJECT_START
) {
2396 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2398 fprintf (outfile
, "gc finalizing object %p at %llu\n", (void *) OBJ_ADDR (objdiff
), (unsigned long long) time_base
);
2399 } else if (subtype
== TYPE_GC_FINALIZE_OBJECT_END
) {
2400 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2402 fprintf (outfile
, "gc finalized object %p at %llu\n", (void *) OBJ_ADDR (objdiff
), (unsigned long long) time_base
);
2406 case TYPE_METADATA
: {
2407 int subtype
= *p
& 0xf0;
2408 const char *load_str
= subtype
== TYPE_END_LOAD
? "loaded" : "unloaded";
2409 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2411 intptr_t ptrdiff
= decode_sleb128 (p
, &p
);
2412 LOG_TIME (time_base
, tdiff
);
2414 if (mtype
== TYPE_CLASS
) {
2415 intptr_t imptrdiff
= decode_sleb128 (p
, &p
);
2416 if (ctx
->data_version
< 13)
2417 decode_uleb128 (p
, &p
); /* flags */
2419 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
);
2420 add_class (ptr_base
+ ptrdiff
, (char*)p
);
2423 } else if (mtype
== TYPE_IMAGE
) {
2424 if (ctx
->data_version
< 13)
2425 decode_uleb128 (p
, &p
); /* flags */
2427 fprintf (outfile
, "%s image %p (%s) at %llu\n", load_str
, (void*)(ptr_base
+ ptrdiff
), p
, (unsigned long long) time_base
);
2428 if (subtype
== TYPE_END_LOAD
)
2429 add_image (ptr_base
+ ptrdiff
, (char*)p
);
2432 if (ctx
->data_version
>= 16) {
2433 while (*p
) p
++; // mvid
2436 } else if (mtype
== TYPE_ASSEMBLY
) {
2437 if (ctx
->data_version
> 13)
2438 decode_sleb128 (p
, &p
); // image
2439 if (ctx
->data_version
< 13)
2440 decode_uleb128 (p
, &p
); /* flags */
2442 fprintf (outfile
, "%s assembly %p (%s) at %llu\n", load_str
, (void*)(ptr_base
+ ptrdiff
), p
, (unsigned long long) time_base
);
2443 if (subtype
== TYPE_END_LOAD
)
2444 add_assembly (ptr_base
+ ptrdiff
, (char*)p
);
2447 } else if (mtype
== TYPE_DOMAIN
) {
2448 if (ctx
->data_version
< 13)
2449 decode_uleb128 (p
, &p
); /* flags */
2450 DomainContext
*nd
= get_domain (ctx
, ptr_base
+ ptrdiff
);
2451 /* no subtype means it's a name event, rather than start/stop */
2453 nd
->friendly_name
= pstrdup ((char *) p
);
2456 fprintf (outfile
, "domain %p named at %llu: %s\n", (void *) (ptr_base
+ ptrdiff
), (unsigned long long) time_base
, p
);
2458 fprintf (outfile
, "%s thread %p at %llu\n", load_str
, (void *) (ptr_base
+ ptrdiff
), (unsigned long long) time_base
);
2464 } else if (mtype
== TYPE_CONTEXT
) {
2465 if (ctx
->data_version
< 13)
2466 decode_uleb128 (p
, &p
); /* flags */
2467 intptr_t domaindiff
= decode_sleb128 (p
, &p
);
2469 fprintf (outfile
, "%s context %p (%p) at %llu\n", load_str
, (void*)(ptr_base
+ ptrdiff
), (void *) (ptr_base
+ domaindiff
), (unsigned long long) time_base
);
2470 if (subtype
== TYPE_END_LOAD
)
2471 get_remctx (ctx
, ptr_base
+ ptrdiff
)->domain_id
= ptr_base
+ domaindiff
;
2472 } else if (mtype
== TYPE_THREAD
) {
2473 if (ctx
->data_version
< 13)
2474 decode_uleb128 (p
, &p
); /* flags */
2475 ThreadContext
*nt
= get_thread (ctx
, ptr_base
+ ptrdiff
);
2476 /* no subtype means it's a name event, rather than start/stop */
2478 nt
->name
= pstrdup ((char*)p
);
2481 fprintf (outfile
, "thread %p named at %llu: %s\n", (void*)(ptr_base
+ ptrdiff
), (unsigned long long) time_base
, p
);
2483 fprintf (outfile
, "%s thread %p at %llu\n", load_str
, (void *) (ptr_base
+ ptrdiff
), (unsigned long long) time_base
);
2489 } else if (mtype
== TYPE_VTABLE
) {
2490 intptr_t domaindiff
= decode_sleb128 (p
, &p
);
2491 intptr_t classdiff
= decode_sleb128 (p
, &p
);
2493 fprintf (outfile
, "vtable %p for class %p in domain %p at %llu\n", (void *) (ptrdiff
+ ptr_base
), (void *) (classdiff
+ ptr_base
), (void *) (domaindiff
+ ptr_base
), (unsigned long long) time_base
);
2494 add_vtable (ptr_base
+ ptrdiff
, ptr_base
+ classdiff
);
2499 int has_bt
= *p
& TYPE_ALLOC_BT
;
2500 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2501 intptr_t ptrdiff
= decode_sleb128 (p
, &p
);
2502 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2505 MethodDesc
* sframes
[8];
2506 MethodDesc
** frames
= sframes
;
2508 if (ctx
->data_version
> 14) {
2509 VTableDesc
*vt
= lookup_vtable (ptr_base
+ ptrdiff
);
2512 cd
= lookup_class (ptr_base
+ ptrdiff
);
2513 len
= decode_uleb128 (p
, &p
);
2514 LOG_TIME (time_base
, tdiff
);
2517 fprintf (outfile
, "alloced object %p, size %llu (%s) at %llu\n", (void*)OBJ_ADDR (objdiff
), (unsigned long long) len
, cd
->name
, (unsigned long long) time_base
);
2520 frames
= decode_bt (ctx
, sframes
, &num_bt
, p
, &p
, ptr_base
, &method_base
);
2522 fprintf (outfile
, "Cannot load backtrace\n");
2526 if ((thread_filter
&& thread_filter
== thread
->thread_id
) || (time_base
>= time_from
&& time_base
< time_to
)) {
2529 cd
->alloc_size
+= len
;
2531 bt
= add_trace_methods (frames
, num_bt
, &cd
->traces
, len
);
2533 bt
= add_trace_thread (thread
, &cd
->traces
, len
);
2534 if (find_size
&& len
>= find_size
) {
2535 if (!find_name
|| strstr (cd
->name
, find_name
))
2536 found_object (OBJ_ADDR (objdiff
));
2537 } else if (!find_size
&& find_name
&& strstr (cd
->name
, find_name
)) {
2538 found_object (OBJ_ADDR (objdiff
));
2540 if (num_tracked_objects
)
2541 tracked_creation (OBJ_ADDR (objdiff
), cd
, len
, bt
, time_base
);
2543 if (frames
!= sframes
)
2548 int subtype
= *p
& 0xf0;
2549 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2550 int64_t ptrdiff
= decode_sleb128 (p
, &p
);
2551 LOG_TIME (time_base
, tdiff
);
2553 method_base
+= ptrdiff
;
2554 if (subtype
== TYPE_JIT
) {
2555 intptr_t codediff
= decode_sleb128 (p
, &p
);
2556 int codelen
= decode_uleb128 (p
, &p
);
2557 MethodDesc
*jitted_method
;
2559 fprintf (outfile
, "jitted method %p (%s), size: %d, code: %p\n", (void*)(method_base
), p
, codelen
, (void*)(ptr_base
+ codediff
));
2560 jitted_method
= add_method (method_base
, (char*)p
, ptr_base
+ codediff
, codelen
);
2561 if (!(time_base
>= time_from
&& time_base
< time_to
))
2562 jitted_method
->ignore_jit
= 1;
2567 if ((thread_filter
&& thread_filter
!= thread
->thread_id
))
2569 if (!(time_base
>= time_from
&& time_base
< time_to
))
2571 method
= lookup_method (method_base
);
2572 if (subtype
== TYPE_ENTER
) {
2573 add_trace_thread (thread
, &method
->traces
, 1);
2574 push_method (thread
, method
, time_base
);
2576 pop_method (thread
, method
, time_base
);
2579 fprintf (outfile
, "%s method %s\n", subtype
== TYPE_ENTER
? "enter": subtype
== TYPE_EXC_LEAVE
? "exleave": "leave", method
->name
);
2584 int subtype
= *p
& 0xf0;
2585 if (subtype
== TYPE_HEAP_OBJECT
) {
2586 HeapObjectDesc
*ho
= NULL
;
2589 if (ctx
->data_version
> 12) {
2590 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2591 LOG_TIME (time_base
, tdiff
);
2593 objdiff
= decode_sleb128 (p
, &p
);
2595 objdiff
= decode_sleb128 (p
+ 1, &p
);
2596 intptr_t ptrdiff
= decode_sleb128 (p
, &p
);
2597 uint64_t size
= decode_uleb128 (p
, &p
);
2598 if (ctx
->data_version
>= 16)
2600 uintptr_t num
= decode_uleb128 (p
, &p
);
2601 uintptr_t ref_offset
= 0;
2602 uintptr_t last_obj_offset
= 0;
2604 if (ctx
->data_version
> 14) {
2605 VTableDesc
*vt
= lookup_vtable (ptr_base
+ ptrdiff
);
2608 cd
= lookup_class (ptr_base
+ ptrdiff
);
2610 HeapClassDesc
*hcd
= add_heap_shot_class (thread
->current_heap_shot
, cd
, size
);
2611 if (collect_traces
) {
2612 ho
= alloc_heap_obj (OBJ_ADDR (objdiff
), hcd
, num
);
2613 add_heap_shot_obj (thread
->current_heap_shot
, ho
);
2618 ho
= heap_shot_obj_add_refs (thread
->current_heap_shot
, OBJ_ADDR (objdiff
), num
, &ref_offset
);
2620 for (i
= 0; i
< num
; ++i
) {
2621 /* FIXME: use object distance to measure how good
2622 * the GC is at keeping related objects close
2624 uintptr_t offset
= ctx
->data_version
> 1? last_obj_offset
+ decode_uleb128 (p
, &p
): -1;
2625 intptr_t obj1diff
= decode_sleb128 (p
, &p
);
2626 last_obj_offset
= offset
;
2628 ho
->refs
[ref_offset
+ i
] = OBJ_ADDR (obj1diff
);
2629 if (num_tracked_objects
)
2630 track_obj_reference (OBJ_ADDR (obj1diff
), OBJ_ADDR (objdiff
), cd
);
2633 fprintf (outfile
, "traced object %p, size %llu (%s), refs: %zd\n", (void*)OBJ_ADDR (objdiff
), (unsigned long long) size
, cd
->name
, num
);
2634 } else if (subtype
== TYPE_HEAP_ROOT
) {
2636 if (ctx
->data_version
> 14) {
2638 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2639 LOG_TIME (time_base
, tdiff
);
2641 num
= decode_uleb128 (p
, &p
);
2642 for (i
= 0; i
< num
; ++i
) {
2643 intptr_t ptrdiff
= decode_sleb128 (p
, &p
);
2644 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2647 fprintf (outfile
, "root object %p at address %p\n", (void*)OBJ_ADDR (objdiff
), (void *) (ptr_base
+ ptrdiff
));
2649 thread_add_root (thread
, OBJ_ADDR (objdiff
), MONO_PROFILER_GC_ROOT_MISC
, 0);
2652 if (ctx
->data_version
> 12) {
2653 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2654 LOG_TIME (time_base
, tdiff
);
2656 num
= decode_uleb128 (p
, &p
);
2658 num
= decode_uleb128 (p
+ 1, &p
);
2659 uintptr_t gc_num G_GNUC_UNUSED
= decode_uleb128 (p
, &p
);
2661 for (i
= 0; i
< num
; ++i
) {
2662 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2664 if (ctx
->data_version
== 13)
2667 root_type
= decode_uleb128 (p
, &p
);
2668 /* we just discard the extra info for now */
2669 uintptr_t extra_info
= decode_uleb128 (p
, &p
);
2671 fprintf (outfile
, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff
), get_root_name (root_type
));
2673 thread_add_root (thread
, OBJ_ADDR (objdiff
), root_type
, extra_info
);
2676 } else if (subtype
== TYPE_HEAP_ROOT_REGISTER
) {
2677 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2678 LOG_TIME (time_base
, tdiff
);
2681 int64_t ptrdiff
= decode_sleb128 (p
, &p
);
2682 uint64_t size
= decode_uleb128 (p
, &p
);
2684 int64_t keydiff
= decode_sleb128 (p
, &p
);
2685 char *desc
= (char*) p
;
2689 fprintf (outfile
, "root register address %p size %lld type %d key %p name %s\n", (void *) (ptr_base
+ ptrdiff
), (unsigned long long) size
, type
, (void *) (ptr_base
+ keydiff
), desc
);
2690 } else if (subtype
== TYPE_HEAP_ROOT_UNREGISTER
) {
2691 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2692 LOG_TIME (time_base
, tdiff
);
2694 int64_t ptrdiff
= decode_sleb128 (p
, &p
);
2697 fprintf (outfile
, "root unregister address %p\n", (void *) (ptr_base
+ ptrdiff
));
2698 } else if (subtype
== TYPE_HEAP_END
) {
2699 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2700 LOG_TIME (time_base
, tdiff
);
2703 fprintf (outfile
, "heap shot end\n");
2704 if (collect_traces
) {
2705 HeapShot
*hs
= thread
->current_heap_shot
;
2706 if (hs
&& thread
->num_roots
) {
2707 /* transfer the root ownershipt to the heapshot */
2708 hs
->num_roots
= thread
->num_roots
;
2709 hs
->roots
= thread
->roots
;
2710 hs
->roots_extra
= thread
->roots_extra
;
2711 hs
->roots_types
= thread
->roots_types
;
2713 g_free (thread
->roots
);
2714 g_free (thread
->roots_extra
);
2715 g_free (thread
->roots_types
);
2717 thread
->num_roots
= 0;
2718 thread
->size_roots
= 0;
2719 thread
->roots
= NULL
;
2720 thread
->roots_extra
= NULL
;
2721 thread
->roots_types
= NULL
;
2722 heap_shot_resolve_reverse_refs (hs
);
2723 heap_shot_mark_objects (hs
);
2724 heap_shot_free_objects (hs
);
2726 thread
->current_heap_shot
= NULL
;
2727 } else if (subtype
== TYPE_HEAP_START
) {
2728 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2729 LOG_TIME (time_base
, tdiff
);
2732 fprintf (outfile
, "heap shot start\n");
2733 thread
->current_heap_shot
= new_heap_shot (time_base
);
2737 case TYPE_MONITOR
: {
2738 int has_bt
= *p
& TYPE_MONITOR_BT
;
2740 if (ctx
->data_version
< 13)
2741 event
= (*p
>> 4) & 0x3;
2742 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2743 if (ctx
->data_version
> 13)
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 MonitorDesc
*mdesc
= lookup_monitor (OBJ_ADDR (objdiff
));
2756 if (event
== MONO_PROFILER_MONITOR_CONTENTION
) {
2758 monitor_contention
++;
2759 mdesc
->contentions
++;
2760 thread
->monitor
= mdesc
;
2761 thread
->contention_start
= time_base
;
2763 } else if (event
== MONO_PROFILER_MONITOR_FAIL
) {
2766 if (thread
->monitor
&& thread
->contention_start
) {
2767 uint64_t wait_time
= time_base
- thread
->contention_start
;
2768 if (wait_time
> thread
->monitor
->max_wait_time
)
2769 thread
->monitor
->max_wait_time
= wait_time
;
2770 thread
->monitor
->wait_time
+= wait_time
;
2771 thread
->monitor
= NULL
;
2772 thread
->contention_start
= 0;
2775 } else if (event
== MONO_PROFILER_MONITOR_DONE
) {
2778 if (thread
->monitor
&& thread
->contention_start
) {
2779 uint64_t wait_time
= time_base
- thread
->contention_start
;
2780 if (wait_time
> thread
->monitor
->max_wait_time
)
2781 thread
->monitor
->max_wait_time
= wait_time
;
2782 thread
->monitor
->wait_time
+= wait_time
;
2783 thread
->monitor
= NULL
;
2784 thread
->contention_start
= 0;
2790 frames
= decode_bt (ctx
, sframes
, &num_bt
, p
, &p
, ptr_base
, &method_base
);
2792 fprintf (outfile
, "Cannot load backtrace\n");
2795 if (record
&& event
== MONO_PROFILER_MONITOR_CONTENTION
)
2796 add_trace_methods (frames
, num_bt
, &mdesc
->traces
, 1);
2799 add_trace_thread (thread
, &mdesc
->traces
, 1);
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
;
2828 if (ctx
->data_version
> 13)
2829 decode_uleb128 (p
, &p
); // exception object
2831 clause_summary
[clause_type
]++;
2833 fprintf (outfile
, "clause %s (%d) in method %s\n", clause_name (clause_type
), clause_num
, lookup_method (method_base
)->name
);
2835 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2840 frames
= decode_bt (ctx
, sframes
, &has_bt
, p
, &p
, ptr_base
, &method_base
);
2842 fprintf (outfile
, "Cannot load backtrace\n");
2846 add_trace_methods (frames
, has_bt
, &exc_traces
, 1);
2849 add_trace_thread (thread
, &exc_traces
, 1);
2851 if (frames
!= sframes
)
2854 fprintf (outfile
, "throw %p\n", (void*)OBJ_ADDR (objdiff
));
2858 case TYPE_RUNTIME
: {
2859 int subtype
= *p
& 0xf0;
2860 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2861 LOG_TIME (time_base
, tdiff
);
2863 if (subtype
== TYPE_JITHELPER
) {
2865 if (ctx
->data_version
> 12)
2868 type
= decode_uleb128 (p
, &p
);
2869 if (ctx
->data_version
< 14)
2871 intptr_t codediff
= decode_sleb128 (p
, &p
);
2872 int codelen
= decode_uleb128 (p
, &p
);
2874 if (type
== MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
) {
2875 name
= (const char *)p
;
2879 name
= code_buffer_desc (type
);
2882 jit_helpers_code_size
+= codelen
;
2884 fprintf (outfile
, "jit helper %s, size: %d, code: %p\n", name
, codelen
, (void*)(ptr_base
+ codediff
));
2889 int subtype
= *p
& 0xf0;
2890 if (subtype
== TYPE_SAMPLE_HIT
) {
2894 if (ctx
->data_version
> 12) {
2895 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2896 LOG_TIME (time_base
, tdiff
);
2898 if (ctx
->data_version
< 14)
2901 sample_type
= SAMPLE_CYCLES
;
2904 sample_type
= decode_uleb128 (p
+ 1, &p
);
2905 tstamp
= decode_uleb128 (p
, &p
);
2907 void *tid
= (void *) thread_id
;
2908 if (ctx
->data_version
> 10)
2909 tid
= (void *) (ptr_base
+ decode_sleb128 (p
, &p
));
2910 int count
= decode_uleb128 (p
, &p
);
2911 for (i
= 0; i
< count
; ++i
) {
2912 uintptr_t ip
= ptr_base
+ decode_sleb128 (p
, &p
);
2913 if ((tstamp
>= time_from
&& tstamp
< time_to
))
2914 add_stat_sample (sample_type
, ip
);
2916 fprintf (outfile
, "sample hit, type: %d at %p for thread %p\n", sample_type
, (void*)ip
, tid
);
2918 if (ctx
->data_version
> 5) {
2919 count
= decode_uleb128 (p
, &p
);
2920 for (i
= 0; i
< count
; ++i
) {
2922 int64_t ptrdiff
= decode_sleb128 (p
, &p
);
2923 method_base
+= ptrdiff
;
2924 method
= lookup_method (method_base
);
2926 fprintf (outfile
, "sample hit bt %d: %s\n", i
, method
->name
);
2927 if (ctx
->data_version
< 13) {
2928 decode_sleb128 (p
, &p
); /* il offset */
2929 decode_sleb128 (p
, &p
); /* native offset */
2933 } else if (subtype
== TYPE_SAMPLE_USYM
) {
2934 /* un unmanaged symbol description */
2936 if (ctx
->data_version
> 12) {
2937 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2938 LOG_TIME (time_base
, tdiff
);
2940 addr
= ptr_base
+ decode_sleb128 (p
, &p
);
2942 addr
= ptr_base
+ decode_sleb128 (p
+ 1, &p
);
2943 uintptr_t size
= decode_uleb128 (p
, &p
);
2945 name
= pstrdup ((char*)p
);
2946 add_unmanaged_symbol (addr
, name
, size
);
2948 fprintf (outfile
, "unmanaged symbol %s at %p\n", name
, (void*)addr
);
2951 } else if (subtype
== TYPE_SAMPLE_UBIN
) {
2952 /* un unmanaged binary loaded in memory */
2953 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2954 uintptr_t addr
= decode_sleb128 (p
, &p
);
2955 if (ctx
->data_version
> 13)
2957 uint64_t offset G_GNUC_UNUSED
= decode_uleb128 (p
, &p
);
2958 uintptr_t size
= decode_uleb128 (p
, &p
);
2960 LOG_TIME (time_base
, tdiff
);
2962 name
= pstrdup ((char*)p
);
2963 add_unmanaged_binary (addr
, name
, size
);
2965 fprintf (outfile
, "unmanaged binary %s at %p\n", name
, (void*)addr
);
2968 } else if (subtype
== TYPE_SAMPLE_COUNTERS_DESC
) {
2970 if (ctx
->data_version
> 12) {
2971 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2972 LOG_TIME (time_base
, tdiff
);
2974 len
= decode_uleb128 (p
, &p
);
2976 len
= decode_uleb128 (p
+ 1, &p
);
2977 for (i
= 0; i
< len
; i
++) {
2978 uint64_t type
, unit
, variance
, index
;
2979 uint64_t section
= decode_uleb128 (p
, &p
);
2980 char *section_str
, *name
;
2981 if (section
!= MONO_COUNTER_PERFCOUNTERS
) {
2982 section_str
= (char*) section_name (section
);
2984 section_str
= pstrdup ((char*)p
);
2987 name
= pstrdup ((char*)p
);
2989 if (ctx
->data_version
> 12 && ctx
->data_version
< 15) {
2994 type
= decode_uleb128 (p
, &p
);
2995 unit
= decode_uleb128 (p
, &p
);
2996 variance
= decode_uleb128 (p
, &p
);
2998 index
= decode_uleb128 (p
, &p
);
2999 add_counter (section_str
, name
, (int)type
, (int)unit
, (int)variance
, (int)index
);
3001 } else if (subtype
== TYPE_SAMPLE_COUNTERS
) {
3003 CounterValue
*value
, *previous
= NULL
;
3005 uint64_t timestamp
; // milliseconds since startup
3006 if (ctx
->data_version
> 12) {
3007 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
3008 LOG_TIME (time_base
, tdiff
);
3010 timestamp
= (time_base
- startup_time
) / 1000 / 1000;
3012 timestamp
= decode_uleb128 (p
+ 1, &p
);
3013 uint64_t time_between
= timestamp
/ 1000 * 1000 * 1000 * 1000 + startup_time
;
3015 uint64_t type
, index
= decode_uleb128 (p
, &p
);
3019 for (list
= counters
; list
; list
= list
->next
) {
3020 if (list
->counter
->index
== (int)index
) {
3021 previous
= list
->counter
->values_last
;
3026 if (ctx
->data_version
> 12 && ctx
->data_version
< 15)
3029 type
= decode_uleb128 (p
, &p
);
3031 value
= (CounterValue
*) g_calloc (1, sizeof (CounterValue
));
3032 value
->timestamp
= timestamp
;
3035 case MONO_COUNTER_INT
:
3036 #if SIZEOF_VOID_P == 4
3037 case MONO_COUNTER_WORD
:
3039 value
->buffer
= (unsigned char *)g_malloc (sizeof (int32_t));
3040 *(int32_t*)value
->buffer
= (int32_t)decode_sleb128 (p
, &p
) + (previous
? (*(int32_t*)previous
->buffer
) : 0);
3042 case MONO_COUNTER_UINT
:
3043 value
->buffer
= (unsigned char *) g_malloc (sizeof (uint32_t));
3044 *(uint32_t*)value
->buffer
= (uint32_t)decode_uleb128 (p
, &p
) + (previous
? (*(uint32_t*)previous
->buffer
) : 0);
3046 case MONO_COUNTER_LONG
:
3047 #if SIZEOF_VOID_P == 8
3048 case MONO_COUNTER_WORD
:
3050 case MONO_COUNTER_TIME_INTERVAL
:
3051 value
->buffer
= (unsigned char *) g_malloc (sizeof (int64_t));
3052 *(int64_t*)value
->buffer
= (int64_t)decode_sleb128 (p
, &p
) + (previous
? (*(int64_t*)previous
->buffer
) : 0);
3054 case MONO_COUNTER_ULONG
:
3055 value
->buffer
= (unsigned char *) g_malloc (sizeof (uint64_t));
3056 *(uint64_t*)value
->buffer
= (uint64_t)decode_uleb128 (p
, &p
) + (previous
? (*(uint64_t*)previous
->buffer
) : 0);
3058 case MONO_COUNTER_DOUBLE
:
3059 value
->buffer
= (unsigned char *) g_malloc (sizeof (double));
3060 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
3061 for (i
= 0; i
< sizeof (double); i
++)
3063 for (i
= sizeof (double) - 1; i
>= 0; i
--)
3065 value
->buffer
[i
] = *p
++;
3067 case MONO_COUNTER_STRING
:
3069 value
->buffer
= NULL
;
3071 value
->buffer
= (unsigned char*) pstrdup ((char*)p
);
3076 if (time_between
>= time_from
&& time_between
<= time_to
)
3077 add_counter_value (index
, value
);
3084 case TYPE_COVERAGE
:{
3085 int subtype
= *p
& 0xf0;
3087 case TYPE_COVERAGE_METHOD
: {
3090 if (ctx
->data_version
> 12) {
3091 uint64_t tdiff
= decode_uleb128 (p
, &p
);
3092 LOG_TIME (time_base
, tdiff
);
3107 decode_uleb128 (p
, &p
);
3108 decode_uleb128 (p
, &p
);
3109 decode_uleb128 (p
, &p
);
3113 case TYPE_COVERAGE_STATEMENT
: {
3116 if (ctx
->data_version
> 12) {
3117 uint64_t tdiff
= decode_uleb128 (p
, &p
);
3118 LOG_TIME (time_base
, tdiff
);
3122 decode_uleb128 (p
, &p
);
3123 decode_uleb128 (p
, &p
);
3124 decode_uleb128 (p
, &p
);
3125 decode_uleb128 (p
, &p
);
3126 decode_uleb128 (p
, &p
);
3130 case TYPE_COVERAGE_ASSEMBLY
: {
3133 if (ctx
->data_version
> 12) {
3134 uint64_t tdiff
= decode_uleb128 (p
, &p
);
3135 LOG_TIME (time_base
, tdiff
);
3146 decode_uleb128 (p
, &p
);
3147 decode_uleb128 (p
, &p
);
3148 decode_uleb128 (p
, &p
);
3152 case TYPE_COVERAGE_CLASS
: {
3155 if (ctx
->data_version
> 12) {
3156 uint64_t tdiff
= decode_uleb128 (p
, &p
);
3157 LOG_TIME (time_base
, tdiff
);
3166 decode_uleb128 (p
, &p
);
3167 decode_uleb128 (p
, &p
);
3168 decode_uleb128 (p
, &p
);
3179 int subtype
= *p
& 0xf0;
3180 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
3181 LOG_TIME (time_base
, tdiff
);
3183 if (subtype
== TYPE_SYNC_POINT
) {
3186 fprintf (outfile
, "sync point %i (%s)\n", type
, sync_point_name (type
));
3187 } else if (subtype
== TYPE_AOT_ID
) {
3189 fprintf (outfile
, "aot id %s\n", p
);
3190 while (*p
) p
++; // aot id
3196 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
);
3199 record_event_stats (event
, p
- start
);
3201 thread
->last_time
= time_base
;
3202 for (i
= 0; i
< thread
->stack_id
; ++i
)
3203 thread
->stack
[i
]->recurse_count
= 0;
3208 read_header_string (ProfContext
*ctx
, char **field
)
3210 if (!load_data (ctx
, 4))
3213 if (!load_data (ctx
, read_int32 (ctx
->buf
)))
3216 *field
= pstrdup ((const char *) ctx
->buf
);
3222 load_file (char *name
)
3225 ProfContext
*ctx
= (ProfContext
*) g_calloc (sizeof (ProfContext
), 1);
3226 if (strcmp (name
, "-") == 0)
3229 ctx
->file
= fopen (name
, "rb");
3231 printf ("Cannot open file: %s\n", name
);
3234 #if defined (HAVE_SYS_ZLIB)
3235 if (ctx
->file
!= stdin
)
3236 ctx
->gzfile
= gzdopen (fileno (ctx
->file
), "rb");
3238 if (!load_data (ctx
, 16))
3241 if (read_int32 (p
) != LOG_HEADER_ID
)
3244 ctx
->version_major
= *p
++;
3245 ctx
->version_minor
= *p
++;
3246 ctx
->data_version
= *p
++;
3247 if (ctx
->data_version
> LOG_DATA_VERSION
)
3249 /* reading 64 bit files on 32 bit systems not supported yet */
3250 if (*p
++ > sizeof (void*))
3252 ctx
->startup_time
= read_int64 (p
);
3254 // nanoseconds startup time
3255 if (ctx
->version_major
>= 3)
3256 if (!load_data (ctx
, 8))
3258 if (!load_data (ctx
, 14))
3261 ctx
->timer_overhead
= read_int32 (p
);
3263 if (read_int32 (p
)) /* flags must be 0 */
3266 ctx
->pid
= read_int32 (p
);
3268 ctx
->port
= read_int16 (p
);
3270 if (ctx
->version_major
>= 1) {
3271 if (!read_header_string (ctx
, &ctx
->args
))
3273 if (!read_header_string (ctx
, &ctx
->arch
))
3275 if (!read_header_string (ctx
, &ctx
->os
))
3278 if (!load_data (ctx
, 2)) /* old opsys field, was never used */
3288 static int alloc_sort_mode
= ALLOC_SORT_BYTES
;
3291 compare_class (const void *a
, const void *b
)
3293 ClassDesc
*const *A
= (ClassDesc
*const *)a
;
3294 ClassDesc
*const *B
= (ClassDesc
*const *)b
;
3295 uint64_t vala
, valb
;
3296 if (alloc_sort_mode
== ALLOC_SORT_BYTES
) {
3297 vala
= (*A
)->alloc_size
;
3298 valb
= (*B
)->alloc_size
;
3300 vala
= (*A
)->allocs
;
3301 valb
= (*B
)->allocs
;
3311 dump_header (ProfContext
*ctx
)
3313 time_t st
= ctx
->startup_time
/ 1000;
3314 char *t
= ctime (&st
);
3315 fprintf (outfile
, "\nMono log profiler data\n");
3316 fprintf (outfile
, "\tProfiler version: %d.%d\n", ctx
->version_major
, ctx
->version_minor
);
3317 fprintf (outfile
, "\tData version: %d\n", ctx
->data_version
);
3318 if (ctx
->version_major
>= 1) {
3319 fprintf (outfile
, "\tArguments: %s\n", ctx
->args
);
3320 fprintf (outfile
, "\tArchitecture: %s\n", ctx
->arch
);
3321 fprintf (outfile
, "\tOperating system: %s\n", ctx
->os
);
3323 fprintf (outfile
, "\tMean timer overhead: %d nanoseconds\n", ctx
->timer_overhead
);
3324 fprintf (outfile
, "\tProgram startup: %s", t
);
3326 fprintf (outfile
, "\tProgram ID: %d\n", ctx
->pid
);
3328 fprintf (outfile
, "\tServer listening on: %d\n", ctx
->port
);
3332 dump_traces (TraceDesc
*traces
, const char *desc
)
3339 sort_context_array (traces
);
3340 for (j
= 0; j
< traces
->count
; ++j
) {
3343 bt
= traces
->traces
[j
].bt
;
3346 fprintf (outfile
, "\t%llu %s from:\n", (unsigned long long) traces
->traces
[j
].count
, desc
);
3347 for (k
= 0; k
< bt
->count
; ++k
)
3348 fprintf (outfile
, "\t\t%s\n", bt
->methods
[k
]->name
);
3353 dump_threads (ProfContext
*ctx
)
3355 ThreadContext
*thread
;
3356 fprintf (outfile
, "\nThread summary\n");
3357 for (thread
= ctx
->threads
; thread
; thread
= thread
->next
) {
3358 if (thread
->thread_id
) {
3359 fprintf (outfile
, "\tThread: %p, name: \"%s\"\n", (void*)thread
->thread_id
, thread
->name
? thread
->name
: "");
3365 dump_domains (ProfContext
*ctx
)
3367 fprintf (outfile
, "\nDomain summary\n");
3369 for (DomainContext
*domain
= ctx
->domains
; domain
; domain
= domain
->next
)
3370 fprintf (outfile
, "\tDomain: %p, friendly name: \"%s\"\n", (void *) domain
->domain_id
, domain
->friendly_name
);
3374 dump_remctxs (ProfContext
*ctx
)
3376 fprintf (outfile
, "\nContext summary\n");
3378 for (RemCtxContext
*remctx
= ctx
->remctxs
; remctx
; remctx
= remctx
->next
)
3379 fprintf (outfile
, "\tContext: %p, domain: %p\n", (void *) remctx
->remctx_id
, (void *) remctx
->domain_id
);
3383 dump_exceptions (void)
3386 fprintf (outfile
, "\nException summary\n");
3387 fprintf (outfile
, "\tThrows: %llu\n", (unsigned long long) throw_count
);
3388 dump_traces (&exc_traces
, "throws");
3389 for (i
= 0; i
<= MONO_EXCEPTION_CLAUSE_FAULT
; ++i
) {
3390 if (!clause_summary
[i
])
3392 fprintf (outfile
, "\tExecuted %s clauses: %llu\n", clause_name (i
), (unsigned long long) clause_summary
[i
]);
3397 compare_monitor (const void *a
, const void *b
)
3399 MonitorDesc
*const *A
= (MonitorDesc
*const *)a
;
3400 MonitorDesc
*const *B
= (MonitorDesc
*const *)b
;
3401 if ((*B
)->wait_time
== (*A
)->wait_time
)
3403 if ((*B
)->wait_time
< (*A
)->wait_time
)
3409 dump_monitors (void)
3411 MonitorDesc
**monitors
;
3415 monitors
= (MonitorDesc
**) g_malloc (sizeof (void*) * num_monitors
);
3416 for (i
= 0, j
= 0; i
< SMALL_HASH_SIZE
; ++i
) {
3417 MonitorDesc
*mdesc
= monitor_hash
[i
];
3419 monitors
[j
++] = mdesc
;
3420 mdesc
= mdesc
->next
;
3423 qsort (monitors
, num_monitors
, sizeof (void*), compare_monitor
);
3424 fprintf (outfile
, "\nMonitor lock summary\n");
3425 for (i
= 0; i
< num_monitors
; ++i
) {
3426 MonitorDesc
*mdesc
= monitors
[i
];
3427 fprintf (outfile
, "\tLock object %p: %d contentions\n", (void*)mdesc
->objid
, (int)mdesc
->contentions
);
3428 fprintf (outfile
, "\t\t%.6f secs total wait time, %.6f max, %.6f average\n",
3429 mdesc
->wait_time
/1000000000.0, mdesc
->max_wait_time
/1000000000.0, mdesc
->wait_time
/1000000000.0/mdesc
->contentions
);
3430 dump_traces (&mdesc
->traces
, "contentions");
3432 fprintf (outfile
, "\tLock contentions: %llu\n", (unsigned long long) monitor_contention
);
3433 fprintf (outfile
, "\tLock acquired: %llu\n", (unsigned long long) monitor_acquired
);
3434 fprintf (outfile
, "\tLock failures: %llu\n", (unsigned long long) monitor_failed
);
3441 fprintf (outfile
, "\nGC summary\n");
3442 fprintf (outfile
, "\tGC resizes: %d\n", gc_resizes
);
3443 fprintf (outfile
, "\tMax heap size: %llu\n", (unsigned long long) max_heap_size
);
3444 fprintf (outfile
, "\tObject moves: %llu\n", (unsigned long long) gc_object_moves
);
3445 for (i
= 0; i
< 3; ++i
) {
3446 if (!gc_info
[i
].count
)
3448 fprintf (outfile
, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
3449 i
, gc_info
[i
].count
,
3450 (unsigned long long) (gc_info
[i
].max_time
/ 1000),
3451 (unsigned long long) (gc_info
[i
].total_time
/ 1000),
3452 (unsigned long long) (gc_info
[i
].total_time
/ gc_info
[i
].count
/ 1000));
3454 for (i
= 0; i
< 3; ++i
) {
3455 if (!handle_info
[i
].max_live
)
3457 fprintf (outfile
, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
3458 get_handle_name (i
),
3459 (unsigned long long) (handle_info
[i
].created
),
3460 (unsigned long long) (handle_info
[i
].destroyed
),
3461 (unsigned long long) (handle_info
[i
].max_live
));
3462 dump_traces (&handle_info
[i
].traces
, "created");
3463 dump_traces (&handle_info
[i
].destroy_traces
, "destroyed");
3472 int compiled_methods
= 0;
3474 fprintf (outfile
, "\nJIT summary\n");
3475 for (i
= 0; i
< HASH_SIZE
; ++i
) {
3476 m
= method_hash
[i
];
3477 for (m
= method_hash
[i
]; m
; m
= m
->next
) {
3478 if (!m
->code
|| m
->ignore_jit
)
3481 code_size
+= m
->len
;
3484 fprintf (outfile
, "\tCompiled methods: %d\n", compiled_methods
);
3485 fprintf (outfile
, "\tGenerated code size: %d\n", code_size
);
3486 fprintf (outfile
, "\tJIT helpers: %d\n", num_jit_helpers
);
3487 fprintf (outfile
, "\tJIT helpers code size: %d\n", jit_helpers_code_size
);
3491 dump_allocations (void)
3494 intptr_t allocs
= 0;
3496 int header_done
= 0;
3497 ClassDesc
**classes
= (ClassDesc
**) g_malloc (num_classes
* sizeof (void*));
3500 for (i
= 0; i
< HASH_SIZE
; ++i
) {
3501 cd
= class_hash
[i
];
3507 qsort (classes
, num_classes
, sizeof (void*), compare_class
);
3508 for (i
= 0; i
< num_classes
; ++i
) {
3512 allocs
+= cd
->allocs
;
3513 size
+= cd
->alloc_size
;
3514 if (!header_done
++) {
3515 fprintf (outfile
, "\nAllocation summary\n");
3516 fprintf (outfile
, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
3518 fprintf (outfile
, "%10llu %10zd %8llu %s\n",
3519 (unsigned long long) (cd
->alloc_size
),
3521 (unsigned long long) (cd
->alloc_size
/ cd
->allocs
),
3523 dump_traces (&cd
->traces
, "bytes");
3526 fprintf (outfile
, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size
, allocs
);
3535 static int method_sort_mode
= METHOD_SORT_TOTAL
;
3538 compare_method (const void *a
, const void *b
)
3540 MethodDesc
*const *A
= (MethodDesc
*const *)a
;
3541 MethodDesc
*const *B
= (MethodDesc
*const *)b
;
3542 uint64_t vala
, valb
;
3543 if (method_sort_mode
== METHOD_SORT_SELF
) {
3544 vala
= (*A
)->self_time
;
3545 valb
= (*B
)->self_time
;
3546 } else if (method_sort_mode
== METHOD_SORT_CALLS
) {
3550 vala
= (*A
)->total_time
;
3551 valb
= (*B
)->total_time
;
3561 dump_metadata (void)
3563 fprintf (outfile
, "\nMetadata summary\n");
3564 fprintf (outfile
, "\tLoaded images: %d\n", num_images
);
3568 for (i
= 0; i
< SMALL_HASH_SIZE
; ++i
) {
3569 image
= image_hash
[i
];
3571 fprintf (outfile
, "\t\t%s\n", image
->filename
);
3572 image
= image
->next
;
3576 fprintf (outfile
, "\tLoaded assemblies: %d\n", num_assemblies
);
3578 AssemblyDesc
*assembly
;
3580 for (i
= 0; i
< SMALL_HASH_SIZE
; ++i
) {
3581 assembly
= assembly_hash
[i
];
3583 fprintf (outfile
, "\t\t%s\n", assembly
->asmname
);
3584 assembly
= assembly
->next
;
3595 int header_done
= 0;
3596 MethodDesc
**methods
= (MethodDesc
**) g_malloc (num_methods
* sizeof (void*));
3599 for (i
= 0; i
< HASH_SIZE
; ++i
) {
3600 cd
= method_hash
[i
];
3602 cd
->total_time
= cd
->self_time
+ cd
->callee_time
;
3607 qsort (methods
, num_methods
, sizeof (void*), compare_method
);
3608 for (i
= 0; i
< num_methods
; ++i
) {
3615 msecs
= cd
->total_time
/ 1000000;
3616 smsecs
= (cd
->total_time
- cd
->callee_time
) / 1000000;
3617 if (!msecs
&& !verbose
)
3619 if (!header_done
++) {
3620 fprintf (outfile
, "\nMethod call summary\n");
3621 fprintf (outfile
, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
3623 fprintf (outfile
, "%8llu %8llu %10llu %s\n",
3624 (unsigned long long) (msecs
),
3625 (unsigned long long) (smsecs
),
3626 (unsigned long long) (cd
->calls
),
3628 dump_traces (&cd
->traces
, "calls");
3631 fprintf (outfile
, "Total calls: %llu\n", (unsigned long long) calls
);
3635 compare_heap_class (const void *a
, const void *b
)
3637 HeapClassDesc
*const *A
= (HeapClassDesc
*const *)a
;
3638 HeapClassDesc
*const *B
= (HeapClassDesc
*const *)b
;
3639 uint64_t vala
, valb
;
3640 if (alloc_sort_mode
== ALLOC_SORT_BYTES
) {
3641 vala
= (*A
)->total_size
;
3642 valb
= (*B
)->total_size
;
3655 compare_rev_class (const void *a
, const void *b
)
3657 const HeapClassRevRef
*A
= (const HeapClassRevRef
*)a
;
3658 const HeapClassRevRef
*B
= (const HeapClassRevRef
*)b
;
3659 if (B
->count
== A
->count
)
3661 if (B
->count
< A
->count
)
3667 dump_rev_claases (HeapClassRevRef
*revs
, int count
)
3674 for (j
= 0; j
< count
; ++j
) {
3675 HeapClassDesc
*cd
= revs
[j
].klass
;
3676 fprintf (outfile
, "\t\t%llu references from: %s\n",
3677 (unsigned long long) (revs
[j
].count
),
3683 heap_shot_summary (HeapShot
*hs
, int hs_num
, HeapShot
*last_hs
)
3690 HeapClassDesc
**sorted
;
3691 sorted
= (HeapClassDesc
**) g_malloc (sizeof (void*) * hs
->class_count
);
3692 for (i
= 0; i
< hs
->hash_size
; ++i
) {
3693 cd
= hs
->class_hash
[i
];
3697 size
+= cd
->total_size
;
3698 sorted
[ccount
++] = cd
;
3700 hs
->sorted
= sorted
;
3701 qsort (sorted
, ccount
, sizeof (void*), compare_heap_class
);
3702 fprintf (outfile
, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %zd\n",
3704 (hs
->timestamp
- startup_time
)/1000000000.0,
3705 (unsigned long long) (size
),
3706 (unsigned long long) (count
),
3707 ccount
, hs
->num_roots
);
3708 if (!verbose
&& ccount
> 30)
3710 fprintf (outfile
, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
3711 for (i
= 0; i
< ccount
; ++i
) {
3712 HeapClassRevRef
*rev_sorted
;
3714 HeapClassDesc
*ocd
= NULL
;
3717 ocd
= heap_class_lookup (last_hs
, cd
->klass
);
3718 fprintf (outfile
, "\t%10llu %10llu %8llu %s",
3719 (unsigned long long) (cd
->total_size
),
3720 (unsigned long long) (cd
->count
),
3721 (unsigned long long) (cd
->total_size
/ cd
->count
),
3724 int64_t bdiff
= cd
->total_size
- ocd
->total_size
;
3725 int64_t cdiff
= cd
->count
- ocd
->count
;
3726 fprintf (outfile
, " (bytes: %+lld, count: %+lld)\n", (long long) bdiff
, (long long) cdiff
);
3728 fprintf (outfile
, "\n");
3730 if (!collect_traces
)
3732 rev_sorted
= (HeapClassRevRef
*) g_malloc (cd
->rev_count
* sizeof (HeapClassRevRef
));
3734 for (j
= 0; j
< cd
->rev_hash_size
; ++j
) {
3735 if (cd
->rev_hash
[j
].klass
)
3736 rev_sorted
[k
++] = cd
->rev_hash
[j
];
3738 assert (cd
->rev_count
== k
);
3739 qsort (rev_sorted
, cd
->rev_count
, sizeof (HeapClassRevRef
), compare_rev_class
);
3740 if (cd
->root_references
)
3741 fprintf (outfile
, "\t\t%zd root references (%zd pinning)\n", cd
->root_references
, cd
->pinned_references
);
3742 dump_rev_claases (rev_sorted
, cd
->rev_count
);
3743 g_free (rev_sorted
);
3749 compare_heap_shots (const void *a
, const void *b
)
3751 HeapShot
*const *A
= (HeapShot
*const *)a
;
3752 HeapShot
*const *B
= (HeapShot
*const *)b
;
3753 if ((*B
)->timestamp
== (*A
)->timestamp
)
3755 if ((*B
)->timestamp
> (*A
)->timestamp
)
3761 dump_heap_shots (void)
3763 HeapShot
**hs_sorted
;
3765 HeapShot
*last_hs
= NULL
;
3769 hs_sorted
= (HeapShot
**) g_malloc (num_heap_shots
* sizeof (void*));
3770 fprintf (outfile
, "\nHeap shot summary\n");
3772 for (hs
= heap_shots
; hs
; hs
= hs
->next
)
3773 hs_sorted
[i
++] = hs
;
3774 qsort (hs_sorted
, num_heap_shots
, sizeof (void*), compare_heap_shots
);
3775 for (i
= 0; i
< num_heap_shots
; ++i
) {
3777 heap_shot_summary (hs
, i
, last_hs
);
3782 #define DUMP_EVENT_STAT(EVENT,SUBTYPE) dump_event (#EVENT, #SUBTYPE, EVENT, SUBTYPE);
3785 dump_event (const char *event_name
, const char *subtype_name
, int event
, int subtype
)
3787 int idx
= event
| subtype
;
3788 EventStat evt
= stats
[idx
];
3792 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
);
3798 fprintf (outfile
, "\nMlpd statistics\n");
3799 fprintf (outfile
, "\tBuffer count %d\toverhead %d (%d bytes per header)\n", buffer_count
, buffer_count
* BUFFER_HEADER_SIZE
, BUFFER_HEADER_SIZE
);
3800 fprintf (outfile
, "\nEvent details:\n");
3802 DUMP_EVENT_STAT (TYPE_ALLOC
, TYPE_ALLOC_NO_BT
);
3803 DUMP_EVENT_STAT (TYPE_ALLOC
, TYPE_ALLOC_BT
);
3805 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_EVENT
);
3806 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_RESIZE
);
3807 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_MOVE
);
3808 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_HANDLE_CREATED
);
3809 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_HANDLE_DESTROYED
);
3810 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_HANDLE_CREATED_BT
);
3811 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_HANDLE_DESTROYED_BT
);
3812 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_FINALIZE_START
);
3813 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_FINALIZE_END
);
3814 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_FINALIZE_OBJECT_START
);
3815 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_FINALIZE_OBJECT_END
);
3817 DUMP_EVENT_STAT (TYPE_METADATA
, TYPE_END_LOAD
);
3818 DUMP_EVENT_STAT (TYPE_METADATA
, TYPE_END_UNLOAD
);
3820 DUMP_EVENT_STAT (TYPE_METHOD
, TYPE_LEAVE
);
3821 DUMP_EVENT_STAT (TYPE_METHOD
, TYPE_ENTER
);
3822 DUMP_EVENT_STAT (TYPE_METHOD
, TYPE_EXC_LEAVE
);
3823 DUMP_EVENT_STAT (TYPE_METHOD
, TYPE_JIT
);
3825 DUMP_EVENT_STAT (TYPE_EXCEPTION
, TYPE_THROW_NO_BT
);
3826 DUMP_EVENT_STAT (TYPE_EXCEPTION
, TYPE_THROW_BT
);
3827 DUMP_EVENT_STAT (TYPE_EXCEPTION
, TYPE_CLAUSE
);
3829 DUMP_EVENT_STAT (TYPE_MONITOR
, TYPE_MONITOR_NO_BT
);
3830 DUMP_EVENT_STAT (TYPE_MONITOR
, TYPE_MONITOR_BT
);
3832 DUMP_EVENT_STAT (TYPE_HEAP
, TYPE_HEAP_START
);
3833 DUMP_EVENT_STAT (TYPE_HEAP
, TYPE_HEAP_END
);
3834 DUMP_EVENT_STAT (TYPE_HEAP
, TYPE_HEAP_OBJECT
);
3835 DUMP_EVENT_STAT (TYPE_HEAP
, TYPE_HEAP_ROOT
);
3837 DUMP_EVENT_STAT (TYPE_SAMPLE
, TYPE_SAMPLE_HIT
);
3838 DUMP_EVENT_STAT (TYPE_SAMPLE
, TYPE_SAMPLE_USYM
);
3839 DUMP_EVENT_STAT (TYPE_SAMPLE
, TYPE_SAMPLE_UBIN
);
3840 DUMP_EVENT_STAT (TYPE_SAMPLE
, TYPE_SAMPLE_COUNTERS_DESC
);
3841 DUMP_EVENT_STAT (TYPE_SAMPLE
, TYPE_SAMPLE_COUNTERS
);
3843 DUMP_EVENT_STAT (TYPE_RUNTIME
, TYPE_JITHELPER
);
3845 DUMP_EVENT_STAT (TYPE_COVERAGE
, TYPE_COVERAGE_ASSEMBLY
);
3846 DUMP_EVENT_STAT (TYPE_COVERAGE
, TYPE_COVERAGE_METHOD
);
3847 DUMP_EVENT_STAT (TYPE_COVERAGE
, TYPE_COVERAGE_STATEMENT
);
3848 DUMP_EVENT_STAT (TYPE_COVERAGE
, TYPE_COVERAGE_CLASS
);
3850 DUMP_EVENT_STAT (TYPE_META
, TYPE_SYNC_POINT
);
3851 DUMP_EVENT_STAT (TYPE_META
, TYPE_AOT_ID
);
3857 flush_context (ProfContext
*ctx
)
3859 ThreadContext
*thread
;
3860 /* FIXME: sometimes there are leftovers: indagate */
3861 for (thread
= ctx
->threads
; thread
; thread
= thread
->next
) {
3862 while (thread
->stack_id
) {
3864 fprintf (outfile
, "thread %p has %d items on stack\n", (void*)thread
->thread_id
, thread
->stack_id
);
3865 pop_method (thread
, thread
->stack
[thread
->stack_id
- 1], thread
->last_time
);
3870 static const char *reports
= "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,domain,context,heapshot,counters";
3873 match_option (const char *p
, const char *opt
)
3875 int len
= strlen (opt
);
3876 if (strncmp (p
, opt
, len
) == 0) {
3885 print_reports (ProfContext
*ctx
, const char *reps
, int parse_only
)
3889 for (p
= reps
; *p
; p
= opt
) {
3890 if ((opt
= match_option (p
, "header")) != p
) {
3895 if ((opt
= match_option (p
, "thread")) != p
) {
3900 if ((opt
= match_option (p
, "domain")) != p
) {
3905 if ((opt
= match_option (p
, "context")) != p
) {
3910 if ((opt
= match_option (p
, "gc")) != p
) {
3915 if ((opt
= match_option (p
, "jit")) != p
) {
3920 if ((opt
= match_option (p
, "alloc")) != p
) {
3922 dump_allocations ();
3925 if ((opt
= match_option (p
, "call")) != p
) {
3930 if ((opt
= match_option (p
, "metadata")) != p
) {
3935 if ((opt
= match_option (p
, "exception")) != p
) {
3940 if ((opt
= match_option (p
, "monitor")) != p
) {
3945 if ((opt
= match_option (p
, "heapshot")) != p
) {
3950 if ((opt
= match_option (p
, "sample")) != p
) {
3955 if ((opt
= match_option (p
, "counters")) != p
) {
3960 if ((opt
= match_option (p
, "coverage")) != p
) {
3961 printf ("The log profiler no longer supports code coverage. Please use the dedicated coverage profiler instead. See mono-profilers(1) for more information.\n");
3964 if ((opt
= match_option (p
, "stats")) != p
) {
3975 add_find_spec (const char *p
)
3977 if (p
[0] == 'S' && p
[1] == ':') {
3979 find_size
= strtoul (p
+ 2, &vale
, 10);
3981 } else if (p
[0] == 'T' && p
[1] == ':') {
3991 printf ("Mono log profiler report version %d.%d\n", LOG_VERSION_MAJOR
, LOG_VERSION_MINOR
);
3992 printf ("Usage: mprof-report [OPTIONS] FILENAME\n");
3993 printf ("FILENAME can be '-' to read from standard input.\n");
3994 printf ("Options:\n");
3995 printf ("\t--help display this help\n");
3996 printf ("\t--out=FILE write to FILE instead of stdout\n");
3997 printf ("\t--traces collect and show backtraces\n");
3998 printf ("\t--maxframes=NUM limit backtraces to NUM entries\n");
3999 printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
4000 printf ("\t %s\n", reports
);
4001 printf ("\t--method-sort=MODE sort methods according to MODE: total, self, calls\n");
4002 printf ("\t--alloc-sort=MODE sort allocations according to MODE: bytes, count\n");
4003 printf ("\t--counters-sort=MODE sort counters according to MODE: time, category\n");
4004 printf ("\t only accessible in verbose mode\n");
4005 printf ("\t--track=OB1[,OB2...] track what happens to objects OBJ1, O2 etc.\n");
4006 printf ("\t--find=FINDSPEC find and track objects matching FINFSPEC, where FINDSPEC is:\n");
4007 printf ("\t S:minimum_size or T:partial_name\n");
4008 printf ("\t--thread=THREADID consider just the data for thread THREADID\n");
4009 printf ("\t--time=FROM-TO consider data FROM seconds from startup up to TO seconds\n");
4010 printf ("\t--verbose increase verbosity level\n");
4011 printf ("\t--debug display decoding debug info for mprof-report devs\n");
4015 main (int argc
, char *argv
[])
4020 for (i
= 1; i
< argc
; ++i
) {
4021 if (strcmp ("--debug", argv
[i
]) == 0) {
4023 } else if (strcmp ("--help", argv
[i
]) == 0) {
4026 } else if (strncmp ("--alloc-sort=", argv
[i
], 13) == 0) {
4027 const char *val
= argv
[i
] + 13;
4028 if (strcmp (val
, "bytes") == 0) {
4029 alloc_sort_mode
= ALLOC_SORT_BYTES
;
4030 } else if (strcmp (val
, "count") == 0) {
4031 alloc_sort_mode
= ALLOC_SORT_COUNT
;
4036 } else if (strncmp ("--method-sort=", argv
[i
], 14) == 0) {
4037 const char *val
= argv
[i
] + 14;
4038 if (strcmp (val
, "total") == 0) {
4039 method_sort_mode
= METHOD_SORT_TOTAL
;
4040 } else if (strcmp (val
, "self") == 0) {
4041 method_sort_mode
= METHOD_SORT_SELF
;
4042 } else if (strcmp (val
, "calls") == 0) {
4043 method_sort_mode
= METHOD_SORT_CALLS
;
4048 } else if (strncmp ("--counters-sort=", argv
[i
], 16) == 0) {
4049 const char *val
= argv
[i
] + 16;
4050 if (strcmp (val
, "time") == 0) {
4051 counters_sort_mode
= COUNTERS_SORT_TIME
;
4052 } else if (strcmp (val
, "category") == 0) {
4053 counters_sort_mode
= COUNTERS_SORT_CATEGORY
;
4058 } else if (strncmp ("--reports=", argv
[i
], 10) == 0) {
4059 const char *val
= argv
[i
] + 10;
4060 if (!print_reports (NULL
, val
, 1)) {
4065 } else if (strncmp ("--out=", argv
[i
], 6) == 0) {
4066 const char *val
= argv
[i
] + 6;
4067 outfile
= fopen (val
, "w");
4069 printf ("Cannot open output file: %s\n", val
);
4072 } else if (strncmp ("--maxframes=", argv
[i
], 12) == 0) {
4073 const char *val
= argv
[i
] + 12;
4075 trace_max
= strtoul (val
, &vale
, 10);
4076 } else if (strncmp ("--find=", argv
[i
], 7) == 0) {
4077 const char *val
= argv
[i
] + 7;
4078 if (!add_find_spec (val
)) {
4082 } else if (strncmp ("--track=", argv
[i
], 8) == 0) {
4083 const char *val
= argv
[i
] + 8;
4086 uintptr_t tracked_obj
;
4091 tracked_obj
= strtoul (val
, &vale
, 0);
4092 found_object (tracked_obj
);
4095 } else if (strncmp ("--thread=", argv
[i
], 9) == 0) {
4096 const char *val
= argv
[i
] + 9;
4098 thread_filter
= strtoul (val
, &vale
, 0);
4099 } else if (strncmp ("--time=", argv
[i
], 7) == 0) {
4100 char *val
= pstrdup (argv
[i
] + 7);
4101 double from_secs
, to_secs
;
4102 char *top
= strchr (val
, '-');
4108 from_secs
= atof (val
);
4109 to_secs
= atof (top
);
4111 if (from_secs
> to_secs
) {
4115 time_from
= from_secs
* 1000000000;
4116 time_to
= to_secs
* 1000000000;
4117 use_time_filter
= 1;
4118 } else if (strcmp ("--verbose", argv
[i
]) == 0) {
4120 } else if (strcmp ("--traces", argv
[i
]) == 0) {
4123 } else if (strncmp ("--coverage-out=", argv
[i
], 15) == 0) {
4124 // For backwards compatibility.
4133 ctx
= load_file (argv
[i
]);
4135 printf ("Not a log profiler data file (or unsupported version).\n");
4138 while (decode_buffer (ctx
));
4139 flush_context (ctx
);
4140 if (num_tracked_objects
)
4142 print_reports (ctx
, reports
, 0);