2 #include "mono/metadata/profiler-private.h"
3 #include "mono/metadata/debug-helpers.h"
4 #include "mono/metadata/mono-debug.h"
5 #include "mono/metadata/class-internals.h"
6 #include "mono/io-layer/io-layer.h"
10 static MonoProfiler
* current_profiler
= NULL
;
12 static MonoProfileAppDomainFunc domain_start_load
;
13 static MonoProfileAppDomainResult domain_end_load
;
14 static MonoProfileAppDomainFunc domain_start_unload
;
15 static MonoProfileAppDomainFunc domain_end_unload
;
17 static MonoProfileAssemblyFunc assembly_start_load
;
18 static MonoProfileAssemblyResult assembly_end_load
;
19 static MonoProfileAssemblyFunc assembly_start_unload
;
20 static MonoProfileAssemblyFunc assembly_end_unload
;
22 static MonoProfileModuleFunc module_start_load
;
23 static MonoProfileModuleResult module_end_load
;
24 static MonoProfileModuleFunc module_start_unload
;
25 static MonoProfileModuleFunc module_end_unload
;
27 static MonoProfileClassFunc class_start_load
;
28 static MonoProfileClassResult class_end_load
;
29 static MonoProfileClassFunc class_start_unload
;
30 static MonoProfileClassFunc class_end_unload
;
32 static MonoProfileMethodFunc jit_start
;
33 static MonoProfileMethodResult jit_end
;
34 static MonoProfileMethodResult man_unman_transition
;
35 static MonoProfileAllocFunc allocation_cb
;
36 static MonoProfileMethodFunc method_enter
;
37 static MonoProfileMethodFunc method_leave
;
39 static MonoProfileThreadFunc thread_start
;
40 static MonoProfileThreadFunc thread_end
;
42 static MonoProfileCoverageFilterFunc coverage_filter_cb
;
44 static MonoProfileFunc shutdown_callback
;
46 static CRITICAL_SECTION profiler_coverage_mutex
;
48 /* this is directly accessible to other mono libs. */
49 MonoProfileFlags mono_profiler_events
;
52 mono_profiler_install (MonoProfiler
*prof
, MonoProfileFunc callback
)
55 g_error ("profiler already setup");
56 current_profiler
= prof
;
57 shutdown_callback
= callback
;
58 InitializeCriticalSection (&profiler_coverage_mutex
);
62 mono_profiler_set_events (MonoProfileFlags events
)
64 mono_profiler_events
= events
;
68 mono_profiler_get_events (void)
70 return mono_profiler_events
;
74 mono_profiler_install_enter_leave (MonoProfileMethodFunc enter
, MonoProfileMethodFunc fleave
)
77 method_leave
= fleave
;
81 mono_profiler_install_jit_compile (MonoProfileMethodFunc start
, MonoProfileMethodResult end
)
88 mono_profiler_install_thread (MonoProfileThreadFunc start
, MonoProfileThreadFunc end
)
95 mono_profiler_install_transition (MonoProfileMethodResult callback
)
97 man_unman_transition
= callback
;
101 mono_profiler_install_allocation (MonoProfileAllocFunc callback
)
103 allocation_cb
= callback
;
107 mono_profiler_install_coverage_filter (MonoProfileCoverageFilterFunc callback
)
109 coverage_filter_cb
= callback
;
113 mono_profiler_install_appdomain (MonoProfileAppDomainFunc start_load
, MonoProfileAppDomainResult end_load
,
114 MonoProfileAppDomainFunc start_unload
, MonoProfileAppDomainFunc end_unload
)
117 domain_start_load
= start_load
;
118 domain_end_load
= end_load
;
119 domain_start_unload
= start_unload
;
120 domain_end_unload
= end_unload
;
124 mono_profiler_install_assembly (MonoProfileAssemblyFunc start_load
, MonoProfileAssemblyResult end_load
,
125 MonoProfileAssemblyFunc start_unload
, MonoProfileAssemblyFunc end_unload
)
127 assembly_start_load
= start_load
;
128 assembly_end_load
= end_load
;
129 assembly_start_unload
= start_unload
;
130 assembly_end_unload
= end_unload
;
134 mono_profiler_install_module (MonoProfileModuleFunc start_load
, MonoProfileModuleResult end_load
,
135 MonoProfileModuleFunc start_unload
, MonoProfileModuleFunc end_unload
)
137 module_start_load
= start_load
;
138 module_end_load
= end_load
;
139 module_start_unload
= start_unload
;
140 module_end_unload
= end_unload
;
144 mono_profiler_install_class (MonoProfileClassFunc start_load
, MonoProfileClassResult end_load
,
145 MonoProfileClassFunc start_unload
, MonoProfileClassFunc end_unload
)
147 class_start_load
= start_load
;
148 class_end_load
= end_load
;
149 class_start_unload
= start_unload
;
150 class_end_unload
= end_unload
;
154 mono_profiler_method_enter (MonoMethod
*method
)
156 if ((mono_profiler_events
& MONO_PROFILE_ENTER_LEAVE
) && method_enter
)
157 method_enter (current_profiler
, method
);
161 mono_profiler_method_leave (MonoMethod
*method
)
163 if ((mono_profiler_events
& MONO_PROFILE_ENTER_LEAVE
) && method_leave
)
164 method_leave (current_profiler
, method
);
168 mono_profiler_method_jit (MonoMethod
*method
)
170 if ((mono_profiler_events
& MONO_PROFILE_JIT_COMPILATION
) && jit_start
)
171 jit_start (current_profiler
, method
);
175 mono_profiler_method_end_jit (MonoMethod
*method
, int result
)
177 if ((mono_profiler_events
& MONO_PROFILE_JIT_COMPILATION
) && jit_end
)
178 jit_end (current_profiler
, method
, result
);
182 mono_profiler_code_transition (MonoMethod
*method
, int result
)
184 if ((mono_profiler_events
& MONO_PROFILE_TRANSITIONS
) && man_unman_transition
)
185 man_unman_transition (current_profiler
, method
, result
);
189 mono_profiler_allocation (MonoObject
*obj
, MonoClass
*klass
)
191 if ((mono_profiler_events
& MONO_PROFILE_ALLOCATIONS
) && allocation_cb
)
192 allocation_cb (current_profiler
, obj
, klass
);
196 mono_profiler_thread_start (guint32 tid
)
198 if ((mono_profiler_events
& MONO_PROFILE_THREADS
) && thread_start
)
199 thread_start (current_profiler
, tid
);
203 mono_profiler_thread_end (guint32 tid
)
205 if ((mono_profiler_events
& MONO_PROFILE_THREADS
) && thread_end
)
206 thread_end (current_profiler
, tid
);
210 mono_profiler_assembly_event (MonoAssembly
*assembly
, int code
)
212 if (!(mono_profiler_events
& MONO_PROFILE_ASSEMBLY_EVENTS
))
216 case MONO_PROFILE_START_LOAD
:
217 if (assembly_start_load
)
218 assembly_start_load (current_profiler
, assembly
);
220 case MONO_PROFILE_START_UNLOAD
:
221 if (assembly_start_unload
)
222 assembly_start_unload (current_profiler
, assembly
);
224 case MONO_PROFILE_END_UNLOAD
:
225 if (assembly_end_unload
)
226 assembly_end_unload (current_profiler
, assembly
);
229 g_assert_not_reached ();
234 mono_profiler_assembly_loaded (MonoAssembly
*assembly
, int result
)
236 if ((mono_profiler_events
& MONO_PROFILE_ASSEMBLY_EVENTS
) && assembly_end_load
)
237 assembly_end_load (current_profiler
, assembly
, result
);
241 mono_profiler_module_event (MonoImage
*module
, int code
)
243 if (!(mono_profiler_events
& MONO_PROFILE_MODULE_EVENTS
))
247 case MONO_PROFILE_START_LOAD
:
248 if (module_start_load
)
249 module_start_load (current_profiler
, module
);
251 case MONO_PROFILE_START_UNLOAD
:
252 if (module_start_unload
)
253 module_start_unload (current_profiler
, module
);
255 case MONO_PROFILE_END_UNLOAD
:
256 if (module_end_unload
)
257 module_end_unload (current_profiler
, module
);
260 g_assert_not_reached ();
265 mono_profiler_module_loaded (MonoImage
*module
, int result
)
267 if ((mono_profiler_events
& MONO_PROFILE_MODULE_EVENTS
) && module_end_load
)
268 module_end_load (current_profiler
, module
, result
);
272 mono_profiler_class_event (MonoClass
*klass
, int code
)
274 if (!(mono_profiler_events
& MONO_PROFILE_CLASS_EVENTS
))
278 case MONO_PROFILE_START_LOAD
:
279 if (class_start_load
)
280 class_start_load (current_profiler
, klass
);
282 case MONO_PROFILE_START_UNLOAD
:
283 if (class_start_unload
)
284 class_start_unload (current_profiler
, klass
);
286 case MONO_PROFILE_END_UNLOAD
:
287 if (class_end_unload
)
288 class_end_unload (current_profiler
, klass
);
291 g_assert_not_reached ();
296 mono_profiler_class_loaded (MonoClass
*klass
, int result
)
298 if ((mono_profiler_events
& MONO_PROFILE_CLASS_EVENTS
) && class_end_load
)
299 class_end_load (current_profiler
, klass
, result
);
303 mono_profiler_appdomain_event (MonoDomain
*domain
, int code
)
305 if (!(mono_profiler_events
& MONO_PROFILE_APPDOMAIN_EVENTS
))
309 case MONO_PROFILE_START_LOAD
:
310 if (domain_start_load
)
311 domain_start_load (current_profiler
, domain
);
313 case MONO_PROFILE_START_UNLOAD
:
314 if (domain_start_unload
)
315 domain_start_unload (current_profiler
, domain
);
317 case MONO_PROFILE_END_UNLOAD
:
318 if (domain_end_unload
)
319 domain_end_unload (current_profiler
, domain
);
322 g_assert_not_reached ();
327 mono_profiler_appdomain_loaded (MonoDomain
*domain
, int result
)
329 if ((mono_profiler_events
& MONO_PROFILE_APPDOMAIN_EVENTS
) && domain_end_load
)
330 domain_end_load (current_profiler
, domain
, result
);
334 mono_profiler_shutdown (void)
336 if (current_profiler
&& shutdown_callback
)
337 shutdown_callback (current_profiler
);
340 static GHashTable
*coverage_hash
= NULL
;
342 MonoProfileCoverageInfo
*
343 mono_profiler_coverage_alloc (MonoMethod
*method
, int entries
)
345 MonoProfileCoverageInfo
*res
;
347 if (coverage_filter_cb
)
348 if (! (*coverage_filter_cb
) (current_profiler
, method
))
351 EnterCriticalSection (&profiler_coverage_mutex
);
353 coverage_hash
= g_hash_table_new (NULL
, NULL
);
355 res
= g_malloc0 (sizeof (MonoProfileCoverageInfo
) + sizeof (void*) * 2 * entries
);
357 res
->entries
= entries
;
359 g_hash_table_insert (coverage_hash
, method
, res
);
360 LeaveCriticalSection (&profiler_coverage_mutex
);
365 /* safe only when the method antive code has been unloaded */
367 mono_profiler_coverage_free (MonoMethod
*method
)
369 MonoProfileCoverageInfo
* info
;
371 EnterCriticalSection (&profiler_coverage_mutex
);
372 if (!coverage_hash
) {
373 LeaveCriticalSection (&profiler_coverage_mutex
);
377 info
= g_hash_table_lookup (coverage_hash
, method
);
380 g_hash_table_remove (coverage_hash
, method
);
382 LeaveCriticalSection (&profiler_coverage_mutex
);
386 mono_profiler_coverage_get (MonoProfiler
*prof
, MonoMethod
*method
, MonoProfileCoverageFunc func
)
388 MonoProfileCoverageInfo
* info
;
391 unsigned char *start
, *end
, *cil_code
;
392 MonoMethodHeader
*header
;
393 MonoProfileCoverageEntry entry
;
395 EnterCriticalSection (&profiler_coverage_mutex
);
396 info
= g_hash_table_lookup (coverage_hash
, method
);
397 LeaveCriticalSection (&profiler_coverage_mutex
);
402 header
= mono_method_get_header (method
);
403 start
= (unsigned char*)header
->code
;
404 end
= start
+ header
->code_size
;
405 for (i
= 0; i
< info
->entries
; ++i
) {
406 cil_code
= info
->data
[i
].cil_code
;
407 if (cil_code
&& cil_code
>= start
&& cil_code
< end
) {
408 offset
= cil_code
- start
;
409 entry
.iloffset
= offset
;
410 entry
.method
= method
;
411 entry
.counter
= info
->data
[i
].count
;
412 /* the debug interface doesn't support column info, sigh */
414 entry
.filename
= mono_debug_source_location_from_il_offset (method
, offset
, &line
);
423 * Small profiler extracted from mint: we should move it in a loadable module
424 * and improve it to do graphs and more accurate timestamping with rdtsc.
428 #define USE_WIN32COUNTER 0
432 unsigned int lows
, highs
, lowe
, highe
;
435 #define rdtsc(low,high) \
436 __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
441 rdtsc_elapsed (MonoRdtscTimer
*t
)
443 unsigned long long diff
;
444 unsigned int highe
= t
->highe
;
445 if (t
->lowe
< t
->lows
)
447 diff
= (((unsigned long long) highe
- t
->highs
) << 32) + (t
->lowe
- t
->lows
);
448 return ((double)diff
/ freq
) / 1000000; /* have to return the result in seconds */
459 if (!(cpuinfo
= fopen ("/proc/cpuinfo", "r")))
461 while (fgets (buf
, sizeof(buf
), cpuinfo
)) {
462 if (sscanf (buf
, "cpu MHz : %f", &val
) == 1) {
463 /*printf ("got mh: %f\n", val);*/
466 if (strncmp (buf
, "flags", 5) == 0) {
467 if (strstr (buf
, "tsc")) {
469 /*printf ("have tsc\n");*/
474 return have_flag
? have_freq
: 0;
477 #define MONO_TIMER_STARTUP \
478 if (!(freq = have_rdtsc ())) g_error ("Compiled with TSC support, but none found");
479 #define MONO_TIMER_TYPE MonoRdtscTimer
480 #define MONO_TIMER_INIT(t)
481 #define MONO_TIMER_DESTROY(t)
482 #define MONO_TIMER_START(t) rdtsc ((t).lows, (t).highs);
483 #define MONO_TIMER_STOP(t) rdtsc ((t).lowe, (t).highe);
484 #define MONO_TIMER_ELAPSED(t) rdtsc_elapsed (&(t))
486 #elif USE_WIN32COUNTER
490 LARGE_INTEGER start
, stop
;
496 win32_elapsed (MonoWin32Timer
*t
)
498 LONGLONG diff
= t
->stop
.QuadPart
- t
->start
.QuadPart
;
499 return ((double)diff
/ freq
) / 1000000; /* have to return the result in seconds */
503 have_win32counter (void) {
506 if (!QueryPerformanceFrequency (&f
))
511 #define MONO_TIMER_STARTUP \
512 if (!(freq = have_win32counter ())) g_error ("Compiled with Win32 counter support, but none found");
513 #define MONO_TIMER_TYPE MonoWin32Timer
514 #define MONO_TIMER_INIT(t)
515 #define MONO_TIMER_DESTROY(t)
516 #define MONO_TIMER_START(t) QueryPerformanceCounter (&(t).start)
517 #define MONO_TIMER_STOP(t) QueryPerformanceCounter (&(t).stop)
518 #define MONO_TIMER_ELAPSED(t) win32_elapsed (&(t))
523 GTimeVal start
, stop
;
527 timeval_elapsed (MonoGLibTimer
*t
)
529 if (t
->start
.tv_usec
> t
->stop
.tv_usec
) {
530 t
->stop
.tv_usec
+= G_USEC_PER_SEC
;
533 return (t
->stop
.tv_sec
- t
->start
.tv_sec
)
534 + ((double)(t
->stop
.tv_usec
- t
->start
.tv_usec
))/ G_USEC_PER_SEC
;
537 #define MONO_TIMER_STARTUP
538 #define MONO_TIMER_TYPE MonoGLibTimer
539 #define MONO_TIMER_INIT(t)
540 #define MONO_TIMER_DESTROY(t)
541 #define MONO_TIMER_START(t) g_get_current_time (&(t).start)
542 #define MONO_TIMER_STOP(t) g_get_current_time (&(t).stop)
543 #define MONO_TIMER_ELAPSED(t) timeval_elapsed (&(t))
546 typedef struct _AllocInfo AllocInfo
;
547 typedef struct _CallerInfo CallerInfo
;
548 typedef struct _LastCallerInfo LastCallerInfo
;
550 struct _MonoProfiler
{
552 MonoMemPool
*mempool
;
553 /* info about JIT time */
554 MONO_TIMER_TYPE jit_timer
;
557 MonoMethod
*max_jit_method
;
562 /* chain of callers for the current thread */
563 LastCallerInfo
*callers
;
564 /* LastCallerInfo nodes for faster allocation */
565 LastCallerInfo
*cstorage
;
572 AllocInfo
*alloc_info
;
573 CallerInfo
*caller_info
;
576 typedef struct _MethodCallProfile MethodCallProfile
;
578 struct _MethodCallProfile
{
579 MethodCallProfile
*next
;
580 MONO_TIMER_TYPE timer
;
597 struct _LastCallerInfo
{
598 LastCallerInfo
*next
;
600 MONO_TIMER_TYPE timer
;
604 create_profiler (void)
606 MonoProfiler
*prof
= g_new0 (MonoProfiler
, 1);
608 prof
->methods
= g_hash_table_new (NULL
, NULL
);
609 MONO_TIMER_INIT (prof
->jit_timer
);
610 prof
->mempool
= mono_mempool_new ();
615 #ifdef HAVE_KW_THREAD
616 static __thread MonoProfiler
* tls_profiler
;
617 # define GET_PROFILER() tls_profiler
618 # define SET_PROFILER(x) tls_profiler = (x)
619 # define ALLOC_PROFILER() /* nop */
621 static guint32 profiler_thread_id
= -1;
622 # define GET_PROFILER() ((MonoProfiler *)TlsGetValue (profiler_thread_id))
623 # define SET_PROFILER(x) TlsSetValue (profiler_thread_id, x);
624 # define ALLOC_PROFILER() profiler_thread_id = TlsAlloc ()
627 #define GET_THREAD_PROF(prof) do { \
628 MonoProfiler *_tprofiler = GET_PROFILER (); \
630 _tprofiler = create_profiler (); \
631 prof->per_thread = g_slist_prepend (prof->per_thread, _tprofiler); \
632 SET_PROFILER (_tprofiler); \
637 /* thread unsafe but faster variant */
638 #define GET_THREAD_PROF(prof)
642 compare_profile (MethodProfile
*profa
, MethodProfile
*profb
)
644 return (gint
)((profb
->total
- profa
->total
)*1000);
648 build_profile (MonoMethod
*m
, MethodProfile
*prof
, GList
**funcs
)
651 *funcs
= g_list_insert_sorted (*funcs
, prof
, (GCompareFunc
)compare_profile
);
655 method_get_name (MonoMethod
* method
)
659 sig
= mono_signature_get_desc (method
->signature
, FALSE
);
660 res
= g_strdup_printf ("%s.%s::%s(%s)", method
->klass
->name_space
, method
->klass
->name
,
666 static void output_callers (MethodProfile
*p
);
669 output_profile (GList
*funcs
)
674 guint64 total_calls
= 0;
677 g_print ("Time(ms) Count P/call(ms) Method name\n");
678 for (tmp
= funcs
; tmp
; tmp
= tmp
->next
) {
680 total_calls
+= p
->count
;
681 if (!(gint
)(p
->total
*1000))
683 m
= method_get_name (p
->method
);
684 printf ("########################\n");
685 printf ("% 8.3f ", (double) (p
->total
* 1000));
686 printf ("%7llu ", p
->count
);
687 printf ("% 8.3f ", (double) (p
->total
* 1000)/(double)p
->count
);
694 printf ("Total number of calls: %lld\n", total_calls
);
703 compare_newobj_profile (NewobjProfile
*profa
, NewobjProfile
*profb
)
705 return (gint
)profb
->count
- (gint
)profa
->count
;
709 build_newobj_profile (MonoClass
*class, MethodProfile
*mprof
, GList
**funcs
)
711 NewobjProfile
*prof
= g_new (NewobjProfile
, 1);
716 /* we use the total amount of memory to sort */
717 for (tmp
= mprof
->alloc_info
; tmp
; tmp
= tmp
->next
)
720 *funcs
= g_list_insert_sorted (*funcs
, prof
, (GCompareFunc
)compare_newobj_profile
);
724 compare_caller (CallerInfo
*a
, CallerInfo
*b
)
726 return b
->count
- a
->count
;
730 compare_alloc (AllocInfo
*a
, AllocInfo
*b
)
732 return b
->mem
- a
->mem
;
736 sort_alloc_list (AllocInfo
*ai
)
740 for (tmp
= ai
; tmp
; tmp
= tmp
->next
) {
741 l
= g_slist_insert_sorted (l
, tmp
, (GCompareFunc
)compare_alloc
);
747 sort_caller_list (CallerInfo
*ai
)
751 for (tmp
= ai
; tmp
; tmp
= tmp
->next
) {
752 l
= g_slist_insert_sorted (l
, tmp
, (GCompareFunc
)compare_caller
);
758 output_callers (MethodProfile
*p
) {
759 guint total_callers
, percent
;
760 GSList
*sorted
, *tmps
;
764 g_print (" Callers (with count) that contribute at least for 1%%:\n");
766 for (cinfo
= p
->caller_info
; cinfo
; cinfo
= cinfo
->next
) {
767 total_callers
+= cinfo
->count
;
769 sorted
= sort_caller_list (p
->caller_info
);
770 for (tmps
= sorted
; tmps
; tmps
= tmps
->next
) {
772 percent
= (cinfo
->count
* 100)/total_callers
;
775 m
= method_get_name (cinfo
->caller
);
776 g_print (" %8d % 3d %% %s\n", cinfo
->count
, percent
, m
);
782 output_newobj_profile (GList
*proflist
)
793 GSList
*sorted
, *tmps
;
795 g_print ("\nAllocation profiler\n");
798 g_print ("%-9s %s\n", "Total mem", "Method");
799 for (tmp
= proflist
; tmp
; tmp
= tmp
->next
) {
802 if (p
->count
< 50000)
805 m
= method_get_name (mp
->method
);
806 g_print ("########################\n%8d KB %s\n", p
->count
/ 1024, m
);
808 sorted
= sort_alloc_list (mp
->alloc_info
);
809 for (tmps
= sorted
; tmps
; tmps
= tmps
->next
) {
811 if (ainfo
->mem
< 50000)
813 klass
= ainfo
->klass
;
816 klass
= klass
->element_class
;
820 g_snprintf (buf
, sizeof (buf
), "%s.%s%s",
821 klass
->name_space
, klass
->name
, isarray
);
822 g_print (" %8d KB %8d %-48s\n", ainfo
->mem
/ 1024, ainfo
->count
, buf
);
827 g_print ("Total memory allocated: %d KB\n", total
/ 1024);
831 merge_methods (MonoMethod
*method
, MethodProfile
*profile
, MonoProfiler
*prof
)
833 MethodProfile
*mprof
;
834 AllocInfo
*talloc_info
, *alloc_info
;
835 CallerInfo
*tcaller_info
, *caller_info
;
837 mprof
= g_hash_table_lookup (prof
->methods
, method
);
839 /* the master thread didn't see this method, just transfer the info as is */
840 g_hash_table_insert (prof
->methods
, method
, profile
);
843 /* merge the info from profile into mprof */
844 mprof
->count
+= profile
->count
;
845 mprof
->total
+= profile
->total
;
846 /* merge alloc info */
847 for (talloc_info
= profile
->alloc_info
; talloc_info
; talloc_info
= talloc_info
->next
) {
848 for (alloc_info
= mprof
->alloc_info
; alloc_info
; alloc_info
= alloc_info
->next
) {
849 if (alloc_info
->klass
== talloc_info
->klass
) {
850 /* mprof already has a record for the klass, merge */
851 alloc_info
->count
+= talloc_info
->count
;
852 alloc_info
->mem
+= talloc_info
->mem
;
857 /* mprof didn't have the info, just copy it over */
858 alloc_info
= mono_mempool_alloc0 (prof
->mempool
, sizeof (AllocInfo
));
859 *alloc_info
= *talloc_info
;
860 alloc_info
->next
= mprof
->alloc_info
;
861 mprof
->alloc_info
= alloc_info
->next
;
864 /* merge callers info */
865 for (tcaller_info
= profile
->caller_info
; tcaller_info
; tcaller_info
= tcaller_info
->next
) {
866 for (caller_info
= mprof
->caller_info
; caller_info
; caller_info
= caller_info
->next
) {
867 if (caller_info
->caller
== tcaller_info
->caller
) {
868 /* mprof already has a record for the caller method, merge */
869 caller_info
->count
+= tcaller_info
->count
;
874 /* mprof didn't have the info, just copy it over */
875 caller_info
= mono_mempool_alloc0 (prof
->mempool
, sizeof (CallerInfo
));
876 *caller_info
= *tcaller_info
;
877 caller_info
->next
= mprof
->caller_info
;
878 mprof
->caller_info
= caller_info
;
884 merge_thread_data (MonoProfiler
*master
, MonoProfiler
*tprof
)
886 master
->jit_time
+= tprof
->jit_time
;
887 master
->methods_jitted
+= tprof
->methods_jitted
;
888 if (master
->max_jit_time
< tprof
->max_jit_time
) {
889 master
->max_jit_time
= tprof
->max_jit_time
;
890 master
->max_jit_method
= tprof
->max_jit_method
;
893 g_hash_table_foreach (tprof
->methods
, (GHFunc
)merge_methods
, master
);
897 simple_method_enter (MonoProfiler
*prof
, MonoMethod
*method
)
899 MethodProfile
*profile_info
;
900 LastCallerInfo
*callinfo
;
901 GET_THREAD_PROF (prof
);
902 /*g_print ("enter %p %s::%s in %d (%p)\n", method, method->klass->name, method->name, GetCurrentThreadId (), prof);*/
903 if (!(profile_info
= g_hash_table_lookup (prof
->methods
, method
))) {
904 profile_info
= mono_mempool_alloc0 (prof
->mempool
, sizeof (MethodProfile
));
905 MONO_TIMER_INIT (profile_info
->u
.timer
);
906 g_hash_table_insert (prof
->methods
, method
, profile_info
);
908 profile_info
->count
++;
911 MonoMethod
*caller
= prof
->callers
->method
;
912 for (cinfo
= profile_info
->caller_info
; cinfo
; cinfo
= cinfo
->next
) {
913 if (cinfo
->caller
== caller
)
917 cinfo
= mono_mempool_alloc0 (prof
->mempool
, sizeof (CallerInfo
));
918 cinfo
->caller
= caller
;
919 cinfo
->next
= profile_info
->caller_info
;
920 profile_info
->caller_info
= cinfo
;
924 if (!(callinfo
= prof
->cstorage
)) {
925 callinfo
= mono_mempool_alloc (prof
->mempool
, sizeof (LastCallerInfo
));
926 MONO_TIMER_INIT (callinfo
->timer
);
928 prof
->cstorage
= prof
->cstorage
->next
;
930 callinfo
->method
= method
;
931 callinfo
->next
= prof
->callers
;
932 prof
->callers
= callinfo
;
933 MONO_TIMER_START (callinfo
->timer
);
937 simple_method_leave (MonoProfiler
*prof
, MonoMethod
*method
)
939 MethodProfile
*profile_info
;
940 LastCallerInfo
*callinfo
, *newcallinfo
= NULL
;
942 GET_THREAD_PROF (prof
);
943 /*g_print ("leave %p %s::%s in %d (%p)\n", method, method->klass->name, method->name, GetCurrentThreadId (), prof);*/
944 callinfo
= prof
->callers
;
945 /* should really not happen, but we don't catch exceptions events, yet ... */
947 MONO_TIMER_STOP (callinfo
->timer
);
948 profile_info
= g_hash_table_lookup (prof
->methods
, callinfo
->method
);
950 profile_info
->total
+= MONO_TIMER_ELAPSED (callinfo
->timer
);
951 newcallinfo
= callinfo
->next
;
952 callinfo
->next
= prof
->cstorage
;
953 prof
->cstorage
= callinfo
;
954 if (callinfo
->method
== method
)
956 callinfo
= newcallinfo
;
958 prof
->callers
= newcallinfo
;
962 simple_allocation (MonoProfiler
*prof
, MonoObject
*obj
, MonoClass
*klass
)
964 MethodProfile
*profile_info
;
967 GET_THREAD_PROF (prof
);
969 MonoMethod
*caller
= prof
->callers
->method
;
971 /* Otherwise all allocations are attributed to icall_wrapper_mono_object_new */
972 if (caller
->wrapper_type
== MONO_WRAPPER_MANAGED_TO_NATIVE
)
973 caller
= prof
->callers
->next
->method
;
975 if (!(profile_info
= g_hash_table_lookup (prof
->methods
, caller
)))
976 g_assert_not_reached ();
978 return; /* fine for now */
981 for (tmp
= profile_info
->alloc_info
; tmp
; tmp
= tmp
->next
) {
982 if (tmp
->klass
== klass
)
986 tmp
= mono_mempool_alloc0 (prof
->mempool
, sizeof (AllocInfo
));
988 tmp
->next
= profile_info
->alloc_info
;
989 profile_info
->alloc_info
= tmp
;
992 if (klass
== mono_defaults
.string_class
) {
993 tmp
->mem
+= sizeof (MonoString
) + 2 * mono_string_length ((MonoString
*)obj
) + 2;
994 } else if (klass
->parent
== mono_defaults
.array_class
) {
995 tmp
->mem
+= sizeof (MonoArray
) + mono_array_element_size (klass
) * mono_array_length ((MonoArray
*)obj
);
997 tmp
->mem
+= mono_class_instance_size (klass
);
1002 simple_method_jit (MonoProfiler
*prof
, MonoMethod
*method
)
1004 GET_THREAD_PROF (prof
);
1005 prof
->methods_jitted
++;
1006 MONO_TIMER_START (prof
->jit_timer
);
1010 simple_method_end_jit (MonoProfiler
*prof
, MonoMethod
*method
, int result
)
1013 GET_THREAD_PROF (prof
);
1014 MONO_TIMER_STOP (prof
->jit_timer
);
1015 jtime
= MONO_TIMER_ELAPSED (prof
->jit_timer
);
1016 prof
->jit_time
+= jtime
;
1017 if (jtime
> prof
->max_jit_time
) {
1018 prof
->max_jit_time
= jtime
;
1019 prof
->max_jit_method
= method
;
1024 simple_shutdown (MonoProfiler
*prof
)
1026 GList
*profile
= NULL
;
1027 MonoProfiler
*tprof
;
1031 for (tmp
= prof
->per_thread
; tmp
; tmp
= tmp
->next
) {
1033 merge_thread_data (prof
, tprof
);
1036 printf("Total time spent compiling %d methods (sec): %.4g\n", prof
->methods_jitted
, prof
->jit_time
);
1037 if (prof
->max_jit_method
) {
1038 str
= method_get_name (prof
->max_jit_method
);
1039 printf("Slowest method to compile (sec): %.4g: %s\n", prof
->max_jit_time
, str
);
1042 g_hash_table_foreach (prof
->methods
, (GHFunc
)build_profile
, &profile
);
1043 output_profile (profile
);
1044 g_list_free (profile
);
1047 g_hash_table_foreach (prof
->methods
, (GHFunc
)build_newobj_profile
, &profile
);
1048 output_newobj_profile (profile
);
1049 g_list_free (profile
);
1053 mono_profiler_install_simple (const char *desc
)
1056 gchar
**args
, **ptr
;
1057 MonoProfileFlags flags
= MONO_PROFILE_ENTER_LEAVE
|MONO_PROFILE_JIT_COMPILATION
|MONO_PROFILE_ALLOCATIONS
;
1063 if (strstr (desc
, ":"))
1064 desc
= strstr (desc
, ":") + 1;
1067 args
= g_strsplit (desc
? desc
: "", ",", -1);
1069 for (ptr
= args
; ptr
&& *ptr
; ptr
++) {
1070 const char *arg
= *ptr
;
1072 if (!strcmp (arg
, "-time"))
1073 flags
&= ~MONO_PROFILE_ENTER_LEAVE
;
1075 if (!strcmp (arg
, "-alloc"))
1076 flags
&= ~MONO_PROFILE_ALLOCATIONS
;
1078 fprintf (stderr
, "profiler : Unknown argument '%s'.\n", arg
);
1084 prof
= create_profiler ();
1086 SET_PROFILER (prof
);
1088 mono_profiler_install (prof
, simple_shutdown
);
1089 /* later do also object creation */
1090 mono_profiler_install_enter_leave (simple_method_enter
, simple_method_leave
);
1091 mono_profiler_install_jit_compile (simple_method_jit
, simple_method_end_jit
);
1092 mono_profiler_install_allocation (simple_allocation
);
1093 mono_profiler_set_events (flags
);
1096 typedef void (*ProfilerInitializer
) (const char*);
1097 #define INITIALIZER_NAME "mono_profiler_startup"
1100 mono_profiler_load (const char *desc
)
1102 if (!desc
|| (strcmp ("default", desc
) == 0) || (strncmp (desc
, "default:", 8) == 0)) {
1103 mono_profiler_install_simple (desc
);
1106 const char* col
= strchr (desc
, ':');
1111 mname
= g_memdup (desc
, col
- desc
);
1112 mname
[col
- desc
] = 0;
1114 mname
= g_strdup (desc
);
1116 libname
= g_strdup_printf ("mono-profiler-%s", mname
);
1117 path
= g_module_build_path (NULL
, libname
);
1118 pmodule
= g_module_open (path
, G_MODULE_BIND_LAZY
);
1120 ProfilerInitializer func
;
1121 if (!g_module_symbol (pmodule
, INITIALIZER_NAME
, (gpointer
*)&func
)) {
1122 g_warning ("Cannot find initializer function %s in profiler module: %s", INITIALIZER_NAME
, libname
);
1127 g_warning ("Error loading profiler module '%s': %s", libname
, g_module_error ());