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_INTERP
: return "Mono Interp";
318 case MONO_COUNTER_JIT
: return "Mono JIT";
319 case MONO_COUNTER_GC
: return "Mono GC";
320 case MONO_COUNTER_METADATA
: return "Mono Metadata";
321 case MONO_COUNTER_GENERICS
: return "Mono Generics";
322 case MONO_COUNTER_SECURITY
: return "Mono Security";
323 case MONO_COUNTER_RUNTIME
: return "Mono Runtime";
324 case MONO_COUNTER_SYSTEM
: return "Mono System";
325 case MONO_COUNTER_PROFILER
: return "Mono Profiler";
326 default: return "<unknown>";
334 case MONO_COUNTER_INT
: return "Int";
335 case MONO_COUNTER_UINT
: return "UInt";
336 case MONO_COUNTER_WORD
: return "Word";
337 case MONO_COUNTER_LONG
: return "Long";
338 case MONO_COUNTER_ULONG
: return "ULong";
339 case MONO_COUNTER_DOUBLE
: return "Double";
340 case MONO_COUNTER_STRING
: return "String";
341 case MONO_COUNTER_TIME_INTERVAL
: return "Time Interval";
342 default: return "<unknown>";
350 case MONO_COUNTER_RAW
: return "Raw";
351 case MONO_COUNTER_BYTES
: return "Bytes";
352 case MONO_COUNTER_TIME
: return "Time";
353 case MONO_COUNTER_COUNT
: return "Count";
354 case MONO_COUNTER_PERCENTAGE
: return "Percentage";
355 default: return "<unknown>";
360 variance_name (int variance
)
363 case MONO_COUNTER_MONOTONIC
: return "Monotonic";
364 case MONO_COUNTER_CONSTANT
: return "Constant";
365 case MONO_COUNTER_VARIABLE
: return "Variable";
366 default: return "<unknown>";
371 dump_counters_value (Counter
*counter
, const char *key_format
, const char *key
, void *value
)
376 snprintf (format
, sizeof (format
), "%s : %%s\n", key_format
);
377 fprintf (outfile
, format
, key
, "<null>");
379 switch (counter
->type
) {
380 case MONO_COUNTER_INT
:
381 #if SIZEOF_VOID_P == 4
382 case MONO_COUNTER_WORD
:
384 snprintf (format
, sizeof (format
), "%s : %%d\n", key_format
);
385 fprintf (outfile
, format
, key
, *(int32_t*)value
);
387 case MONO_COUNTER_UINT
:
388 snprintf (format
, sizeof (format
), "%s : %%u\n", key_format
);
389 fprintf (outfile
, format
, key
, *(uint32_t*)value
);
391 case MONO_COUNTER_LONG
:
392 #if SIZEOF_VOID_P == 8
393 case MONO_COUNTER_WORD
:
395 case MONO_COUNTER_TIME_INTERVAL
:
396 if (counter
->type
== MONO_COUNTER_LONG
&& counter
->unit
== MONO_COUNTER_TIME
) {
397 snprintf (format
, sizeof (format
), "%s : %%0.3fms\n", key_format
);
398 fprintf (outfile
, format
, key
, (double)*(int64_t*)value
/ 10000.0);
399 } else if (counter
->type
== MONO_COUNTER_TIME_INTERVAL
) {
400 snprintf (format
, sizeof (format
), "%s : %%0.3fms\n", key_format
);
401 fprintf (outfile
, format
, key
, (double)*(int64_t*)value
/ 1000.0);
403 snprintf (format
, sizeof (format
), "%s : %%u\n", key_format
);
404 fprintf (outfile
, format
, key
, *(int64_t*)value
);
407 case MONO_COUNTER_ULONG
:
408 snprintf (format
, sizeof (format
), "%s : %%llu\n", key_format
);
409 fprintf (outfile
, format
, key
, *(uint64_t*)value
);
411 case MONO_COUNTER_DOUBLE
:
412 snprintf (format
, sizeof (format
), "%s : %%f\n", key_format
);
413 fprintf (outfile
, format
, key
, *(double*)value
);
415 case MONO_COUNTER_STRING
:
416 snprintf (format
, sizeof (format
), "%s : %%s\n", key_format
);
417 fprintf (outfile
, format
, key
, *(char*)value
);
427 CounterValue
*cvalue
;
428 CounterTimestamp
*ctimestamp
;
429 CounterSection
*csection
;
431 char strtimestamp
[17];
432 int i
, section_printed
;
434 fprintf (outfile
, "\nCounters:\n");
437 char counters_to_print
[][64] = {
439 "Methods JITted using mono JIT",
440 "Methods JITted using LLVM",
441 "Total time spent JITting (sec)",
449 "CPU Load Average - 1min",
450 "CPU Load Average - 5min",
451 "CPU Load Average - 15min",
455 for (csection
= counters_sections
; csection
; csection
= csection
->next
) {
458 for (clist
= csection
->counters
; clist
; clist
= clist
->next
) {
459 counter
= clist
->counter
;
460 if (!counter
->values_last
)
463 for (i
= 0; counters_to_print
[i
][0] != 0; i
++) {
464 if (strcmp (counters_to_print
[i
], counter
->name
) == 0) {
465 if (!section_printed
) {
466 fprintf (outfile
, "\t%s:\n", csection
->value
);
470 dump_counters_value (counter
, "\t\t%-30s", counter
->name
, counter
->values_last
->buffer
);
476 } else if (counters_sort_mode
== COUNTERS_SORT_TIME
) {
477 for (ctimestamp
= counters_timestamps
; ctimestamp
; ctimestamp
= ctimestamp
->next
) {
478 fprintf (outfile
, "\t%llu:%02llu:%02llu:%02llu.%03llu:\n",
479 (unsigned long long) (ctimestamp
->value
/ 1000 / 60 / 60 / 24 % 1000),
480 (unsigned long long) (ctimestamp
->value
/ 1000 / 60 / 60 % 24),
481 (unsigned long long) (ctimestamp
->value
/ 1000 / 60 % 60),
482 (unsigned long long) (ctimestamp
->value
/ 1000 % 60),
483 (unsigned long long) (ctimestamp
->value
% 1000));
485 for (csection
= ctimestamp
->sections
; csection
; csection
= csection
->next
) {
486 fprintf (outfile
, "\t\t%s:\n", csection
->value
);
488 for (clist
= csection
->counters
; clist
; clist
= clist
->next
) {
489 counter
= clist
->counter
;
490 for (cvalue
= counter
->values
; cvalue
; cvalue
= cvalue
->next
) {
491 if (cvalue
->timestamp
!= ctimestamp
->value
)
494 dump_counters_value (counter
, "\t\t\t%-30s", counter
->name
, cvalue
->buffer
);
499 } else if (counters_sort_mode
== COUNTERS_SORT_CATEGORY
) {
500 for (csection
= counters_sections
; csection
; csection
= csection
->next
) {
501 fprintf (outfile
, "\t%s:\n", csection
->value
);
503 for (clist
= csection
->counters
; clist
; clist
= clist
->next
) {
504 counter
= clist
->counter
;
505 fprintf (outfile
, "\t\t%s: [type: %s, unit: %s, variance: %s]\n",
506 counter
->name
, type_name (counter
->type
), unit_name (counter
->unit
), variance_name (counter
->variance
));
508 for (cvalue
= counter
->values
; cvalue
; cvalue
= cvalue
->next
) {
509 snprintf (strtimestamp
, sizeof (strtimestamp
), "%llu:%02llu:%02llu:%02llu.%03llu",
510 (unsigned long long) (cvalue
->timestamp
/ 1000 / 60 / 60 / 24 % 1000),
511 (unsigned long long) (cvalue
->timestamp
/ 1000 / 60 / 60 % 24),
512 (unsigned long long) (cvalue
->timestamp
/ 1000 / 60 % 60),
513 (unsigned long long) (cvalue
->timestamp
/ 1000 % 60),
514 (unsigned long long) (cvalue
->timestamp
% 1000));
516 dump_counters_value (counter
, "\t\t\t%s", strtimestamp
, cvalue
->buffer
);
523 static int num_images
;
524 typedef struct _ImageDesc ImageDesc
;
531 static ImageDesc
* image_hash
[SMALL_HASH_SIZE
] = {0};
534 add_image (intptr_t image
, char *name
)
536 int slot
= ((image
>> 2) & 0xffff) % SMALL_HASH_SIZE
;
537 ImageDesc
*cd
= (ImageDesc
*) g_malloc (sizeof (ImageDesc
));
539 cd
->filename
= pstrdup (name
);
540 cd
->next
= image_hash
[slot
];
541 image_hash
[slot
] = cd
;
545 static int num_assemblies
;
547 typedef struct _AssemblyDesc AssemblyDesc
;
548 struct _AssemblyDesc
{
554 static AssemblyDesc
* assembly_hash
[SMALL_HASH_SIZE
] = {0};
557 add_assembly (intptr_t assembly
, char *name
)
559 int slot
= ((assembly
>> 2) & 0xffff) % SMALL_HASH_SIZE
;
560 AssemblyDesc
*cd
= (AssemblyDesc
*) g_malloc (sizeof (AssemblyDesc
));
561 cd
->assembly
= assembly
;
562 cd
->asmname
= pstrdup (name
);
563 cd
->next
= assembly_hash
[slot
];
564 assembly_hash
[slot
] = cd
;
568 typedef struct _BackTrace BackTrace
;
580 typedef struct _ClassDesc ClassDesc
;
590 static ClassDesc
* class_hash
[HASH_SIZE
] = {0};
591 static int num_classes
= 0;
594 add_class (intptr_t klass
, const char *name
)
596 int slot
= ((klass
>> 2) & 0xffff) % HASH_SIZE
;
598 cd
= class_hash
[slot
];
599 while (cd
&& cd
->klass
!= klass
)
601 /* we resolved an unknown class (unless we had the code unloaded) */
603 /*printf ("resolved unknown: %s\n", name);*/
605 cd
->name
= pstrdup (name
);
608 cd
= (ClassDesc
*) g_calloc (sizeof (ClassDesc
), 1);
610 cd
->name
= pstrdup (name
);
611 cd
->next
= class_hash
[slot
];
614 cd
->traces
.count
= 0;
616 cd
->traces
.traces
= NULL
;
617 class_hash
[slot
] = cd
;
623 lookup_class (intptr_t klass
)
625 int slot
= ((klass
>> 2) & 0xffff) % HASH_SIZE
;
626 ClassDesc
*cd
= class_hash
[slot
];
627 while (cd
&& cd
->klass
!= klass
)
631 snprintf (buf
, sizeof (buf
), "unresolved class %p", (void*)klass
);
632 return add_class (klass
, buf
);
637 typedef struct _VTableDesc VTableDesc
;
644 static VTableDesc
* vtable_hash
[HASH_SIZE
] = {0};
647 add_vtable (intptr_t vtable
, intptr_t klass
)
649 int slot
= ((vtable
>> 2) & 0xffff) % HASH_SIZE
;
651 VTableDesc
*vt
= vtable_hash
[slot
];
653 while (vt
&& vt
->vtable
!= vtable
)
659 vt
= (VTableDesc
*) g_calloc (sizeof (VTableDesc
), 1);
662 vt
->klass
= lookup_class (klass
);
663 vt
->next
= vtable_hash
[slot
];
665 vtable_hash
[slot
] = vt
;
671 lookup_vtable (intptr_t vtable
)
673 int slot
= ((vtable
>> 2) & 0xffff) % HASH_SIZE
;
674 VTableDesc
*vt
= vtable_hash
[slot
];
676 while (vt
&& vt
->vtable
!= vtable
)
680 return add_vtable (vtable
, 0);
685 typedef struct _MethodDesc MethodDesc
;
694 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 */
697 uint64_t callee_time
;
702 static MethodDesc
* method_hash
[HASH_SIZE
] = {0};
703 static int num_methods
= 0;
706 add_method (intptr_t method
, const char *name
, intptr_t code
, int len
)
708 int slot
= ((method
>> 2) & 0xffff) % HASH_SIZE
;
710 cd
= method_hash
[slot
];
711 while (cd
&& cd
->method
!= method
)
713 /* we resolved an unknown method (unless we had the code unloaded) */
717 /*printf ("resolved unknown: %s\n", name);*/
719 cd
->name
= pstrdup (name
);
722 cd
= (MethodDesc
*) g_calloc (sizeof (MethodDesc
), 1);
724 cd
->name
= pstrdup (name
);
729 cd
->traces
.count
= 0;
731 cd
->traces
.traces
= NULL
;
732 cd
->next
= method_hash
[slot
];
733 method_hash
[slot
] = cd
;
739 lookup_method (intptr_t method
)
741 int slot
= ((method
>> 2) & 0xffff) % HASH_SIZE
;
742 MethodDesc
*cd
= method_hash
[slot
];
743 while (cd
&& cd
->method
!= method
)
747 snprintf (buf
, sizeof (buf
), "unknown method %p", (void*)method
);
748 return add_method (method
, buf
, 0, 0);
753 static int num_stat_samples
= 0;
754 static int size_stat_samples
= 0;
755 uintptr_t *stat_samples
= NULL
;
756 int *stat_sample_desc
= NULL
;
759 add_stat_sample (int type
, uintptr_t ip
) {
760 if (num_stat_samples
== size_stat_samples
) {
761 size_stat_samples
*= 2;
762 if (!size_stat_samples
)
763 size_stat_samples
= 32;
764 stat_samples
= (uintptr_t *) g_realloc (stat_samples
, size_stat_samples
* sizeof (uintptr_t));
765 stat_sample_desc
= (int *) g_realloc (stat_sample_desc
, size_stat_samples
* sizeof (int));
767 stat_samples
[num_stat_samples
] = ip
;
768 stat_sample_desc
[num_stat_samples
++] = type
;
772 lookup_method_by_ip (uintptr_t ip
)
777 for (i
= 0; i
< HASH_SIZE
; ++i
) {
780 //printf ("checking %p against %p-%p\n", (void*)ip, (void*)(m->code), (void*)(m->code + m->len));
781 if (ip
>= (uintptr_t)m
->code
&& ip
< (uintptr_t)m
->code
+ m
->len
) {
791 compare_method_samples (const void *a
, const void *b
)
793 MethodDesc
*const *A
= (MethodDesc
*const *)a
;
794 MethodDesc
*const *B
= (MethodDesc
*const *)b
;
795 if ((*A
)->sample_hits
== (*B
)->sample_hits
)
797 if ((*B
)->sample_hits
< (*A
)->sample_hits
)
802 typedef struct _UnmanagedSymbol UnmanagedSymbol
;
803 struct _UnmanagedSymbol
{
804 UnmanagedSymbol
*parent
;
809 uintptr_t sample_hits
;
812 static UnmanagedSymbol
**usymbols
= NULL
;
813 static int usymbols_size
= 0;
814 static int usymbols_num
= 0;
817 compare_usymbol_addr (const void *a
, const void *b
)
819 UnmanagedSymbol
*const *A
= (UnmanagedSymbol
*const *)a
;
820 UnmanagedSymbol
*const *B
= (UnmanagedSymbol
*const *)b
;
821 if ((*B
)->addr
== (*A
)->addr
)
823 if ((*B
)->addr
> (*A
)->addr
)
829 compare_usymbol_samples (const void *a
, const void *b
)
831 UnmanagedSymbol
*const *A
= (UnmanagedSymbol
*const *)a
;
832 UnmanagedSymbol
*const *B
= (UnmanagedSymbol
*const *)b
;
833 if ((*B
)->sample_hits
== (*A
)->sample_hits
)
835 if ((*B
)->sample_hits
< (*A
)->sample_hits
)
841 add_unmanaged_symbol (uintptr_t addr
, char *name
, uintptr_t size
)
843 UnmanagedSymbol
*sym
;
844 if (usymbols_num
== usymbols_size
) {
845 int new_size
= usymbols_size
* 2;
848 usymbols
= (UnmanagedSymbol
**) g_realloc (usymbols
, sizeof (void*) * new_size
);
849 usymbols_size
= new_size
;
851 sym
= (UnmanagedSymbol
*) g_calloc (sizeof (UnmanagedSymbol
), 1);
855 usymbols
[usymbols_num
++] = sym
;
858 /* only valid after the symbols are sorted */
859 static UnmanagedSymbol
*
860 lookup_unmanaged_symbol (uintptr_t addr
)
862 int r
= usymbols_num
- 1;
864 UnmanagedSymbol
*sym
;
869 if (addr
== sym
->addr
)
871 if (addr
< sym
->addr
) {
873 } else if (addr
> sym
->addr
) {
878 if (last_best
>= 0 && (addr
- usymbols
[last_best
]->addr
) < 4096)
879 return usymbols
[last_best
];
883 /* we use the same structure for binaries */
884 static UnmanagedSymbol
**ubinaries
= NULL
;
885 static int ubinaries_size
= 0;
886 static int ubinaries_num
= 0;
889 add_unmanaged_binary (uintptr_t addr
, char *name
, uintptr_t size
)
891 UnmanagedSymbol
*sym
;
892 if (ubinaries_num
== ubinaries_size
) {
893 int new_size
= ubinaries_size
* 2;
896 ubinaries
= (UnmanagedSymbol
**) g_realloc (ubinaries
, sizeof (void*) * new_size
);
897 ubinaries_size
= new_size
;
899 sym
= (UnmanagedSymbol
*) g_calloc (sizeof (UnmanagedSymbol
), 1);
904 ubinaries
[ubinaries_num
++] = sym
;
907 static UnmanagedSymbol
*
908 lookup_unmanaged_binary (uintptr_t addr
)
911 for (i
= 0; i
< ubinaries_num
; ++i
) {
912 UnmanagedSymbol
*ubin
= ubinaries
[i
];
913 if (addr
>= ubin
->addr
&& addr
< ubin
->addr
+ ubin
->size
) {
920 // For backwards compatibility.
922 TYPE_SAMPLE_UBIN
= 2 << 4,
926 TYPE_COVERAGE_ASSEMBLY
= 0 << 4,
927 TYPE_COVERAGE_METHOD
= 1 << 4,
928 TYPE_COVERAGE_STATEMENT
= 2 << 4,
929 TYPE_COVERAGE_CLASS
= 3 << 4,
938 SAMPLE_BRANCH_MISSES
,
942 MONO_GC_EVENT_MARK_START
= 1,
943 MONO_GC_EVENT_MARK_END
= 2,
944 MONO_GC_EVENT_RECLAIM_START
= 3,
945 MONO_GC_EVENT_RECLAIM_END
= 4,
949 sample_type_name (int type
)
952 case SAMPLE_CYCLES
: return "cycles";
953 case SAMPLE_INSTRUCTIONS
: return "instructions retired";
954 case SAMPLE_CACHE_MISSES
: return "cache misses";
955 case SAMPLE_CACHE_REFS
: return "cache references";
956 case SAMPLE_BRANCHES
: return "executed branches";
957 case SAMPLE_BRANCH_MISSES
: return "unpredicted branches";
963 set_usym_parent (UnmanagedSymbol
** cachedus
, int count
)
966 for (i
= 0; i
< count
; ++i
) {
967 UnmanagedSymbol
*ubin
= lookup_unmanaged_binary (cachedus
[i
]->addr
);
968 if (ubin
== cachedus
[i
])
970 cachedus
[i
]->parent
= ubin
;
975 print_usym (UnmanagedSymbol
* um
)
978 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
);
980 fprintf (outfile
, "\t%6zd %6.2f %s\n", um
->sample_hits
, um
->sample_hits
*100.0/num_stat_samples
, um
->name
);
984 sym_percent (uintptr_t sample_hits
)
989 pc
= sample_hits
*100.0/num_stat_samples
;
997 int count
= 0, msize
= 0;
998 int unmanaged_hits
= 0;
999 int unresolved_hits
= 0;
1000 MethodDesc
** cachedm
= NULL
;
1001 int ucount
= 0, usize
= 0;
1002 UnmanagedSymbol
** cachedus
= NULL
;
1003 if (!num_stat_samples
)
1005 mono_qsort (usymbols
, usymbols_num
, sizeof (UnmanagedSymbol
*), compare_usymbol_addr
);
1006 for (i
= 0; i
< num_stat_samples
; ++i
) {
1007 MethodDesc
*m
= lookup_method_by_ip (stat_samples
[i
]);
1009 if (!m
->sample_hits
) {
1010 if (count
== msize
) {
1014 cachedm
= (MethodDesc
**) g_realloc (cachedm
, sizeof (void*) * msize
);
1016 cachedm
[count
++] = m
;
1020 UnmanagedSymbol
*usym
= lookup_unmanaged_symbol (stat_samples
[i
]);
1023 //printf ("unmanaged hit at %p\n", (void*)stat_samples [i]);
1024 usym
= lookup_unmanaged_binary (stat_samples
[i
]);
1027 if (!usym
->sample_hits
) {
1028 if (ucount
== usize
) {
1032 cachedus
= (UnmanagedSymbol
**) g_realloc (cachedus
, sizeof (void*) * usize
);
1034 cachedus
[ucount
++] = usym
;
1036 usym
->sample_hits
++;
1041 mono_qsort (cachedm
, count
, sizeof (MethodDesc
*), compare_method_samples
);
1042 mono_qsort (cachedus
, ucount
, sizeof (UnmanagedSymbol
*), compare_usymbol_samples
);
1043 set_usym_parent (cachedus
, ucount
);
1044 fprintf (outfile
, "\nStatistical samples summary\n");
1045 fprintf (outfile
, "\tSample type: %s\n", sample_type_name (stat_sample_desc
[0]));
1046 fprintf (outfile
, "\tUnmanaged hits: %6d (%4.1f%%)\n", unmanaged_hits
, (100.0*unmanaged_hits
)/num_stat_samples
);
1047 fprintf (outfile
, "\tManaged hits: %6d (%4.1f%%)\n", num_stat_samples
- unmanaged_hits
, (100.0*(num_stat_samples
-unmanaged_hits
))/num_stat_samples
);
1048 fprintf (outfile
, "\tUnresolved hits: %6d (%4.1f%%)\n", unresolved_hits
, (100.0*unresolved_hits
)/num_stat_samples
);
1049 fprintf (outfile
, "\t%6s %6s %s\n", "Hits", "%", "Method name");
1052 while (i
< count
|| u
< ucount
) {
1054 MethodDesc
*m
= cachedm
[i
];
1056 UnmanagedSymbol
*um
= cachedus
[u
];
1057 if (um
->sample_hits
> m
->sample_hits
) {
1058 if (!sym_percent (um
->sample_hits
))
1065 if (!sym_percent (m
->sample_hits
))
1067 fprintf (outfile
, "\t%6d %6.2f %s\n", m
->sample_hits
, m
->sample_hits
*100.0/num_stat_samples
, m
->name
);
1072 UnmanagedSymbol
*um
= cachedus
[u
];
1073 if (!sym_percent (um
->sample_hits
))
1082 typedef struct _HeapClassDesc HeapClassDesc
;
1084 HeapClassDesc
*klass
;
1088 struct _HeapClassDesc
{
1092 HeapClassRevRef
*rev_hash
;
1095 uintptr_t pinned_references
;
1096 uintptr_t root_references
;
1100 add_rev_class_hashed (HeapClassRevRef
*rev_hash
, uintptr_t size
, HeapClassDesc
*hklass
, uint64_t value
)
1103 uintptr_t start_pos
;
1104 start_pos
= (hklass
->klass
->klass
>> 2) % size
;
1105 assert (start_pos
< size
);
1108 if (rev_hash
[i
].klass
== hklass
) {
1109 rev_hash
[i
].count
+= value
;
1111 } else if (!rev_hash
[i
].klass
) {
1112 rev_hash
[i
].klass
= hklass
;
1113 rev_hash
[i
].count
+= value
;
1115 for (i
= 0; i
< size
; ++i
)
1116 if (rev_hash
[i
].klass
&& rev_hash
[i
].klass
->klass
== hklass
->klass
)
1118 assert (start_pos
== 1);
1124 } while (i
!= start_pos
);
1125 /* should not happen */
1126 printf ("failed revref store\n");
1131 add_heap_class_rev (HeapClassDesc
*from
, HeapClassDesc
*to
)
1134 if (to
->rev_count
* 2 >= to
->rev_hash_size
) {
1136 uintptr_t old_size
= to
->rev_hash_size
;
1137 to
->rev_hash_size
*= 2;
1138 if (to
->rev_hash_size
== 0)
1139 to
->rev_hash_size
= 4;
1140 n
= (HeapClassRevRef
*) g_calloc (sizeof (HeapClassRevRef
) * to
->rev_hash_size
, 1);
1141 for (i
= 0; i
< old_size
; ++i
) {
1142 if (to
->rev_hash
[i
].klass
)
1143 add_rev_class_hashed (n
, to
->rev_hash_size
, to
->rev_hash
[i
].klass
, to
->rev_hash
[i
].count
);
1146 g_free (to
->rev_hash
);
1149 to
->rev_count
+= add_rev_class_hashed (to
->rev_hash
, to
->rev_hash_size
, from
, 1);
1154 HeapClassDesc
*hklass
;
1159 typedef struct _HeapShot HeapShot
;
1165 HeapClassDesc
**class_hash
;
1166 HeapClassDesc
**sorted
;
1167 HeapObjectDesc
**objects_hash
;
1168 uintptr_t objects_count
;
1169 uintptr_t objects_hash_size
;
1170 uintptr_t num_roots
;
1172 uintptr_t *roots_extra
;
1176 static HeapShot
*heap_shots
= NULL
;
1177 static int num_heap_shots
= 0;
1180 new_heap_shot (uint64_t timestamp
)
1182 HeapShot
*hs
= (HeapShot
*) g_calloc (sizeof (HeapShot
), 1);
1184 hs
->class_hash
= (HeapClassDesc
**) g_calloc (sizeof (void*), hs
->hash_size
);
1185 hs
->timestamp
= timestamp
;
1187 hs
->next
= heap_shots
;
1192 static HeapClassDesc
*
1193 heap_class_lookup (HeapShot
*hs
, ClassDesc
*klass
)
1196 unsigned int start_pos
;
1197 start_pos
= ((uintptr_t)klass
->klass
>> 2) % hs
->hash_size
;
1200 HeapClassDesc
* cd
= hs
->class_hash
[i
];
1203 if (cd
->klass
== klass
)
1206 if (++i
== hs
->hash_size
)
1208 } while (i
!= start_pos
);
1213 add_heap_hashed (HeapClassDesc
**hash
, HeapClassDesc
**retv
, uintptr_t hsize
, ClassDesc
*klass
, uint64_t size
, uint64_t count
)
1216 uintptr_t start_pos
;
1217 start_pos
= ((uintptr_t)klass
->klass
>> 2) % hsize
;
1220 if (hash
[i
] && hash
[i
]->klass
== klass
) {
1221 hash
[i
]->total_size
+= size
;
1222 hash
[i
]->count
+= count
;
1225 } else if (!hash
[i
]) {
1230 hash
[i
] = (HeapClassDesc
*) g_calloc (sizeof (HeapClassDesc
), 1);
1231 hash
[i
]->klass
= klass
;
1232 hash
[i
]->total_size
+= size
;
1233 hash
[i
]->count
+= count
;
1240 } while (i
!= start_pos
);
1241 /* should not happen */
1242 printf ("failed heap class store\n");
1246 static HeapClassDesc
*
1247 add_heap_shot_class (HeapShot
*hs
, ClassDesc
*klass
, uint64_t size
)
1251 if (hs
->class_count
* 2 >= hs
->hash_size
) {
1253 int old_size
= hs
->hash_size
;
1255 if (hs
->hash_size
== 0)
1257 n
= (HeapClassDesc
**) g_calloc (sizeof (void*) * hs
->hash_size
, 1);
1258 for (i
= 0; i
< old_size
; ++i
) {
1259 res
= hs
->class_hash
[i
];
1260 if (hs
->class_hash
[i
])
1261 add_heap_hashed (n
, &res
, hs
->hash_size
, hs
->class_hash
[i
]->klass
, hs
->class_hash
[i
]->total_size
, hs
->class_hash
[i
]->count
);
1264 g_free (hs
->class_hash
);
1268 hs
->class_count
+= add_heap_hashed (hs
->class_hash
, &res
, hs
->hash_size
, klass
, size
, 1);
1269 //if (res->count == 1)
1270 // printf ("added heap class: %s\n", res->klass->name);
1274 static HeapObjectDesc
*
1275 alloc_heap_obj (uintptr_t objaddr
, HeapClassDesc
*hklass
, uintptr_t num_refs
)
1277 HeapObjectDesc
* ho
= (HeapObjectDesc
*) g_calloc (sizeof (HeapObjectDesc
) + num_refs
* sizeof (uintptr_t), 1);
1278 ho
->objaddr
= objaddr
;
1279 ho
->hklass
= hklass
;
1280 ho
->num_refs
= num_refs
;
1285 heap_shot_find_obj_slot (HeapShot
*hs
, uintptr_t objaddr
)
1288 uintptr_t start_pos
;
1289 HeapObjectDesc
**hash
= hs
->objects_hash
;
1290 if (hs
->objects_hash_size
== 0)
1292 start_pos
= ((uintptr_t)objaddr
>> 3) % hs
->objects_hash_size
;
1295 if (hash
[i
] && hash
[i
]->objaddr
== objaddr
) {
1297 } else if (!hash
[i
]) {
1301 if (++i
== hs
->objects_hash_size
)
1303 } while (i
!= start_pos
);
1304 /* should not happen */
1305 //printf ("failed heap obj slot\n");
1309 static HeapObjectDesc
*
1310 heap_shot_obj_add_refs (HeapShot
*hs
, uintptr_t objaddr
, uintptr_t num
, uintptr_t *ref_offset
)
1312 HeapObjectDesc
**hash
= hs
->objects_hash
;
1313 uintptr_t i
= heap_shot_find_obj_slot (hs
, objaddr
);
1315 HeapObjectDesc
* ho
= alloc_heap_obj (objaddr
, hash
[i
]->hklass
, hash
[i
]->num_refs
+ num
);
1316 *ref_offset
= hash
[i
]->num_refs
;
1317 memcpy (ho
->refs
, hash
[i
]->refs
, hash
[i
]->num_refs
* sizeof (uintptr_t));
1322 /* should not happen */
1323 printf ("failed heap obj update\n");
1329 add_heap_hashed_obj (HeapObjectDesc
**hash
, uintptr_t hsize
, HeapObjectDesc
*obj
)
1332 uintptr_t start_pos
;
1333 start_pos
= ((uintptr_t)obj
->objaddr
>> 3) % hsize
;
1336 if (hash
[i
] && hash
[i
]->objaddr
== obj
->objaddr
) {
1337 printf ("duplicate object!\n");
1339 } else if (!hash
[i
]) {
1346 } while (i
!= start_pos
);
1347 /* should not happen */
1348 printf ("failed heap obj store\n");
1353 add_heap_shot_obj (HeapShot
*hs
, HeapObjectDesc
*obj
)
1356 if (hs
->objects_count
* 2 >= hs
->objects_hash_size
) {
1358 uintptr_t old_size
= hs
->objects_hash_size
;
1359 hs
->objects_hash_size
*= 2;
1360 if (hs
->objects_hash_size
== 0)
1361 hs
->objects_hash_size
= 4;
1362 n
= (HeapObjectDesc
**) g_calloc (sizeof (void*) * hs
->objects_hash_size
, 1);
1363 for (i
= 0; i
< old_size
; ++i
) {
1364 if (hs
->objects_hash
[i
])
1365 add_heap_hashed_obj (n
, hs
->objects_hash_size
, hs
->objects_hash
[i
]);
1367 if (hs
->objects_hash
)
1368 g_free (hs
->objects_hash
);
1369 hs
->objects_hash
= n
;
1371 hs
->objects_count
+= add_heap_hashed_obj (hs
->objects_hash
, hs
->objects_hash_size
, obj
);
1375 heap_shot_resolve_reverse_refs (HeapShot
*hs
)
1378 for (i
= 0; i
< hs
->objects_hash_size
; ++i
) {
1380 HeapObjectDesc
*ho
= hs
->objects_hash
[i
];
1383 for (r
= 0; r
< ho
->num_refs
; ++r
) {
1384 uintptr_t oi
= heap_shot_find_obj_slot (hs
, ho
->refs
[r
]);
1385 add_heap_class_rev (ho
->hklass
, hs
->objects_hash
[oi
]->hklass
);
1391 #define MARK_BLACK 2
1394 heap_shot_mark_objects (HeapShot
*hs
)
1397 unsigned char *marks
;
1398 HeapObjectDesc
*obj
, *ref
;
1400 uintptr_t num_marked
= 0, num_unmarked
;
1401 for (i
= 0; i
< hs
->num_roots
; ++i
) {
1403 oi
= heap_shot_find_obj_slot (hs
, hs
->roots
[i
]);
1407 obj
= hs
->objects_hash
[oi
];
1409 if (hs
->roots_types
[i
] & MONO_PROFILER_GC_ROOT_PINNING
)
1410 cd
->pinned_references
++;
1411 cd
->root_references
++;
1415 /* consistency checks: it seems not all the objects are walked in the heap in some cases */
1416 marks
= (unsigned char *) g_calloc (hs
->objects_hash_size
, 1);
1419 for (i
= 0; i
< hs
->num_roots
; ++i
) {
1420 oi
= heap_shot_find_obj_slot (hs
, hs
->roots
[i
]);
1422 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
);
1425 obj
= hs
->objects_hash
[oi
];
1427 marks
[oi
] = obj
->num_refs
? MARK_GRAY
: MARK_BLACK
;
1432 while (marked_some
) {
1434 for (i
= 0; i
< hs
->objects_hash_size
; ++i
) {
1435 if (marks
[i
] != MARK_GRAY
)
1437 marks
[i
] = MARK_BLACK
;
1438 obj
= hs
->objects_hash
[i
];
1439 for (r
= 0; r
< obj
->num_refs
; ++r
) {
1440 oi
= heap_shot_find_obj_slot (hs
, obj
->refs
[r
]);
1442 fprintf (outfile
, "referenced obj %p not found in heap\n", (void*)obj
->refs
[r
]);
1445 ref
= hs
->objects_hash
[oi
];
1447 marks
[oi
] = ref
->num_refs
? MARK_GRAY
: MARK_BLACK
;
1455 for (i
= 0; i
< hs
->objects_hash_size
; ++i
) {
1456 if (hs
->objects_hash
[i
] && !marks
[i
]) {
1458 fprintf (outfile
, "object %p (%s) unmarked\n", (void*)hs
->objects_hash
[i
], hs
->objects_hash
[i
]->hklass
->klass
->name
);
1461 fprintf (outfile
, "Total unmarked: %zd/%zd\n", num_unmarked
, hs
->objects_count
);
1466 heap_shot_free_objects (HeapShot
*hs
)
1469 for (i
= 0; i
< hs
->objects_hash_size
; ++i
) {
1470 HeapObjectDesc
*ho
= hs
->objects_hash
[i
];
1474 if (hs
->objects_hash
)
1475 g_free (hs
->objects_hash
);
1476 hs
->objects_hash
= NULL
;
1477 hs
->objects_hash_size
= 0;
1478 hs
->objects_count
= 0;
1487 MethodDesc
*methods
[1];
1490 static BackTrace
*backtrace_hash
[HASH_SIZE
];
1491 static BackTrace
**backtraces
= NULL
;
1492 static int num_backtraces
= 0;
1493 static int next_backtrace
= 0;
1496 hash_backtrace (int count
, MethodDesc
**methods
)
1500 for (i
= 0; i
< count
; ++i
) {
1501 hash
= (hash
<< 5) - hash
+ methods
[i
]->method
;
1507 compare_backtrace (BackTrace
*bt
, int count
, MethodDesc
**methods
)
1510 if (bt
->count
!= count
)
1512 for (i
= 0; i
< count
; ++i
)
1513 if (methods
[i
] != bt
->methods
[i
])
1519 add_backtrace (int count
, MethodDesc
**methods
)
1521 int hash
= hash_backtrace (count
, methods
);
1522 int slot
= (hash
& 0xffff) % HASH_SIZE
;
1523 BackTrace
*bt
= backtrace_hash
[slot
];
1525 if (bt
->hash
== hash
&& compare_backtrace (bt
, count
, methods
))
1529 bt
= (BackTrace
*) g_malloc (sizeof (BackTrace
) + ((count
- 1) * sizeof (void*)));
1530 bt
->next
= backtrace_hash
[slot
];
1531 backtrace_hash
[slot
] = bt
;
1532 if (next_backtrace
== num_backtraces
) {
1533 num_backtraces
*= 2;
1534 if (!num_backtraces
)
1535 num_backtraces
= 16;
1536 backtraces
= (BackTrace
**) g_realloc (backtraces
, sizeof (void*) * num_backtraces
);
1538 bt
->id
= next_backtrace
++;
1539 backtraces
[bt
->id
] = bt
;
1542 for (slot
= 0; slot
< count
; ++slot
)
1543 bt
->methods
[slot
] = methods
[slot
];
1548 typedef struct _MonitorDesc MonitorDesc
;
1549 typedef struct _ThreadContext ThreadContext
;
1550 typedef struct _DomainContext DomainContext
;
1551 typedef struct _RemCtxContext RemCtxContext
;
1555 #if defined (HAVE_SYS_ZLIB)
1569 uint64_t startup_time
;
1570 ThreadContext
*threads
;
1571 ThreadContext
*current_thread
;
1572 DomainContext
*domains
;
1573 DomainContext
*current_domain
;
1574 RemCtxContext
*remctxs
;
1575 RemCtxContext
*current_remctx
;
1578 struct _ThreadContext
{
1579 ThreadContext
*next
;
1582 /* emulated stack */
1584 uint64_t *time_stack
;
1585 uint64_t *callee_time_stack
;
1587 uint64_t contention_start
;
1588 MonitorDesc
*monitor
;
1591 HeapShot
*current_heap_shot
;
1592 uintptr_t num_roots
;
1593 uintptr_t size_roots
;
1595 uintptr_t *roots_extra
;
1597 uint64_t gc_start_times
[3];
1600 struct _DomainContext
{
1601 DomainContext
*next
;
1603 const char *friendly_name
;
1606 struct _RemCtxContext
{
1607 RemCtxContext
*next
;
1613 ensure_buffer (ProfContext
*ctx
, int size
)
1615 if (ctx
->size
< size
) {
1616 ctx
->buf
= (unsigned char *) g_realloc (ctx
->buf
, size
);
1622 load_data (ProfContext
*ctx
, int size
)
1624 ensure_buffer (ctx
, size
);
1625 #if defined (HAVE_SYS_ZLIB)
1627 int r
= gzread (ctx
->gzfile
, ctx
->buf
, size
);
1629 return size
== 0? 1: 0;
1634 int r
= fread (ctx
->buf
, size
, 1, ctx
->file
);
1636 return size
== 0? 1: 0;
1641 static ThreadContext
*
1642 get_thread (ProfContext
*ctx
, intptr_t thread_id
)
1644 ThreadContext
*thread
;
1645 if (ctx
->current_thread
&& ctx
->current_thread
->thread_id
== thread_id
)
1646 return ctx
->current_thread
;
1647 thread
= ctx
->threads
;
1649 if (thread
->thread_id
== thread_id
) {
1652 thread
= thread
->next
;
1654 thread
= (ThreadContext
*) g_calloc (sizeof (ThreadContext
), 1);
1655 thread
->next
= ctx
->threads
;
1656 ctx
->threads
= thread
;
1657 thread
->thread_id
= thread_id
;
1658 thread
->last_time
= 0;
1659 thread
->stack_id
= 0;
1660 thread
->stack_size
= 32;
1661 thread
->stack
= (MethodDesc
**) g_malloc (thread
->stack_size
* sizeof (void*));
1662 thread
->time_stack
= (uint64_t *) g_malloc (thread
->stack_size
* sizeof (uint64_t));
1663 thread
->callee_time_stack
= (uint64_t *) g_malloc (thread
->stack_size
* sizeof (uint64_t));
1667 static DomainContext
*
1668 get_domain (ProfContext
*ctx
, intptr_t domain_id
)
1670 if (ctx
->current_domain
&& ctx
->current_domain
->domain_id
== domain_id
)
1671 return ctx
->current_domain
;
1673 DomainContext
*domain
= ctx
->domains
;
1676 if (domain
->domain_id
== domain_id
)
1679 domain
= domain
->next
;
1682 domain
= (DomainContext
*) g_calloc (sizeof (DomainContext
), 1);
1683 domain
->next
= ctx
->domains
;
1684 ctx
->domains
= domain
;
1685 domain
->domain_id
= domain_id
;
1690 static RemCtxContext
*
1691 get_remctx (ProfContext
*ctx
, intptr_t remctx_id
)
1693 if (ctx
->current_remctx
&& ctx
->current_remctx
->remctx_id
== remctx_id
)
1694 return ctx
->current_remctx
;
1696 RemCtxContext
*remctx
= ctx
->remctxs
;
1699 if (remctx
->remctx_id
== remctx_id
)
1702 remctx
= remctx
->next
;
1705 remctx
= (RemCtxContext
*) g_calloc (sizeof (RemCtxContext
), 1);
1706 remctx
->next
= ctx
->remctxs
;
1707 ctx
->remctxs
= remctx
;
1708 remctx
->remctx_id
= remctx_id
;
1713 static ThreadContext
*
1714 load_thread (ProfContext
*ctx
, intptr_t thread_id
)
1716 ThreadContext
*thread
= get_thread (ctx
, thread_id
);
1717 ctx
->current_thread
= thread
;
1722 ensure_thread_stack (ThreadContext
*thread
)
1724 if (thread
->stack_id
== thread
->stack_size
) {
1725 thread
->stack_size
*= 2;
1726 thread
->stack
= (MethodDesc
**) g_realloc (thread
->stack
, thread
->stack_size
* sizeof (void*));
1727 thread
->time_stack
= (uint64_t *) g_realloc (thread
->time_stack
, thread
->stack_size
* sizeof (uint64_t));
1728 thread
->callee_time_stack
= (uint64_t *) g_realloc (thread
->callee_time_stack
, thread
->stack_size
* sizeof (uint64_t));
1733 add_trace_hashed (CallContext
*traces
, int size
, BackTrace
*bt
, uint64_t value
)
1736 unsigned int start_pos
;
1737 start_pos
= bt
->hash
% size
;
1740 if (traces
[i
].bt
== bt
) {
1741 traces
[i
].count
+= value
;
1743 } else if (!traces
[i
].bt
) {
1745 traces
[i
].count
+= value
;
1751 } while (i
!= start_pos
);
1752 /* should not happen */
1753 printf ("failed trace store\n");
1758 add_trace_bt (BackTrace
*bt
, TraceDesc
*trace
, uint64_t value
)
1761 if (!collect_traces
)
1763 if (trace
->count
* 2 >= trace
->size
) {
1765 int old_size
= trace
->size
;
1767 if (trace
->size
== 0)
1769 n
= (CallContext
*) g_calloc (sizeof (CallContext
) * trace
->size
, 1);
1770 for (i
= 0; i
< old_size
; ++i
) {
1771 if (trace
->traces
[i
].bt
)
1772 add_trace_hashed (n
, trace
->size
, trace
->traces
[i
].bt
, trace
->traces
[i
].count
);
1775 g_free (trace
->traces
);
1778 trace
->count
+= add_trace_hashed (trace
->traces
, trace
->size
, bt
, value
);
1782 add_trace_thread (ThreadContext
*thread
, TraceDesc
*trace
, uint64_t value
)
1785 int count
= thread
->stack_id
;
1786 if (!collect_traces
)
1788 if (count
> trace_max
)
1790 bt
= add_backtrace (count
, thread
->stack
+ thread
->stack_id
- count
);
1791 add_trace_bt (bt
, trace
, value
);
1796 add_trace_methods (MethodDesc
**methods
, int count
, TraceDesc
*trace
, uint64_t value
)
1799 if (!collect_traces
)
1801 if (count
> trace_max
)
1803 bt
= add_backtrace (count
, methods
);
1804 add_trace_bt (bt
, trace
, value
);
1809 thread_add_root (ThreadContext
*ctx
, uintptr_t obj
, int root_type
, uintptr_t extra_info
)
1811 if (ctx
->num_roots
== ctx
->size_roots
) {
1812 int new_size
= ctx
->size_roots
* 2;
1815 ctx
->roots
= (uintptr_t *) g_realloc (ctx
->roots
, new_size
* sizeof (uintptr_t));
1816 ctx
->roots_extra
= (uintptr_t *) g_realloc (ctx
->roots_extra
, new_size
* sizeof (uintptr_t));
1817 ctx
->roots_types
= (int *) g_realloc (ctx
->roots_types
, new_size
* sizeof (int));
1818 ctx
->size_roots
= new_size
;
1820 ctx
->roots_types
[ctx
->num_roots
] = root_type
;
1821 ctx
->roots_extra
[ctx
->num_roots
] = extra_info
;
1822 ctx
->roots
[ctx
->num_roots
++] = obj
;
1826 compare_callc (const void *a
, const void *b
)
1828 const CallContext
*A
= (const CallContext
*)a
;
1829 const CallContext
*B
= (const CallContext
*)b
;
1830 if (B
->count
== A
->count
)
1832 if (B
->count
< A
->count
)
1838 sort_context_array (TraceDesc
* traces
)
1841 for (i
= 0, j
= 0; i
< traces
->size
; ++i
) {
1842 if (traces
->traces
[i
].bt
) {
1843 traces
->traces
[j
].bt
= traces
->traces
[i
].bt
;
1844 traces
->traces
[j
].count
= traces
->traces
[i
].count
;
1848 mono_qsort (traces
->traces
, traces
->count
, sizeof (CallContext
), compare_callc
);
1852 push_method (ThreadContext
*thread
, MethodDesc
*method
, uint64_t timestamp
)
1854 ensure_thread_stack (thread
);
1855 thread
->time_stack
[thread
->stack_id
] = timestamp
;
1856 thread
->callee_time_stack
[thread
->stack_id
] = 0;
1857 thread
->stack
[thread
->stack_id
++] = method
;
1858 method
->recurse_count
++;
1862 pop_method (ThreadContext
*thread
, MethodDesc
*method
, uint64_t timestamp
)
1864 method
->recurse_count
--;
1865 if (thread
->stack_id
> 0 && thread
->stack
[thread
->stack_id
- 1] == method
) {
1869 if (timestamp
< thread
->time_stack
[thread
->stack_id
])
1870 fprintf (outfile
, "time went backwards for %s\n", method
->name
);
1871 tdiff
= timestamp
- thread
->time_stack
[thread
->stack_id
];
1872 if (thread
->callee_time_stack
[thread
->stack_id
] > tdiff
)
1873 fprintf (outfile
, "callee time bigger for %s\n", method
->name
);
1874 method
->self_time
+= tdiff
- thread
->callee_time_stack
[thread
->stack_id
];
1875 method
->callee_time
+= thread
->callee_time_stack
[thread
->stack_id
];
1876 if (thread
->stack_id
)
1877 thread
->callee_time_stack
[thread
->stack_id
- 1] += tdiff
;
1878 //fprintf (outfile, "method %s took %d\n", method->name, (int)(tdiff/1000));
1880 fprintf (outfile
, "unmatched leave at stack pos: %d for method %s\n", thread
->stack_id
, method
->name
);
1885 uint64_t total_time
;
1889 static GCDesc gc_info
[3];
1890 static uint64_t max_heap_size
;
1891 static uint64_t gc_object_moves
;
1892 static int gc_resizes
;
1899 TraceDesc destroy_traces
;
1901 static HandleInfo handle_info
[4];
1904 gc_event_name (int ev
)
1907 case MONO_GC_EVENT_START
: return "start";
1908 case MONO_GC_EVENT_MARK_START
: return "mark start";
1909 case MONO_GC_EVENT_MARK_END
: return "mark end";
1910 case MONO_GC_EVENT_RECLAIM_START
: return "reclaim start";
1911 case MONO_GC_EVENT_RECLAIM_END
: return "reclaim end";
1912 case MONO_GC_EVENT_END
: return "end";
1913 case MONO_GC_EVENT_PRE_STOP_WORLD
: return "pre stop";
1914 case MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED
: return "pre stop lock";
1915 case MONO_GC_EVENT_POST_STOP_WORLD
: return "post stop";
1916 case MONO_GC_EVENT_PRE_START_WORLD
: return "pre start";
1917 case MONO_GC_EVENT_POST_START_WORLD
: return "post start";
1918 case MONO_GC_EVENT_POST_START_WORLD_UNLOCKED
: return "post start unlock";
1925 sync_point_name (int type
)
1928 case SYNC_POINT_PERIODIC
: return "periodic";
1929 case SYNC_POINT_WORLD_STOP
: return "world stop";
1930 case SYNC_POINT_WORLD_START
: return "world start";
1936 static uint64_t clause_summary
[MONO_EXCEPTION_CLAUSE_FAULT
+ 1];
1937 static uint64_t throw_count
= 0;
1938 static TraceDesc exc_traces
;
1941 clause_name (int type
)
1944 case MONO_EXCEPTION_CLAUSE_NONE
: return "catch";
1945 case MONO_EXCEPTION_CLAUSE_FILTER
: return "filter";
1946 case MONO_EXCEPTION_CLAUSE_FINALLY
: return "finally";
1947 case MONO_EXCEPTION_CLAUSE_FAULT
: return "fault";
1948 default: return "invalid";
1952 static uint64_t monitor_contention
;
1953 static uint64_t monitor_failed
;
1954 static uint64_t monitor_acquired
;
1956 struct _MonitorDesc
{
1959 uintptr_t contentions
;
1961 uint64_t max_wait_time
;
1965 static MonitorDesc
* monitor_hash
[SMALL_HASH_SIZE
] = {0};
1966 static int num_monitors
= 0;
1969 lookup_monitor (uintptr_t objid
)
1971 int slot
= ((objid
>> 3) & 0xffff) % SMALL_HASH_SIZE
;
1972 MonitorDesc
*cd
= monitor_hash
[slot
];
1973 while (cd
&& cd
->objid
!= objid
)
1976 cd
= (MonitorDesc
*) g_calloc (sizeof (MonitorDesc
), 1);
1978 cd
->next
= monitor_hash
[slot
];
1979 monitor_hash
[slot
] = cd
;
1986 monitor_ev_name (int ev
)
1989 case MONO_PROFILER_MONITOR_CONTENTION
: return "contended";
1990 case MONO_PROFILER_MONITOR_DONE
: return "acquired";
1991 case MONO_PROFILER_MONITOR_FAIL
: return "not taken";
1992 default: return "invalid";
1997 get_handle_name (int htype
)
2000 case 0: return "weak";
2001 case 1: return "weaktrack";
2002 case 2: return "normal";
2003 case 3: return "pinned";
2004 default: return "unknown";
2009 get_root_name (int rtype
)
2011 switch (rtype
& MONO_PROFILER_GC_ROOT_TYPEMASK
) {
2012 case MONO_PROFILER_GC_ROOT_STACK
: return "stack";
2013 case MONO_PROFILER_GC_ROOT_FINALIZER
: return "finalizer";
2014 case MONO_PROFILER_GC_ROOT_HANDLE
: return "handle";
2015 case MONO_PROFILER_GC_ROOT_OTHER
: return "other";
2016 case MONO_PROFILER_GC_ROOT_MISC
: return "misc";
2017 default: return "unknown";
2022 decode_uleb128 (uint8_t *buf
, uint8_t **endbuf
)
2029 res
|= (((uint64_t) (b
& 0x7f)) << shift
);
2043 decode_sleb128 (uint8_t *buf
, uint8_t **endbuf
)
2053 res
= res
| (((intptr_t) (b
& 0x7f)) << shift
);
2057 if (shift
< sizeof (intptr_t) * 8 && (b
& 0x40))
2058 res
|= - ((intptr_t) 1 << shift
);
2070 decode_bt (ProfContext
*ctx
, MethodDesc
** sframes
, int *size
, unsigned char *p
, unsigned char **endp
, intptr_t ptr_base
, intptr_t *method_base
)
2072 MethodDesc
**frames
;
2074 if (ctx
->data_version
< 13)
2075 decode_uleb128 (p
, &p
); /* flags */
2076 int count
= decode_uleb128 (p
, &p
);
2078 frames
= (MethodDesc
**) g_malloc (count
* sizeof (void*));
2081 for (i
= 0; i
< count
; ++i
) {
2082 intptr_t ptrdiff
= decode_sleb128 (p
, &p
);
2083 if (ctx
->data_version
> 12) {
2084 *method_base
+= ptrdiff
;
2085 frames
[i
] = lookup_method (*method_base
);
2087 frames
[i
] = lookup_method (ptr_base
+ ptrdiff
);
2096 tracked_creation (uintptr_t obj
, ClassDesc
*cd
, uint64_t size
, BackTrace
*bt
, uint64_t timestamp
)
2099 for (i
= 0; i
< num_tracked_objects
; ++i
) {
2100 if (tracked_objects
[i
] != obj
)
2102 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);
2103 if (bt
&& bt
->count
) {
2105 for (k
= 0; k
< bt
->count
; ++k
)
2106 fprintf (outfile
, "\t%s\n", bt
->methods
[k
]->name
);
2112 track_handle (uintptr_t obj
, int htype
, uint32_t handle
, BackTrace
*bt
, uint64_t timestamp
)
2115 for (i
= 0; i
< num_tracked_objects
; ++i
) {
2116 if (tracked_objects
[i
] != obj
)
2118 fprintf (outfile
, "Object %p referenced from handle %u at %.3f secs.\n", (void*)obj
, handle
, (timestamp
- startup_time
) / 1000000000.0);
2119 if (bt
&& bt
->count
) {
2121 for (k
= 0; k
< bt
->count
; ++k
)
2122 fprintf (outfile
, "\t%s\n", bt
->methods
[k
]->name
);
2128 track_move (uintptr_t src
, uintptr_t dst
)
2131 for (i
= 0; i
< num_tracked_objects
; ++i
) {
2132 if (tracked_objects
[i
] == src
)
2133 fprintf (outfile
, "Object %p moved to %p\n", (void*)src
, (void*)dst
);
2134 else if (tracked_objects
[i
] == dst
)
2135 fprintf (outfile
, "Object %p moved from %p\n", (void*)dst
, (void*)src
);
2140 track_obj_reference (uintptr_t obj
, uintptr_t parent
, ClassDesc
*cd
)
2143 for (i
= 0; i
< num_tracked_objects
; ++i
) {
2144 if (tracked_objects
[i
] == obj
)
2145 fprintf (outfile
, "Object %p referenced from %p (%s).\n", (void*)obj
, (void*)parent
, cd
->name
);
2150 found_object (uintptr_t obj
)
2152 num_tracked_objects
++;
2153 tracked_objects
= (uintptr_t *) g_realloc (tracked_objects
, num_tracked_objects
* sizeof (tracked_objects
[0]));
2154 tracked_objects
[num_tracked_objects
- 1] = obj
;
2157 static int num_jit_helpers
= 0;
2158 static int jit_helpers_code_size
= 0;
2161 code_buffer_desc (int type
)
2164 case MONO_PROFILER_CODE_BUFFER_METHOD
:
2166 case MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE
:
2167 return "method trampoline";
2168 case MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE
:
2169 return "unbox trampoline";
2170 case MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE
:
2171 return "imt trampoline";
2172 case MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE
:
2173 return "generics trampoline";
2174 case MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
:
2175 return "specific trampoline";
2176 case MONO_PROFILER_CODE_BUFFER_HELPER
:
2177 return "misc helper";
2178 case MONO_PROFILER_CODE_BUFFER_MONITOR
:
2179 return "monitor/lock";
2180 case MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE
:
2181 return "delegate invoke";
2182 case MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING
:
2183 return "exception handling";
2185 return "unspecified";
2189 #define OBJ_ADDR(diff) ((obj_base + diff) << 3)
2190 #define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
2194 #define BUFFER_HEADER_SIZE 48
2197 int count
, min_size
, max_size
, bytes
;
2200 static int buffer_count
;
2201 static EventStat stats
[256];
2204 record_event_stats (int type
, int size
)
2206 ++stats
[type
].count
;
2207 if (!stats
[type
].min_size
)
2208 stats
[type
].min_size
= size
;
2209 stats
[type
].min_size
= MIN (stats
[type
].min_size
, size
);
2210 stats
[type
].max_size
= MAX (stats
[type
].max_size
, size
);
2211 stats
[type
].bytes
+= size
;
2215 decode_buffer (ProfContext
*ctx
)
2222 intptr_t method_base
;
2224 uint64_t file_offset
;
2226 ThreadContext
*thread
;
2228 #ifdef HAVE_SYS_ZLIB
2230 file_offset
= gztell (ctx
->gzfile
);
2233 file_offset
= ftell (ctx
->file
);
2234 if (!load_data (ctx
, 48))
2237 if (read_int32 (p
) != BUF_ID
) {
2238 fprintf (outfile
, "Incorrect buffer id: 0x%x\n", read_int32 (p
));
2239 for (i
= 0; i
< 48; ++i
) {
2240 fprintf (outfile
, "0x%x%s", p
[i
], i
% 8?" ":"\n");
2244 len
= read_int32 (p
+ 4);
2245 time_base
= read_int64 (p
+ 8);
2246 ptr_base
= read_int64 (p
+ 16);
2247 obj_base
= read_int64 (p
+ 24);
2248 thread_id
= read_int64 (p
+ 32);
2249 method_base
= read_int64 (p
+ 40);
2251 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
);
2252 thread
= load_thread (ctx
, thread_id
);
2253 if (!load_data (ctx
, len
))
2258 if (!startup_time
) {
2259 startup_time
= time_base
;
2260 if (use_time_filter
) {
2261 time_from
+= startup_time
;
2262 time_to
+= startup_time
;
2265 for (i
= 0; i
< thread
->stack_id
; ++i
)
2266 thread
->stack
[i
]->recurse_count
++;
2270 unsigned char *start
= p
;
2271 unsigned char event
= *p
;
2274 int subtype
= *p
& 0xf0;
2275 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2276 LOG_TIME (time_base
, tdiff
);
2278 if (subtype
== TYPE_GC_RESIZE
) {
2279 uint64_t new_size
= decode_uleb128 (p
, &p
);
2281 fprintf (outfile
, "gc heap resized to %llu\n", (unsigned long long) new_size
);
2283 if (new_size
> max_heap_size
)
2284 max_heap_size
= new_size
;
2285 } else if (subtype
== TYPE_GC_EVENT
) {
2287 if (ctx
->data_version
> 12)
2290 ev
= decode_uleb128 (p
, &p
);
2292 if (ctx
->data_version
> 12)
2295 gen
= decode_uleb128 (p
, &p
);
2297 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
);
2299 fprintf (outfile
, "incorrect gc gen: %d\n", gen
);
2302 if (ev
== MONO_GC_EVENT_START
) {
2303 thread
->gc_start_times
[gen
] = time_base
;
2304 gc_info
[gen
].count
++;
2305 } else if (ev
== MONO_GC_EVENT_END
) {
2306 tdiff
= time_base
- thread
->gc_start_times
[gen
];
2307 gc_info
[gen
].total_time
+= tdiff
;
2308 if (tdiff
> gc_info
[gen
].max_time
)
2309 gc_info
[gen
].max_time
= tdiff
;
2311 } else if (subtype
== TYPE_GC_MOVE
) {
2312 int j
, num
= decode_uleb128 (p
, &p
);
2313 gc_object_moves
+= num
/ 2;
2314 for (j
= 0; j
< num
; j
+= 2) {
2315 intptr_t obj1diff
= decode_sleb128 (p
, &p
);
2316 intptr_t obj2diff
= decode_sleb128 (p
, &p
);
2317 if (num_tracked_objects
)
2318 track_move (OBJ_ADDR (obj1diff
), OBJ_ADDR (obj2diff
));
2320 fprintf (outfile
, "moved obj %p to %p\n", (void*)OBJ_ADDR (obj1diff
), (void*)OBJ_ADDR (obj2diff
));
2323 } else if (subtype
== TYPE_GC_HANDLE_CREATED
|| subtype
== TYPE_GC_HANDLE_CREATED_BT
) {
2324 int has_bt
= subtype
== TYPE_GC_HANDLE_CREATED_BT
;
2326 MethodDesc
*sframes
[8];
2327 MethodDesc
**frames
= sframes
;
2328 int htype
= decode_uleb128 (p
, &p
);
2329 uint32_t handle
= decode_uleb128 (p
, &p
);
2330 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2333 frames
= decode_bt (ctx
, sframes
, &num_bt
, p
, &p
, ptr_base
, &method_base
);
2335 fprintf (outfile
, "Cannot load backtrace\n");
2341 if ((thread_filter
&& thread_filter
== thread
->thread_id
) || (time_base
>= time_from
&& time_base
< time_to
)) {
2342 handle_info
[htype
].created
++;
2343 handle_info
[htype
].live
++;
2344 if (handle_info
[htype
].live
> handle_info
[htype
].max_live
)
2345 handle_info
[htype
].max_live
= handle_info
[htype
].live
;
2348 bt
= add_trace_methods (frames
, num_bt
, &handle_info
[htype
].traces
, 1);
2350 bt
= add_trace_thread (thread
, &handle_info
[htype
].traces
, 1);
2351 if (num_tracked_objects
)
2352 track_handle (OBJ_ADDR (objdiff
), htype
, handle
, bt
, time_base
);
2355 fprintf (outfile
, "handle (%s) %u created for object %p\n", get_handle_name (htype
), handle
, (void*)OBJ_ADDR (objdiff
));
2356 if (frames
!= sframes
)
2358 } else if (subtype
== TYPE_GC_HANDLE_DESTROYED
|| subtype
== TYPE_GC_HANDLE_DESTROYED_BT
) {
2359 int has_bt
= subtype
== TYPE_GC_HANDLE_DESTROYED_BT
;
2361 MethodDesc
*sframes
[8];
2362 MethodDesc
**frames
= sframes
;
2363 int htype
= decode_uleb128 (p
, &p
);
2364 uint32_t handle
= decode_uleb128 (p
, &p
);
2367 frames
= decode_bt (ctx
, sframes
, &num_bt
, p
, &p
, ptr_base
, &method_base
);
2369 fprintf (outfile
, "Cannot load backtrace\n");
2375 if ((thread_filter
&& thread_filter
== thread
->thread_id
) || (time_base
>= time_from
&& time_base
< time_to
)) {
2376 handle_info
[htype
].destroyed
++;
2377 handle_info
[htype
].live
--;
2380 bt
= add_trace_methods (frames
, num_bt
, &handle_info
[htype
].destroy_traces
, 1);
2382 bt
= add_trace_thread (thread
, &handle_info
[htype
].destroy_traces
, 1);
2383 /* TODO: track_handle_free () - would need to record and keep track of the associated object address... */
2386 fprintf (outfile
, "handle (%s) %u destroyed\n", get_handle_name (htype
), handle
);
2387 if (frames
!= sframes
)
2389 } else if (subtype
== TYPE_GC_FINALIZE_START
) {
2390 // TODO: Generate a finalizer report based on these events.
2392 fprintf (outfile
, "gc finalizer queue being processed at %llu\n", (unsigned long long) time_base
);
2393 } else if (subtype
== TYPE_GC_FINALIZE_END
) {
2395 fprintf (outfile
, "gc finalizer queue finished processing at %llu\n", (unsigned long long) time_base
);
2396 } else if (subtype
== TYPE_GC_FINALIZE_OBJECT_START
) {
2397 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2399 fprintf (outfile
, "gc finalizing object %p at %llu\n", (void *) OBJ_ADDR (objdiff
), (unsigned long long) time_base
);
2400 } else if (subtype
== TYPE_GC_FINALIZE_OBJECT_END
) {
2401 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2403 fprintf (outfile
, "gc finalized object %p at %llu\n", (void *) OBJ_ADDR (objdiff
), (unsigned long long) time_base
);
2407 case TYPE_METADATA
: {
2408 int subtype
= *p
& 0xf0;
2409 const char *load_str
= subtype
== TYPE_END_LOAD
? "loaded" : "unloaded";
2410 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2412 intptr_t ptrdiff
= decode_sleb128 (p
, &p
);
2413 LOG_TIME (time_base
, tdiff
);
2415 if (mtype
== TYPE_CLASS
) {
2416 intptr_t imptrdiff
= decode_sleb128 (p
, &p
);
2417 if (ctx
->data_version
< 13)
2418 decode_uleb128 (p
, &p
); /* flags */
2420 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
);
2421 add_class (ptr_base
+ ptrdiff
, (char*)p
);
2424 } else if (mtype
== TYPE_IMAGE
) {
2425 if (ctx
->data_version
< 13)
2426 decode_uleb128 (p
, &p
); /* flags */
2428 fprintf (outfile
, "%s image %p (%s) at %llu\n", load_str
, (void*)(ptr_base
+ ptrdiff
), p
, (unsigned long long) time_base
);
2429 if (subtype
== TYPE_END_LOAD
)
2430 add_image (ptr_base
+ ptrdiff
, (char*)p
);
2433 if (ctx
->data_version
>= 16) {
2434 while (*p
) p
++; // mvid
2437 } else if (mtype
== TYPE_ASSEMBLY
) {
2438 if (ctx
->data_version
> 13)
2439 decode_sleb128 (p
, &p
); // image
2440 if (ctx
->data_version
< 13)
2441 decode_uleb128 (p
, &p
); /* flags */
2443 fprintf (outfile
, "%s assembly %p (%s) at %llu\n", load_str
, (void*)(ptr_base
+ ptrdiff
), p
, (unsigned long long) time_base
);
2444 if (subtype
== TYPE_END_LOAD
)
2445 add_assembly (ptr_base
+ ptrdiff
, (char*)p
);
2448 } else if (mtype
== TYPE_DOMAIN
) {
2449 if (ctx
->data_version
< 13)
2450 decode_uleb128 (p
, &p
); /* flags */
2451 DomainContext
*nd
= get_domain (ctx
, ptr_base
+ ptrdiff
);
2452 /* no subtype means it's a name event, rather than start/stop */
2454 nd
->friendly_name
= pstrdup ((char *) p
);
2457 fprintf (outfile
, "domain %p named at %llu: %s\n", (void *) (ptr_base
+ ptrdiff
), (unsigned long long) time_base
, p
);
2459 fprintf (outfile
, "%s thread %p at %llu\n", load_str
, (void *) (ptr_base
+ ptrdiff
), (unsigned long long) time_base
);
2465 } else if (mtype
== TYPE_CONTEXT
) {
2466 if (ctx
->data_version
< 13)
2467 decode_uleb128 (p
, &p
); /* flags */
2468 intptr_t domaindiff
= decode_sleb128 (p
, &p
);
2470 fprintf (outfile
, "%s context %p (%p) at %llu\n", load_str
, (void*)(ptr_base
+ ptrdiff
), (void *) (ptr_base
+ domaindiff
), (unsigned long long) time_base
);
2471 if (subtype
== TYPE_END_LOAD
)
2472 get_remctx (ctx
, ptr_base
+ ptrdiff
)->domain_id
= ptr_base
+ domaindiff
;
2473 } else if (mtype
== TYPE_THREAD
) {
2474 if (ctx
->data_version
< 13)
2475 decode_uleb128 (p
, &p
); /* flags */
2476 ThreadContext
*nt
= get_thread (ctx
, ptr_base
+ ptrdiff
);
2477 /* no subtype means it's a name event, rather than start/stop */
2479 nt
->name
= pstrdup ((char*)p
);
2482 fprintf (outfile
, "thread %p named at %llu: %s\n", (void*)(ptr_base
+ ptrdiff
), (unsigned long long) time_base
, p
);
2484 fprintf (outfile
, "%s thread %p at %llu\n", load_str
, (void *) (ptr_base
+ ptrdiff
), (unsigned long long) time_base
);
2490 } else if (mtype
== TYPE_VTABLE
) {
2491 intptr_t domaindiff
= decode_sleb128 (p
, &p
);
2492 intptr_t classdiff
= decode_sleb128 (p
, &p
);
2494 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
);
2495 add_vtable (ptr_base
+ ptrdiff
, ptr_base
+ classdiff
);
2500 int has_bt
= *p
& TYPE_ALLOC_BT
;
2501 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2502 intptr_t ptrdiff
= decode_sleb128 (p
, &p
);
2503 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2506 MethodDesc
* sframes
[8];
2507 MethodDesc
** frames
= sframes
;
2509 if (ctx
->data_version
> 14) {
2510 VTableDesc
*vt
= lookup_vtable (ptr_base
+ ptrdiff
);
2513 cd
= lookup_class (ptr_base
+ ptrdiff
);
2514 len
= decode_uleb128 (p
, &p
);
2515 LOG_TIME (time_base
, tdiff
);
2518 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
);
2521 frames
= decode_bt (ctx
, sframes
, &num_bt
, p
, &p
, ptr_base
, &method_base
);
2523 fprintf (outfile
, "Cannot load backtrace\n");
2527 if ((thread_filter
&& thread_filter
== thread
->thread_id
) || (time_base
>= time_from
&& time_base
< time_to
)) {
2530 cd
->alloc_size
+= len
;
2532 bt
= add_trace_methods (frames
, num_bt
, &cd
->traces
, len
);
2534 bt
= add_trace_thread (thread
, &cd
->traces
, len
);
2535 if (find_size
&& len
>= find_size
) {
2536 if (!find_name
|| strstr (cd
->name
, find_name
))
2537 found_object (OBJ_ADDR (objdiff
));
2538 } else if (!find_size
&& find_name
&& strstr (cd
->name
, find_name
)) {
2539 found_object (OBJ_ADDR (objdiff
));
2541 if (num_tracked_objects
)
2542 tracked_creation (OBJ_ADDR (objdiff
), cd
, len
, bt
, time_base
);
2544 if (frames
!= sframes
)
2549 int subtype
= *p
& 0xf0;
2550 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2551 int64_t ptrdiff
= decode_sleb128 (p
, &p
);
2552 LOG_TIME (time_base
, tdiff
);
2554 method_base
+= ptrdiff
;
2555 if (subtype
== TYPE_JIT
) {
2556 intptr_t codediff
= decode_sleb128 (p
, &p
);
2557 int codelen
= decode_uleb128 (p
, &p
);
2558 MethodDesc
*jitted_method
;
2560 fprintf (outfile
, "jitted method %p (%s), size: %d, code: %p\n", (void*)(method_base
), p
, codelen
, (void*)(ptr_base
+ codediff
));
2561 jitted_method
= add_method (method_base
, (char*)p
, ptr_base
+ codediff
, codelen
);
2562 if (!(time_base
>= time_from
&& time_base
< time_to
))
2563 jitted_method
->ignore_jit
= 1;
2568 if ((thread_filter
&& thread_filter
!= thread
->thread_id
))
2570 if (!(time_base
>= time_from
&& time_base
< time_to
))
2572 method
= lookup_method (method_base
);
2573 if (subtype
== TYPE_ENTER
) {
2574 add_trace_thread (thread
, &method
->traces
, 1);
2575 push_method (thread
, method
, time_base
);
2577 pop_method (thread
, method
, time_base
);
2580 fprintf (outfile
, "%s method %s\n", subtype
== TYPE_ENTER
? "enter": subtype
== TYPE_EXC_LEAVE
? "exleave": "leave", method
->name
);
2585 int subtype
= *p
& 0xf0;
2586 if (subtype
== TYPE_HEAP_OBJECT
) {
2587 HeapObjectDesc
*ho
= NULL
;
2590 if (ctx
->data_version
> 12) {
2591 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2592 LOG_TIME (time_base
, tdiff
);
2594 objdiff
= decode_sleb128 (p
, &p
);
2596 objdiff
= decode_sleb128 (p
+ 1, &p
);
2597 intptr_t ptrdiff
= decode_sleb128 (p
, &p
);
2598 uint64_t size
= decode_uleb128 (p
, &p
);
2599 if (ctx
->data_version
>= 16)
2601 uintptr_t num
= decode_uleb128 (p
, &p
);
2602 uintptr_t ref_offset
= 0;
2603 uintptr_t last_obj_offset
= 0;
2605 if (ctx
->data_version
> 14) {
2606 VTableDesc
*vt
= lookup_vtable (ptr_base
+ ptrdiff
);
2609 cd
= lookup_class (ptr_base
+ ptrdiff
);
2611 HeapClassDesc
*hcd
= add_heap_shot_class (thread
->current_heap_shot
, cd
, size
);
2612 if (collect_traces
) {
2613 ho
= alloc_heap_obj (OBJ_ADDR (objdiff
), hcd
, num
);
2614 add_heap_shot_obj (thread
->current_heap_shot
, ho
);
2619 ho
= heap_shot_obj_add_refs (thread
->current_heap_shot
, OBJ_ADDR (objdiff
), num
, &ref_offset
);
2621 for (i
= 0; i
< num
; ++i
) {
2622 /* FIXME: use object distance to measure how good
2623 * the GC is at keeping related objects close
2625 uintptr_t offset
= ctx
->data_version
> 1? last_obj_offset
+ decode_uleb128 (p
, &p
): -1;
2626 intptr_t obj1diff
= decode_sleb128 (p
, &p
);
2627 last_obj_offset
= offset
;
2629 ho
->refs
[ref_offset
+ i
] = OBJ_ADDR (obj1diff
);
2630 if (num_tracked_objects
)
2631 track_obj_reference (OBJ_ADDR (obj1diff
), OBJ_ADDR (objdiff
), cd
);
2634 fprintf (outfile
, "traced object %p, size %llu (%s), refs: %zd\n", (void*)OBJ_ADDR (objdiff
), (unsigned long long) size
, cd
->name
, num
);
2635 } else if (subtype
== TYPE_HEAP_ROOT
) {
2637 if (ctx
->data_version
> 14) {
2639 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2640 LOG_TIME (time_base
, tdiff
);
2642 num
= decode_uleb128 (p
, &p
);
2643 for (i
= 0; i
< num
; ++i
) {
2644 intptr_t ptrdiff
= decode_sleb128 (p
, &p
);
2645 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2648 fprintf (outfile
, "root object %p at address %p\n", (void*)OBJ_ADDR (objdiff
), (void *) (ptr_base
+ ptrdiff
));
2650 thread_add_root (thread
, OBJ_ADDR (objdiff
), MONO_PROFILER_GC_ROOT_MISC
, 0);
2653 if (ctx
->data_version
> 12) {
2654 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2655 LOG_TIME (time_base
, tdiff
);
2657 num
= decode_uleb128 (p
, &p
);
2659 num
= decode_uleb128 (p
+ 1, &p
);
2660 uintptr_t gc_num G_GNUC_UNUSED
= decode_uleb128 (p
, &p
);
2662 for (i
= 0; i
< num
; ++i
) {
2663 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2665 if (ctx
->data_version
== 13)
2668 root_type
= decode_uleb128 (p
, &p
);
2669 /* we just discard the extra info for now */
2670 uintptr_t extra_info
= decode_uleb128 (p
, &p
);
2672 fprintf (outfile
, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff
), get_root_name (root_type
));
2674 thread_add_root (thread
, OBJ_ADDR (objdiff
), root_type
, extra_info
);
2677 } else if (subtype
== TYPE_HEAP_ROOT_REGISTER
) {
2678 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2679 LOG_TIME (time_base
, tdiff
);
2682 int64_t ptrdiff
= decode_sleb128 (p
, &p
);
2683 uint64_t size
= decode_uleb128 (p
, &p
);
2685 int64_t keydiff
= decode_sleb128 (p
, &p
);
2686 char *desc
= (char*) p
;
2690 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
);
2691 } else if (subtype
== TYPE_HEAP_ROOT_UNREGISTER
) {
2692 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2693 LOG_TIME (time_base
, tdiff
);
2695 int64_t ptrdiff
= decode_sleb128 (p
, &p
);
2698 fprintf (outfile
, "root unregister address %p\n", (void *) (ptr_base
+ ptrdiff
));
2699 } else if (subtype
== TYPE_HEAP_END
) {
2700 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2701 LOG_TIME (time_base
, tdiff
);
2704 fprintf (outfile
, "heap shot end\n");
2705 if (collect_traces
) {
2706 HeapShot
*hs
= thread
->current_heap_shot
;
2707 if (hs
&& thread
->num_roots
) {
2708 /* transfer the root ownershipt to the heapshot */
2709 hs
->num_roots
= thread
->num_roots
;
2710 hs
->roots
= thread
->roots
;
2711 hs
->roots_extra
= thread
->roots_extra
;
2712 hs
->roots_types
= thread
->roots_types
;
2714 g_free (thread
->roots
);
2715 g_free (thread
->roots_extra
);
2716 g_free (thread
->roots_types
);
2718 thread
->num_roots
= 0;
2719 thread
->size_roots
= 0;
2720 thread
->roots
= NULL
;
2721 thread
->roots_extra
= NULL
;
2722 thread
->roots_types
= NULL
;
2723 heap_shot_resolve_reverse_refs (hs
);
2724 heap_shot_mark_objects (hs
);
2725 heap_shot_free_objects (hs
);
2727 thread
->current_heap_shot
= NULL
;
2728 } else if (subtype
== TYPE_HEAP_START
) {
2729 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2730 LOG_TIME (time_base
, tdiff
);
2733 fprintf (outfile
, "heap shot start\n");
2734 thread
->current_heap_shot
= new_heap_shot (time_base
);
2738 case TYPE_MONITOR
: {
2739 int has_bt
= *p
& TYPE_MONITOR_BT
;
2741 if (ctx
->data_version
< 13)
2742 event
= (*p
>> 4) & 0x3;
2743 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2744 if (ctx
->data_version
> 13)
2746 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2747 MethodDesc
* sframes
[8];
2748 MethodDesc
** frames
= sframes
;
2751 LOG_TIME (time_base
, tdiff
);
2753 record
= (!thread_filter
|| thread_filter
== thread
->thread_id
);
2754 if (!(time_base
>= time_from
&& time_base
< time_to
))
2756 MonitorDesc
*mdesc
= lookup_monitor (OBJ_ADDR (objdiff
));
2757 if (event
== MONO_PROFILER_MONITOR_CONTENTION
) {
2759 monitor_contention
++;
2760 mdesc
->contentions
++;
2761 thread
->monitor
= mdesc
;
2762 thread
->contention_start
= time_base
;
2764 } else if (event
== MONO_PROFILER_MONITOR_FAIL
) {
2767 if (thread
->monitor
&& thread
->contention_start
) {
2768 uint64_t wait_time
= time_base
- thread
->contention_start
;
2769 if (wait_time
> thread
->monitor
->max_wait_time
)
2770 thread
->monitor
->max_wait_time
= wait_time
;
2771 thread
->monitor
->wait_time
+= wait_time
;
2772 thread
->monitor
= NULL
;
2773 thread
->contention_start
= 0;
2776 } else if (event
== MONO_PROFILER_MONITOR_DONE
) {
2779 if (thread
->monitor
&& thread
->contention_start
) {
2780 uint64_t wait_time
= time_base
- thread
->contention_start
;
2781 if (wait_time
> thread
->monitor
->max_wait_time
)
2782 thread
->monitor
->max_wait_time
= wait_time
;
2783 thread
->monitor
->wait_time
+= wait_time
;
2784 thread
->monitor
= NULL
;
2785 thread
->contention_start
= 0;
2791 frames
= decode_bt (ctx
, sframes
, &num_bt
, p
, &p
, ptr_base
, &method_base
);
2793 fprintf (outfile
, "Cannot load backtrace\n");
2796 if (record
&& event
== MONO_PROFILER_MONITOR_CONTENTION
)
2797 add_trace_methods (frames
, num_bt
, &mdesc
->traces
, 1);
2800 add_trace_thread (thread
, &mdesc
->traces
, 1);
2803 fprintf (outfile
, "monitor %s for object %p\n", monitor_ev_name (event
), (void*)OBJ_ADDR (objdiff
));
2804 if (frames
!= sframes
)
2808 case TYPE_EXCEPTION
: {
2809 int subtype
= *p
& 0x70;
2810 int has_bt
= *p
& TYPE_THROW_BT
;
2811 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2812 MethodDesc
* sframes
[8];
2813 MethodDesc
** frames
= sframes
;
2815 LOG_TIME (time_base
, tdiff
);
2817 record
= (!thread_filter
|| thread_filter
== thread
->thread_id
);
2818 if (!(time_base
>= time_from
&& time_base
< time_to
))
2820 if (subtype
== TYPE_CLAUSE
) {
2822 if (ctx
->data_version
> 12)
2825 clause_type
= decode_uleb128 (p
, &p
);
2826 int clause_num
= decode_uleb128 (p
, &p
);
2827 int64_t ptrdiff
= decode_sleb128 (p
, &p
);
2828 method_base
+= ptrdiff
;
2829 if (ctx
->data_version
> 13)
2830 decode_uleb128 (p
, &p
); // exception object
2832 clause_summary
[clause_type
]++;
2834 fprintf (outfile
, "clause %s (%d) in method %s\n", clause_name (clause_type
), clause_num
, lookup_method (method_base
)->name
);
2836 intptr_t objdiff
= decode_sleb128 (p
, &p
);
2841 frames
= decode_bt (ctx
, sframes
, &has_bt
, p
, &p
, ptr_base
, &method_base
);
2843 fprintf (outfile
, "Cannot load backtrace\n");
2847 add_trace_methods (frames
, has_bt
, &exc_traces
, 1);
2850 add_trace_thread (thread
, &exc_traces
, 1);
2852 if (frames
!= sframes
)
2855 fprintf (outfile
, "throw %p\n", (void*)OBJ_ADDR (objdiff
));
2859 case TYPE_RUNTIME
: {
2860 int subtype
= *p
& 0xf0;
2861 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2862 LOG_TIME (time_base
, tdiff
);
2864 if (subtype
== TYPE_JITHELPER
) {
2866 if (ctx
->data_version
> 12)
2869 type
= decode_uleb128 (p
, &p
);
2870 if (ctx
->data_version
< 14)
2872 intptr_t codediff
= decode_sleb128 (p
, &p
);
2873 int codelen
= decode_uleb128 (p
, &p
);
2875 if (type
== MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
) {
2876 name
= (const char *)p
;
2880 name
= code_buffer_desc (type
);
2883 jit_helpers_code_size
+= codelen
;
2885 fprintf (outfile
, "jit helper %s, size: %d, code: %p\n", name
, codelen
, (void*)(ptr_base
+ codediff
));
2890 int subtype
= *p
& 0xf0;
2891 if (subtype
== TYPE_SAMPLE_HIT
) {
2895 if (ctx
->data_version
> 12) {
2896 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2897 LOG_TIME (time_base
, tdiff
);
2899 if (ctx
->data_version
< 14)
2902 sample_type
= SAMPLE_CYCLES
;
2905 sample_type
= decode_uleb128 (p
+ 1, &p
);
2906 tstamp
= decode_uleb128 (p
, &p
);
2908 void *tid
= (void *) thread_id
;
2909 if (ctx
->data_version
> 10)
2910 tid
= (void *) (ptr_base
+ decode_sleb128 (p
, &p
));
2911 int count
= decode_uleb128 (p
, &p
);
2912 for (i
= 0; i
< count
; ++i
) {
2913 uintptr_t ip
= ptr_base
+ decode_sleb128 (p
, &p
);
2914 if ((tstamp
>= time_from
&& tstamp
< time_to
))
2915 add_stat_sample (sample_type
, ip
);
2917 fprintf (outfile
, "sample hit, type: %d at %p for thread %p\n", sample_type
, (void*)ip
, tid
);
2919 if (ctx
->data_version
> 5) {
2920 count
= decode_uleb128 (p
, &p
);
2921 for (i
= 0; i
< count
; ++i
) {
2923 int64_t ptrdiff
= decode_sleb128 (p
, &p
);
2924 method_base
+= ptrdiff
;
2925 method
= lookup_method (method_base
);
2927 fprintf (outfile
, "sample hit bt %d: %s\n", i
, method
->name
);
2928 if (ctx
->data_version
< 13) {
2929 decode_sleb128 (p
, &p
); /* il offset */
2930 decode_sleb128 (p
, &p
); /* native offset */
2934 } else if (subtype
== TYPE_SAMPLE_USYM
) {
2935 /* un unmanaged symbol description */
2937 if (ctx
->data_version
> 12) {
2938 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2939 LOG_TIME (time_base
, tdiff
);
2941 addr
= ptr_base
+ decode_sleb128 (p
, &p
);
2943 addr
= ptr_base
+ decode_sleb128 (p
+ 1, &p
);
2944 uintptr_t size
= decode_uleb128 (p
, &p
);
2946 name
= pstrdup ((char*)p
);
2947 add_unmanaged_symbol (addr
, name
, size
);
2949 fprintf (outfile
, "unmanaged symbol %s at %p\n", name
, (void*)addr
);
2952 } else if (subtype
== TYPE_SAMPLE_UBIN
) {
2953 /* un unmanaged binary loaded in memory */
2954 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2955 uintptr_t addr
= decode_sleb128 (p
, &p
);
2956 if (ctx
->data_version
> 13)
2958 uint64_t offset G_GNUC_UNUSED
= decode_uleb128 (p
, &p
);
2959 uintptr_t size
= decode_uleb128 (p
, &p
);
2961 LOG_TIME (time_base
, tdiff
);
2963 name
= pstrdup ((char*)p
);
2964 add_unmanaged_binary (addr
, name
, size
);
2966 fprintf (outfile
, "unmanaged binary %s at %p\n", name
, (void*)addr
);
2969 } else if (subtype
== TYPE_SAMPLE_COUNTERS_DESC
) {
2971 if (ctx
->data_version
> 12) {
2972 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
2973 LOG_TIME (time_base
, tdiff
);
2975 len
= decode_uleb128 (p
, &p
);
2977 len
= decode_uleb128 (p
+ 1, &p
);
2978 for (i
= 0; i
< len
; i
++) {
2979 uint64_t type
, unit
, variance
, index
;
2980 uint64_t section
= decode_uleb128 (p
, &p
);
2981 char *section_str
, *name
;
2982 if (section
!= MONO_COUNTER_PERFCOUNTERS
) {
2983 section_str
= (char*) section_name (section
);
2985 section_str
= pstrdup ((char*)p
);
2988 name
= pstrdup ((char*)p
);
2990 if (ctx
->data_version
> 12 && ctx
->data_version
< 15) {
2995 type
= decode_uleb128 (p
, &p
);
2996 unit
= decode_uleb128 (p
, &p
);
2997 variance
= decode_uleb128 (p
, &p
);
2999 index
= decode_uleb128 (p
, &p
);
3000 add_counter (section_str
, name
, (int)type
, (int)unit
, (int)variance
, (int)index
);
3002 } else if (subtype
== TYPE_SAMPLE_COUNTERS
) {
3004 CounterValue
*value
, *previous
= NULL
;
3006 uint64_t timestamp
; // milliseconds since startup
3007 if (ctx
->data_version
> 12) {
3008 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
3009 LOG_TIME (time_base
, tdiff
);
3011 timestamp
= (time_base
- startup_time
) / 1000 / 1000;
3013 timestamp
= decode_uleb128 (p
+ 1, &p
);
3014 uint64_t time_between
= timestamp
/ 1000 * 1000 * 1000 * 1000 + startup_time
;
3016 uint64_t type
, index
= decode_uleb128 (p
, &p
);
3020 for (list
= counters
; list
; list
= list
->next
) {
3021 if (list
->counter
->index
== (int)index
) {
3022 previous
= list
->counter
->values_last
;
3027 if (ctx
->data_version
> 12 && ctx
->data_version
< 15)
3030 type
= decode_uleb128 (p
, &p
);
3032 value
= (CounterValue
*) g_calloc (1, sizeof (CounterValue
));
3033 value
->timestamp
= timestamp
;
3036 case MONO_COUNTER_INT
:
3037 #if SIZEOF_VOID_P == 4
3038 case MONO_COUNTER_WORD
:
3040 value
->buffer
= (unsigned char *)g_malloc (sizeof (int32_t));
3041 *(int32_t*)value
->buffer
= (int32_t)decode_sleb128 (p
, &p
) + (previous
? (*(int32_t*)previous
->buffer
) : 0);
3043 case MONO_COUNTER_UINT
:
3044 value
->buffer
= (unsigned char *) g_malloc (sizeof (uint32_t));
3045 *(uint32_t*)value
->buffer
= (uint32_t)decode_uleb128 (p
, &p
) + (previous
? (*(uint32_t*)previous
->buffer
) : 0);
3047 case MONO_COUNTER_LONG
:
3048 #if SIZEOF_VOID_P == 8
3049 case MONO_COUNTER_WORD
:
3051 case MONO_COUNTER_TIME_INTERVAL
:
3052 value
->buffer
= (unsigned char *) g_malloc (sizeof (int64_t));
3053 *(int64_t*)value
->buffer
= (int64_t)decode_sleb128 (p
, &p
) + (previous
? (*(int64_t*)previous
->buffer
) : 0);
3055 case MONO_COUNTER_ULONG
:
3056 value
->buffer
= (unsigned char *) g_malloc (sizeof (uint64_t));
3057 *(uint64_t*)value
->buffer
= (uint64_t)decode_uleb128 (p
, &p
) + (previous
? (*(uint64_t*)previous
->buffer
) : 0);
3059 case MONO_COUNTER_DOUBLE
:
3060 value
->buffer
= (unsigned char *) g_malloc (sizeof (double));
3061 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
3062 for (i
= 0; i
< sizeof (double); i
++)
3064 for (i
= sizeof (double) - 1; i
>= 0; i
--)
3066 value
->buffer
[i
] = *p
++;
3068 case MONO_COUNTER_STRING
:
3070 value
->buffer
= NULL
;
3072 value
->buffer
= (unsigned char*) pstrdup ((char*)p
);
3077 if (time_between
>= time_from
&& time_between
<= time_to
)
3078 add_counter_value (index
, value
);
3085 case TYPE_COVERAGE
:{
3086 int subtype
= *p
& 0xf0;
3088 case TYPE_COVERAGE_METHOD
: {
3091 if (ctx
->data_version
> 12) {
3092 uint64_t tdiff
= decode_uleb128 (p
, &p
);
3093 LOG_TIME (time_base
, tdiff
);
3108 decode_uleb128 (p
, &p
);
3109 decode_uleb128 (p
, &p
);
3110 decode_uleb128 (p
, &p
);
3114 case TYPE_COVERAGE_STATEMENT
: {
3117 if (ctx
->data_version
> 12) {
3118 uint64_t tdiff
= decode_uleb128 (p
, &p
);
3119 LOG_TIME (time_base
, tdiff
);
3123 decode_uleb128 (p
, &p
);
3124 decode_uleb128 (p
, &p
);
3125 decode_uleb128 (p
, &p
);
3126 decode_uleb128 (p
, &p
);
3127 decode_uleb128 (p
, &p
);
3131 case TYPE_COVERAGE_ASSEMBLY
: {
3134 if (ctx
->data_version
> 12) {
3135 uint64_t tdiff
= decode_uleb128 (p
, &p
);
3136 LOG_TIME (time_base
, tdiff
);
3147 decode_uleb128 (p
, &p
);
3148 decode_uleb128 (p
, &p
);
3149 decode_uleb128 (p
, &p
);
3153 case TYPE_COVERAGE_CLASS
: {
3156 if (ctx
->data_version
> 12) {
3157 uint64_t tdiff
= decode_uleb128 (p
, &p
);
3158 LOG_TIME (time_base
, tdiff
);
3167 decode_uleb128 (p
, &p
);
3168 decode_uleb128 (p
, &p
);
3169 decode_uleb128 (p
, &p
);
3180 int subtype
= *p
& 0xf0;
3181 uint64_t tdiff
= decode_uleb128 (p
+ 1, &p
);
3182 LOG_TIME (time_base
, tdiff
);
3184 if (subtype
== TYPE_SYNC_POINT
) {
3187 fprintf (outfile
, "sync point %i (%s)\n", type
, sync_point_name (type
));
3188 } else if (subtype
== TYPE_AOT_ID
) {
3190 fprintf (outfile
, "aot id %s\n", p
);
3191 while (*p
) p
++; // aot id
3197 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
);
3200 record_event_stats (event
, p
- start
);
3202 thread
->last_time
= time_base
;
3203 for (i
= 0; i
< thread
->stack_id
; ++i
)
3204 thread
->stack
[i
]->recurse_count
= 0;
3209 read_header_string (ProfContext
*ctx
, char **field
)
3211 if (!load_data (ctx
, 4))
3214 if (!load_data (ctx
, read_int32 (ctx
->buf
)))
3217 *field
= pstrdup ((const char *) ctx
->buf
);
3223 load_file (char *name
)
3226 ProfContext
*ctx
= (ProfContext
*) g_calloc (sizeof (ProfContext
), 1);
3227 if (strcmp (name
, "-") == 0)
3230 ctx
->file
= fopen (name
, "rb");
3232 printf ("Cannot open file: %s\n", name
);
3235 #if defined (HAVE_SYS_ZLIB)
3236 if (ctx
->file
!= stdin
)
3237 ctx
->gzfile
= gzdopen (fileno (ctx
->file
), "rb");
3239 if (!load_data (ctx
, 16))
3242 if (read_int32 (p
) != LOG_HEADER_ID
)
3245 ctx
->version_major
= *p
++;
3246 ctx
->version_minor
= *p
++;
3247 ctx
->data_version
= *p
++;
3248 if (ctx
->data_version
> LOG_DATA_VERSION
)
3250 /* reading 64 bit files on 32 bit systems not supported yet */
3251 if (*p
++ > sizeof (void*))
3253 ctx
->startup_time
= read_int64 (p
);
3255 // nanoseconds startup time
3256 if (ctx
->version_major
>= 3)
3257 if (!load_data (ctx
, 8))
3259 if (!load_data (ctx
, 14))
3262 ctx
->timer_overhead
= read_int32 (p
);
3264 if (read_int32 (p
)) /* flags must be 0 */
3267 ctx
->pid
= read_int32 (p
);
3269 ctx
->port
= read_int16 (p
);
3271 if (ctx
->version_major
>= 1) {
3272 if (!read_header_string (ctx
, &ctx
->args
))
3274 if (!read_header_string (ctx
, &ctx
->arch
))
3276 if (!read_header_string (ctx
, &ctx
->os
))
3279 if (!load_data (ctx
, 2)) /* old opsys field, was never used */
3289 static int alloc_sort_mode
= ALLOC_SORT_BYTES
;
3292 compare_class (const void *a
, const void *b
)
3294 ClassDesc
*const *A
= (ClassDesc
*const *)a
;
3295 ClassDesc
*const *B
= (ClassDesc
*const *)b
;
3296 uint64_t vala
, valb
;
3297 if (alloc_sort_mode
== ALLOC_SORT_BYTES
) {
3298 vala
= (*A
)->alloc_size
;
3299 valb
= (*B
)->alloc_size
;
3301 vala
= (*A
)->allocs
;
3302 valb
= (*B
)->allocs
;
3312 dump_header (ProfContext
*ctx
)
3314 time_t st
= ctx
->startup_time
/ 1000;
3315 char *t
= ctime (&st
);
3316 fprintf (outfile
, "\nMono log profiler data\n");
3317 fprintf (outfile
, "\tProfiler version: %d.%d\n", ctx
->version_major
, ctx
->version_minor
);
3318 fprintf (outfile
, "\tData version: %d\n", ctx
->data_version
);
3319 if (ctx
->version_major
>= 1) {
3320 fprintf (outfile
, "\tArguments: %s\n", ctx
->args
);
3321 fprintf (outfile
, "\tArchitecture: %s\n", ctx
->arch
);
3322 fprintf (outfile
, "\tOperating system: %s\n", ctx
->os
);
3324 fprintf (outfile
, "\tMean timer overhead: %d nanoseconds\n", ctx
->timer_overhead
);
3325 fprintf (outfile
, "\tProgram startup: %s", t
);
3327 fprintf (outfile
, "\tProgram ID: %d\n", ctx
->pid
);
3329 fprintf (outfile
, "\tServer listening on: %d\n", ctx
->port
);
3333 dump_traces (TraceDesc
*traces
, const char *desc
)
3340 sort_context_array (traces
);
3341 for (j
= 0; j
< traces
->count
; ++j
) {
3344 bt
= traces
->traces
[j
].bt
;
3347 fprintf (outfile
, "\t%llu %s from:\n", (unsigned long long) traces
->traces
[j
].count
, desc
);
3348 for (k
= 0; k
< bt
->count
; ++k
)
3349 fprintf (outfile
, "\t\t%s\n", bt
->methods
[k
]->name
);
3354 dump_threads (ProfContext
*ctx
)
3356 ThreadContext
*thread
;
3357 fprintf (outfile
, "\nThread summary\n");
3358 for (thread
= ctx
->threads
; thread
; thread
= thread
->next
) {
3359 if (thread
->thread_id
) {
3360 fprintf (outfile
, "\tThread: %p, name: \"%s\"\n", (void*)thread
->thread_id
, thread
->name
? thread
->name
: "");
3366 dump_domains (ProfContext
*ctx
)
3368 fprintf (outfile
, "\nDomain summary\n");
3370 for (DomainContext
*domain
= ctx
->domains
; domain
; domain
= domain
->next
)
3371 fprintf (outfile
, "\tDomain: %p, friendly name: \"%s\"\n", (void *) domain
->domain_id
, domain
->friendly_name
);
3375 dump_remctxs (ProfContext
*ctx
)
3377 fprintf (outfile
, "\nContext summary\n");
3379 for (RemCtxContext
*remctx
= ctx
->remctxs
; remctx
; remctx
= remctx
->next
)
3380 fprintf (outfile
, "\tContext: %p, domain: %p\n", (void *) remctx
->remctx_id
, (void *) remctx
->domain_id
);
3384 dump_exceptions (void)
3387 fprintf (outfile
, "\nException summary\n");
3388 fprintf (outfile
, "\tThrows: %llu\n", (unsigned long long) throw_count
);
3389 dump_traces (&exc_traces
, "throws");
3390 for (i
= 0; i
<= MONO_EXCEPTION_CLAUSE_FAULT
; ++i
) {
3391 if (!clause_summary
[i
])
3393 fprintf (outfile
, "\tExecuted %s clauses: %llu\n", clause_name (i
), (unsigned long long) clause_summary
[i
]);
3398 compare_monitor (const void *a
, const void *b
)
3400 MonitorDesc
*const *A
= (MonitorDesc
*const *)a
;
3401 MonitorDesc
*const *B
= (MonitorDesc
*const *)b
;
3402 if ((*B
)->wait_time
== (*A
)->wait_time
)
3404 if ((*B
)->wait_time
< (*A
)->wait_time
)
3410 dump_monitors (void)
3412 MonitorDesc
**monitors
;
3416 monitors
= (MonitorDesc
**) g_malloc (sizeof (void*) * num_monitors
);
3417 for (i
= 0, j
= 0; i
< SMALL_HASH_SIZE
; ++i
) {
3418 MonitorDesc
*mdesc
= monitor_hash
[i
];
3420 monitors
[j
++] = mdesc
;
3421 mdesc
= mdesc
->next
;
3424 mono_qsort (monitors
, num_monitors
, sizeof (void*), compare_monitor
);
3425 fprintf (outfile
, "\nMonitor lock summary\n");
3426 for (i
= 0; i
< num_monitors
; ++i
) {
3427 MonitorDesc
*mdesc
= monitors
[i
];
3428 fprintf (outfile
, "\tLock object %p: %d contentions\n", (void*)mdesc
->objid
, (int)mdesc
->contentions
);
3429 fprintf (outfile
, "\t\t%.6f secs total wait time, %.6f max, %.6f average\n",
3430 mdesc
->wait_time
/1000000000.0, mdesc
->max_wait_time
/1000000000.0, mdesc
->wait_time
/1000000000.0/mdesc
->contentions
);
3431 dump_traces (&mdesc
->traces
, "contentions");
3433 fprintf (outfile
, "\tLock contentions: %llu\n", (unsigned long long) monitor_contention
);
3434 fprintf (outfile
, "\tLock acquired: %llu\n", (unsigned long long) monitor_acquired
);
3435 fprintf (outfile
, "\tLock failures: %llu\n", (unsigned long long) monitor_failed
);
3442 fprintf (outfile
, "\nGC summary\n");
3443 fprintf (outfile
, "\tGC resizes: %d\n", gc_resizes
);
3444 fprintf (outfile
, "\tMax heap size: %llu\n", (unsigned long long) max_heap_size
);
3445 fprintf (outfile
, "\tObject moves: %llu\n", (unsigned long long) gc_object_moves
);
3446 for (i
= 0; i
< 3; ++i
) {
3447 if (!gc_info
[i
].count
)
3449 fprintf (outfile
, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
3450 i
, gc_info
[i
].count
,
3451 (unsigned long long) (gc_info
[i
].max_time
/ 1000),
3452 (unsigned long long) (gc_info
[i
].total_time
/ 1000),
3453 (unsigned long long) (gc_info
[i
].total_time
/ gc_info
[i
].count
/ 1000));
3455 for (i
= 0; i
< 3; ++i
) {
3456 if (!handle_info
[i
].max_live
)
3458 fprintf (outfile
, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
3459 get_handle_name (i
),
3460 (unsigned long long) (handle_info
[i
].created
),
3461 (unsigned long long) (handle_info
[i
].destroyed
),
3462 (unsigned long long) (handle_info
[i
].max_live
));
3463 dump_traces (&handle_info
[i
].traces
, "created");
3464 dump_traces (&handle_info
[i
].destroy_traces
, "destroyed");
3473 int compiled_methods
= 0;
3475 fprintf (outfile
, "\nJIT summary\n");
3476 for (i
= 0; i
< HASH_SIZE
; ++i
) {
3477 m
= method_hash
[i
];
3478 for (m
= method_hash
[i
]; m
; m
= m
->next
) {
3479 if (!m
->code
|| m
->ignore_jit
)
3482 code_size
+= m
->len
;
3485 fprintf (outfile
, "\tCompiled methods: %d\n", compiled_methods
);
3486 fprintf (outfile
, "\tGenerated code size: %d\n", code_size
);
3487 fprintf (outfile
, "\tJIT helpers: %d\n", num_jit_helpers
);
3488 fprintf (outfile
, "\tJIT helpers code size: %d\n", jit_helpers_code_size
);
3492 dump_allocations (void)
3495 intptr_t allocs
= 0;
3497 int header_done
= 0;
3498 ClassDesc
**classes
= (ClassDesc
**) g_malloc (num_classes
* sizeof (void*));
3501 for (i
= 0; i
< HASH_SIZE
; ++i
) {
3502 cd
= class_hash
[i
];
3508 mono_qsort (classes
, num_classes
, sizeof (void*), compare_class
);
3509 for (i
= 0; i
< num_classes
; ++i
) {
3513 allocs
+= cd
->allocs
;
3514 size
+= cd
->alloc_size
;
3515 if (!header_done
++) {
3516 fprintf (outfile
, "\nAllocation summary\n");
3517 fprintf (outfile
, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
3519 fprintf (outfile
, "%10llu %10zd %8llu %s\n",
3520 (unsigned long long) (cd
->alloc_size
),
3522 (unsigned long long) (cd
->alloc_size
/ cd
->allocs
),
3524 dump_traces (&cd
->traces
, "bytes");
3527 fprintf (outfile
, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size
, allocs
);
3536 static int method_sort_mode
= METHOD_SORT_TOTAL
;
3539 compare_method (const void *a
, const void *b
)
3541 MethodDesc
*const *A
= (MethodDesc
*const *)a
;
3542 MethodDesc
*const *B
= (MethodDesc
*const *)b
;
3543 uint64_t vala
, valb
;
3544 if (method_sort_mode
== METHOD_SORT_SELF
) {
3545 vala
= (*A
)->self_time
;
3546 valb
= (*B
)->self_time
;
3547 } else if (method_sort_mode
== METHOD_SORT_CALLS
) {
3551 vala
= (*A
)->total_time
;
3552 valb
= (*B
)->total_time
;
3562 dump_metadata (void)
3564 fprintf (outfile
, "\nMetadata summary\n");
3565 fprintf (outfile
, "\tLoaded images: %d\n", num_images
);
3569 for (i
= 0; i
< SMALL_HASH_SIZE
; ++i
) {
3570 image
= image_hash
[i
];
3572 fprintf (outfile
, "\t\t%s\n", image
->filename
);
3573 image
= image
->next
;
3577 fprintf (outfile
, "\tLoaded assemblies: %d\n", num_assemblies
);
3579 AssemblyDesc
*assembly
;
3581 for (i
= 0; i
< SMALL_HASH_SIZE
; ++i
) {
3582 assembly
= assembly_hash
[i
];
3584 fprintf (outfile
, "\t\t%s\n", assembly
->asmname
);
3585 assembly
= assembly
->next
;
3596 int header_done
= 0;
3597 MethodDesc
**methods
= (MethodDesc
**) g_malloc (num_methods
* sizeof (void*));
3600 for (i
= 0; i
< HASH_SIZE
; ++i
) {
3601 cd
= method_hash
[i
];
3603 cd
->total_time
= cd
->self_time
+ cd
->callee_time
;
3608 mono_qsort (methods
, num_methods
, sizeof (void*), compare_method
);
3609 for (i
= 0; i
< num_methods
; ++i
) {
3616 msecs
= cd
->total_time
/ 1000000;
3617 smsecs
= (cd
->total_time
- cd
->callee_time
) / 1000000;
3618 if (!msecs
&& !verbose
)
3620 if (!header_done
++) {
3621 fprintf (outfile
, "\nMethod call summary\n");
3622 fprintf (outfile
, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
3624 fprintf (outfile
, "%8llu %8llu %10llu %s\n",
3625 (unsigned long long) (msecs
),
3626 (unsigned long long) (smsecs
),
3627 (unsigned long long) (cd
->calls
),
3629 dump_traces (&cd
->traces
, "calls");
3632 fprintf (outfile
, "Total calls: %llu\n", (unsigned long long) calls
);
3636 compare_heap_class (const void *a
, const void *b
)
3638 HeapClassDesc
*const *A
= (HeapClassDesc
*const *)a
;
3639 HeapClassDesc
*const *B
= (HeapClassDesc
*const *)b
;
3640 uint64_t vala
, valb
;
3641 if (alloc_sort_mode
== ALLOC_SORT_BYTES
) {
3642 vala
= (*A
)->total_size
;
3643 valb
= (*B
)->total_size
;
3656 compare_rev_class (const void *a
, const void *b
)
3658 const HeapClassRevRef
*A
= (const HeapClassRevRef
*)a
;
3659 const HeapClassRevRef
*B
= (const HeapClassRevRef
*)b
;
3660 if (B
->count
== A
->count
)
3662 if (B
->count
< A
->count
)
3668 dump_rev_claases (HeapClassRevRef
*revs
, int count
)
3675 for (j
= 0; j
< count
; ++j
) {
3676 HeapClassDesc
*cd
= revs
[j
].klass
;
3677 fprintf (outfile
, "\t\t%llu references from: %s\n",
3678 (unsigned long long) (revs
[j
].count
),
3684 heap_shot_summary (HeapShot
*hs
, int hs_num
, HeapShot
*last_hs
)
3691 HeapClassDesc
**sorted
;
3692 sorted
= (HeapClassDesc
**) g_malloc (sizeof (void*) * hs
->class_count
);
3693 for (i
= 0; i
< hs
->hash_size
; ++i
) {
3694 cd
= hs
->class_hash
[i
];
3698 size
+= cd
->total_size
;
3699 sorted
[ccount
++] = cd
;
3701 hs
->sorted
= sorted
;
3702 mono_qsort (sorted
, ccount
, sizeof (void*), compare_heap_class
);
3703 fprintf (outfile
, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %zd\n",
3705 (hs
->timestamp
- startup_time
)/1000000000.0,
3706 (unsigned long long) (size
),
3707 (unsigned long long) (count
),
3708 ccount
, hs
->num_roots
);
3709 if (!verbose
&& ccount
> 30)
3711 fprintf (outfile
, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
3712 for (i
= 0; i
< ccount
; ++i
) {
3713 HeapClassRevRef
*rev_sorted
;
3715 HeapClassDesc
*ocd
= NULL
;
3718 ocd
= heap_class_lookup (last_hs
, cd
->klass
);
3719 fprintf (outfile
, "\t%10llu %10llu %8llu %s",
3720 (unsigned long long) (cd
->total_size
),
3721 (unsigned long long) (cd
->count
),
3722 (unsigned long long) (cd
->total_size
/ cd
->count
),
3725 int64_t bdiff
= cd
->total_size
- ocd
->total_size
;
3726 int64_t cdiff
= cd
->count
- ocd
->count
;
3727 fprintf (outfile
, " (bytes: %+lld, count: %+lld)\n", (long long) bdiff
, (long long) cdiff
);
3729 fprintf (outfile
, "\n");
3731 if (!collect_traces
)
3733 rev_sorted
= (HeapClassRevRef
*) g_malloc (cd
->rev_count
* sizeof (HeapClassRevRef
));
3735 for (j
= 0; j
< cd
->rev_hash_size
; ++j
) {
3736 if (cd
->rev_hash
[j
].klass
)
3737 rev_sorted
[k
++] = cd
->rev_hash
[j
];
3739 assert (cd
->rev_count
== k
);
3740 mono_qsort (rev_sorted
, cd
->rev_count
, sizeof (HeapClassRevRef
), compare_rev_class
);
3741 if (cd
->root_references
)
3742 fprintf (outfile
, "\t\t%zd root references (%zd pinning)\n", cd
->root_references
, cd
->pinned_references
);
3743 dump_rev_claases (rev_sorted
, cd
->rev_count
);
3744 g_free (rev_sorted
);
3750 compare_heap_shots (const void *a
, const void *b
)
3752 HeapShot
*const *A
= (HeapShot
*const *)a
;
3753 HeapShot
*const *B
= (HeapShot
*const *)b
;
3754 if ((*B
)->timestamp
== (*A
)->timestamp
)
3756 if ((*B
)->timestamp
> (*A
)->timestamp
)
3762 dump_heap_shots (void)
3764 HeapShot
**hs_sorted
;
3766 HeapShot
*last_hs
= NULL
;
3770 hs_sorted
= (HeapShot
**) g_malloc (num_heap_shots
* sizeof (void*));
3771 fprintf (outfile
, "\nHeap shot summary\n");
3773 for (hs
= heap_shots
; hs
; hs
= hs
->next
)
3774 hs_sorted
[i
++] = hs
;
3775 mono_qsort (hs_sorted
, num_heap_shots
, sizeof (void*), compare_heap_shots
);
3776 for (i
= 0; i
< num_heap_shots
; ++i
) {
3778 heap_shot_summary (hs
, i
, last_hs
);
3783 #define DUMP_EVENT_STAT(EVENT,SUBTYPE) dump_event (#EVENT, #SUBTYPE, EVENT, SUBTYPE);
3786 dump_event (const char *event_name
, const char *subtype_name
, int event
, int subtype
)
3788 int idx
= event
| subtype
;
3789 EventStat evt
= stats
[idx
];
3793 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
);
3799 fprintf (outfile
, "\nMlpd statistics\n");
3800 fprintf (outfile
, "\tBuffer count %d\toverhead %d (%d bytes per header)\n", buffer_count
, buffer_count
* BUFFER_HEADER_SIZE
, BUFFER_HEADER_SIZE
);
3801 fprintf (outfile
, "\nEvent details:\n");
3803 DUMP_EVENT_STAT (TYPE_ALLOC
, TYPE_ALLOC_NO_BT
);
3804 DUMP_EVENT_STAT (TYPE_ALLOC
, TYPE_ALLOC_BT
);
3806 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_EVENT
);
3807 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_RESIZE
);
3808 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_MOVE
);
3809 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_HANDLE_CREATED
);
3810 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_HANDLE_DESTROYED
);
3811 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_HANDLE_CREATED_BT
);
3812 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_HANDLE_DESTROYED_BT
);
3813 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_FINALIZE_START
);
3814 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_FINALIZE_END
);
3815 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_FINALIZE_OBJECT_START
);
3816 DUMP_EVENT_STAT (TYPE_GC
, TYPE_GC_FINALIZE_OBJECT_END
);
3818 DUMP_EVENT_STAT (TYPE_METADATA
, TYPE_END_LOAD
);
3819 DUMP_EVENT_STAT (TYPE_METADATA
, TYPE_END_UNLOAD
);
3821 DUMP_EVENT_STAT (TYPE_METHOD
, TYPE_LEAVE
);
3822 DUMP_EVENT_STAT (TYPE_METHOD
, TYPE_ENTER
);
3823 DUMP_EVENT_STAT (TYPE_METHOD
, TYPE_EXC_LEAVE
);
3824 DUMP_EVENT_STAT (TYPE_METHOD
, TYPE_JIT
);
3826 DUMP_EVENT_STAT (TYPE_EXCEPTION
, TYPE_THROW_NO_BT
);
3827 DUMP_EVENT_STAT (TYPE_EXCEPTION
, TYPE_THROW_BT
);
3828 DUMP_EVENT_STAT (TYPE_EXCEPTION
, TYPE_CLAUSE
);
3830 DUMP_EVENT_STAT (TYPE_MONITOR
, TYPE_MONITOR_NO_BT
);
3831 DUMP_EVENT_STAT (TYPE_MONITOR
, TYPE_MONITOR_BT
);
3833 DUMP_EVENT_STAT (TYPE_HEAP
, TYPE_HEAP_START
);
3834 DUMP_EVENT_STAT (TYPE_HEAP
, TYPE_HEAP_END
);
3835 DUMP_EVENT_STAT (TYPE_HEAP
, TYPE_HEAP_OBJECT
);
3836 DUMP_EVENT_STAT (TYPE_HEAP
, TYPE_HEAP_ROOT
);
3838 DUMP_EVENT_STAT (TYPE_SAMPLE
, TYPE_SAMPLE_HIT
);
3839 DUMP_EVENT_STAT (TYPE_SAMPLE
, TYPE_SAMPLE_USYM
);
3840 DUMP_EVENT_STAT (TYPE_SAMPLE
, TYPE_SAMPLE_UBIN
);
3841 DUMP_EVENT_STAT (TYPE_SAMPLE
, TYPE_SAMPLE_COUNTERS_DESC
);
3842 DUMP_EVENT_STAT (TYPE_SAMPLE
, TYPE_SAMPLE_COUNTERS
);
3844 DUMP_EVENT_STAT (TYPE_RUNTIME
, TYPE_JITHELPER
);
3846 DUMP_EVENT_STAT (TYPE_COVERAGE
, TYPE_COVERAGE_ASSEMBLY
);
3847 DUMP_EVENT_STAT (TYPE_COVERAGE
, TYPE_COVERAGE_METHOD
);
3848 DUMP_EVENT_STAT (TYPE_COVERAGE
, TYPE_COVERAGE_STATEMENT
);
3849 DUMP_EVENT_STAT (TYPE_COVERAGE
, TYPE_COVERAGE_CLASS
);
3851 DUMP_EVENT_STAT (TYPE_META
, TYPE_SYNC_POINT
);
3852 DUMP_EVENT_STAT (TYPE_META
, TYPE_AOT_ID
);
3858 flush_context (ProfContext
*ctx
)
3860 ThreadContext
*thread
;
3861 /* FIXME: sometimes there are leftovers: indagate */
3862 for (thread
= ctx
->threads
; thread
; thread
= thread
->next
) {
3863 while (thread
->stack_id
) {
3865 fprintf (outfile
, "thread %p has %d items on stack\n", (void*)thread
->thread_id
, thread
->stack_id
);
3866 pop_method (thread
, thread
->stack
[thread
->stack_id
- 1], thread
->last_time
);
3871 static const char *reports
= "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,domain,context,heapshot,counters";
3874 match_option (const char *p
, const char *opt
)
3876 int len
= strlen (opt
);
3877 if (strncmp (p
, opt
, len
) == 0) {
3886 print_reports (ProfContext
*ctx
, const char *reps
, int parse_only
)
3890 for (p
= reps
; *p
; p
= opt
) {
3891 if ((opt
= match_option (p
, "header")) != p
) {
3896 if ((opt
= match_option (p
, "thread")) != p
) {
3901 if ((opt
= match_option (p
, "domain")) != p
) {
3906 if ((opt
= match_option (p
, "context")) != p
) {
3911 if ((opt
= match_option (p
, "gc")) != p
) {
3916 if ((opt
= match_option (p
, "jit")) != p
) {
3921 if ((opt
= match_option (p
, "alloc")) != p
) {
3923 dump_allocations ();
3926 if ((opt
= match_option (p
, "call")) != p
) {
3931 if ((opt
= match_option (p
, "metadata")) != p
) {
3936 if ((opt
= match_option (p
, "exception")) != p
) {
3941 if ((opt
= match_option (p
, "monitor")) != p
) {
3946 if ((opt
= match_option (p
, "heapshot")) != p
) {
3951 if ((opt
= match_option (p
, "sample")) != p
) {
3956 if ((opt
= match_option (p
, "counters")) != p
) {
3961 if ((opt
= match_option (p
, "coverage")) != p
) {
3962 printf ("The log profiler no longer supports code coverage. Please use the dedicated coverage profiler instead. See mono-profilers(1) for more information.\n");
3965 if ((opt
= match_option (p
, "stats")) != p
) {
3976 add_find_spec (const char *p
)
3978 if (p
[0] == 'S' && p
[1] == ':') {
3980 find_size
= strtoul (p
+ 2, &vale
, 10);
3982 } else if (p
[0] == 'T' && p
[1] == ':') {
3992 printf ("Mono log profiler report version %d.%d\n", LOG_VERSION_MAJOR
, LOG_VERSION_MINOR
);
3993 printf ("Usage: mprof-report [OPTIONS] FILENAME\n");
3994 printf ("FILENAME can be '-' to read from standard input.\n");
3995 printf ("Options:\n");
3996 printf ("\t--help display this help\n");
3997 printf ("\t--out=FILE write to FILE instead of stdout\n");
3998 printf ("\t--traces collect and show backtraces\n");
3999 printf ("\t--maxframes=NUM limit backtraces to NUM entries\n");
4000 printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
4001 printf ("\t %s\n", reports
);
4002 printf ("\t--method-sort=MODE sort methods according to MODE: total, self, calls\n");
4003 printf ("\t--alloc-sort=MODE sort allocations according to MODE: bytes, count\n");
4004 printf ("\t--counters-sort=MODE sort counters according to MODE: time, category\n");
4005 printf ("\t only accessible in verbose mode\n");
4006 printf ("\t--track=OB1[,OB2...] track what happens to objects OBJ1, O2 etc.\n");
4007 printf ("\t--find=FINDSPEC find and track objects matching FINFSPEC, where FINDSPEC is:\n");
4008 printf ("\t S:minimum_size or T:partial_name\n");
4009 printf ("\t--thread=THREADID consider just the data for thread THREADID\n");
4010 printf ("\t--time=FROM-TO consider data FROM seconds from startup up to TO seconds\n");
4011 printf ("\t--verbose increase verbosity level\n");
4012 printf ("\t--debug display decoding debug info for mprof-report devs\n");
4016 main (int argc
, char *argv
[])
4021 for (i
= 1; i
< argc
; ++i
) {
4022 if (strcmp ("--debug", argv
[i
]) == 0) {
4024 } else if (strcmp ("--help", argv
[i
]) == 0) {
4027 } else if (strncmp ("--alloc-sort=", argv
[i
], 13) == 0) {
4028 const char *val
= argv
[i
] + 13;
4029 if (strcmp (val
, "bytes") == 0) {
4030 alloc_sort_mode
= ALLOC_SORT_BYTES
;
4031 } else if (strcmp (val
, "count") == 0) {
4032 alloc_sort_mode
= ALLOC_SORT_COUNT
;
4037 } else if (strncmp ("--method-sort=", argv
[i
], 14) == 0) {
4038 const char *val
= argv
[i
] + 14;
4039 if (strcmp (val
, "total") == 0) {
4040 method_sort_mode
= METHOD_SORT_TOTAL
;
4041 } else if (strcmp (val
, "self") == 0) {
4042 method_sort_mode
= METHOD_SORT_SELF
;
4043 } else if (strcmp (val
, "calls") == 0) {
4044 method_sort_mode
= METHOD_SORT_CALLS
;
4049 } else if (strncmp ("--counters-sort=", argv
[i
], 16) == 0) {
4050 const char *val
= argv
[i
] + 16;
4051 if (strcmp (val
, "time") == 0) {
4052 counters_sort_mode
= COUNTERS_SORT_TIME
;
4053 } else if (strcmp (val
, "category") == 0) {
4054 counters_sort_mode
= COUNTERS_SORT_CATEGORY
;
4059 } else if (strncmp ("--reports=", argv
[i
], 10) == 0) {
4060 const char *val
= argv
[i
] + 10;
4061 if (!print_reports (NULL
, val
, 1)) {
4066 } else if (strncmp ("--out=", argv
[i
], 6) == 0) {
4067 const char *val
= argv
[i
] + 6;
4068 outfile
= fopen (val
, "w");
4070 printf ("Cannot open output file: %s\n", val
);
4073 } else if (strncmp ("--maxframes=", argv
[i
], 12) == 0) {
4074 const char *val
= argv
[i
] + 12;
4076 trace_max
= strtoul (val
, &vale
, 10);
4077 } else if (strncmp ("--find=", argv
[i
], 7) == 0) {
4078 const char *val
= argv
[i
] + 7;
4079 if (!add_find_spec (val
)) {
4083 } else if (strncmp ("--track=", argv
[i
], 8) == 0) {
4084 const char *val
= argv
[i
] + 8;
4087 uintptr_t tracked_obj
;
4092 tracked_obj
= strtoul (val
, &vale
, 0);
4093 found_object (tracked_obj
);
4096 } else if (strncmp ("--thread=", argv
[i
], 9) == 0) {
4097 const char *val
= argv
[i
] + 9;
4099 thread_filter
= strtoul (val
, &vale
, 0);
4100 } else if (strncmp ("--time=", argv
[i
], 7) == 0) {
4101 char *val
= pstrdup (argv
[i
] + 7);
4102 double from_secs
, to_secs
;
4103 char *top
= strchr (val
, '-');
4109 from_secs
= atof (val
);
4110 to_secs
= atof (top
);
4112 if (from_secs
> to_secs
) {
4116 time_from
= from_secs
* 1000000000;
4117 time_to
= to_secs
* 1000000000;
4118 use_time_filter
= 1;
4119 } else if (strcmp ("--verbose", argv
[i
]) == 0) {
4121 } else if (strcmp ("--traces", argv
[i
]) == 0) {
4124 } else if (strncmp ("--coverage-out=", argv
[i
], 15) == 0) {
4125 // For backwards compatibility.
4134 ctx
= load_file (argv
[i
]);
4136 printf ("Not a log profiler data file (or unsupported version).\n");
4139 while (decode_buffer (ctx
));
4140 flush_context (ctx
);
4141 if (num_tracked_objects
)
4143 print_reports (ctx
, reports
, 0);