2 #include "mono/metadata/profiler-private.h"
3 #include "mono/metadata/debug-helpers.h"
4 #include "mono/metadata/mono-debug.h"
5 #include "mono/io-layer/io-layer.h"
9 static MonoProfiler * current_profiler = NULL;
11 static MonoProfileAppDomainFunc domain_start_load;
12 static MonoProfileAppDomainResult domain_end_load;
13 static MonoProfileAppDomainFunc domain_start_unload;
14 static MonoProfileAppDomainFunc domain_end_unload;
16 static MonoProfileAssemblyFunc assembly_start_load;
17 static MonoProfileAssemblyResult assembly_end_load;
18 static MonoProfileAssemblyFunc assembly_start_unload;
19 static MonoProfileAssemblyFunc assembly_end_unload;
21 static MonoProfileModuleFunc module_start_load;
22 static MonoProfileModuleResult module_end_load;
23 static MonoProfileModuleFunc module_start_unload;
24 static MonoProfileModuleFunc module_end_unload;
26 static MonoProfileClassFunc class_start_load;
27 static MonoProfileClassResult class_end_load;
28 static MonoProfileClassFunc class_start_unload;
29 static MonoProfileClassFunc class_end_unload;
31 static MonoProfileMethodFunc jit_start;
32 static MonoProfileMethodResult jit_end;
33 static MonoProfileMethodResult man_unman_transition;
34 static MonoProfileAllocFunc allocation_cb;
35 static MonoProfileMethodFunc method_enter;
36 static MonoProfileMethodFunc method_leave;
38 static MonoProfileThreadFunc thread_start;
39 static MonoProfileThreadFunc thread_end;
41 static MonoProfileCoverageFilterFunc coverage_filter_cb;
43 static MonoProfileFunc shutdown_callback;
45 static CRITICAL_SECTION profiler_coverage_mutex;
47 /* this is directly accessible to other mono libs. */
48 MonoProfileFlags mono_profiler_events;
51 mono_profiler_install (MonoProfiler *prof, MonoProfileFunc callback)
54 g_error ("profiler already setup");
55 current_profiler = prof;
56 shutdown_callback = callback;
57 InitializeCriticalSection (&profiler_coverage_mutex);
61 mono_profiler_set_events (MonoProfileFlags events)
63 mono_profiler_events = events;
67 mono_profiler_get_events (void)
69 return mono_profiler_events;
73 mono_profiler_install_enter_leave (MonoProfileMethodFunc enter, MonoProfileMethodFunc fleave)
76 method_leave = fleave;
80 mono_profiler_install_jit_compile (MonoProfileMethodFunc start, MonoProfileMethodResult end)
87 mono_profiler_install_thread (MonoProfileThreadFunc start, MonoProfileThreadFunc end)
94 mono_profiler_install_transition (MonoProfileMethodResult callback)
96 man_unman_transition = callback;
100 mono_profiler_install_allocation (MonoProfileAllocFunc callback)
102 allocation_cb = callback;
106 mono_profiler_install_coverage_filter (MonoProfileCoverageFilterFunc callback)
108 coverage_filter_cb = callback;
112 mono_profiler_install_appdomain (MonoProfileAppDomainFunc start_load, MonoProfileAppDomainResult end_load,
113 MonoProfileAppDomainFunc start_unload, MonoProfileAppDomainFunc end_unload)
116 domain_start_load = start_load;
117 domain_end_load = end_load;
118 domain_start_unload = start_unload;
119 domain_end_unload = end_unload;
123 mono_profiler_install_assembly (MonoProfileAssemblyFunc start_load, MonoProfileAssemblyResult end_load,
124 MonoProfileAssemblyFunc start_unload, MonoProfileAssemblyFunc end_unload)
126 assembly_start_load = start_load;
127 assembly_end_load = end_load;
128 assembly_start_unload = start_unload;
129 assembly_end_unload = end_unload;
133 mono_profiler_install_module (MonoProfileModuleFunc start_load, MonoProfileModuleResult end_load,
134 MonoProfileModuleFunc start_unload, MonoProfileModuleFunc end_unload)
136 module_start_load = start_load;
137 module_end_load = end_load;
138 module_start_unload = start_unload;
139 module_end_unload = end_unload;
143 mono_profiler_install_class (MonoProfileClassFunc start_load, MonoProfileClassResult end_load,
144 MonoProfileClassFunc start_unload, MonoProfileClassFunc end_unload)
146 class_start_load = start_load;
147 class_end_load = end_load;
148 class_start_unload = start_unload;
149 class_end_unload = end_unload;
153 mono_profiler_method_enter (MonoMethod *method)
155 if ((mono_profiler_events & MONO_PROFILE_ENTER_LEAVE) && method_enter)
156 method_enter (current_profiler, method);
160 mono_profiler_method_leave (MonoMethod *method)
162 if ((mono_profiler_events & MONO_PROFILE_ENTER_LEAVE) && method_leave)
163 method_leave (current_profiler, method);
167 mono_profiler_method_jit (MonoMethod *method)
169 if ((mono_profiler_events & MONO_PROFILE_JIT_COMPILATION) && jit_start)
170 jit_start (current_profiler, method);
174 mono_profiler_method_end_jit (MonoMethod *method, int result)
176 if ((mono_profiler_events & MONO_PROFILE_JIT_COMPILATION) && jit_end)
177 jit_end (current_profiler, method, result);
181 mono_profiler_code_transition (MonoMethod *method, int result)
183 if ((mono_profiler_events & MONO_PROFILE_TRANSITIONS) && man_unman_transition)
184 man_unman_transition (current_profiler, method, result);
188 mono_profiler_allocation (MonoObject *obj, MonoClass *klass)
190 if ((mono_profiler_events & MONO_PROFILE_ALLOCATIONS) && allocation_cb)
191 allocation_cb (current_profiler, obj, klass);
195 mono_profiler_thread_start (guint32 tid)
197 if ((mono_profiler_events & MONO_PROFILE_THREADS) && thread_start)
198 thread_start (current_profiler, tid);
202 mono_profiler_thread_end (guint32 tid)
204 if ((mono_profiler_events & MONO_PROFILE_THREADS) && thread_end)
205 thread_end (current_profiler, tid);
209 mono_profiler_assembly_event (MonoAssembly *assembly, int code)
211 if (!(mono_profiler_events & MONO_PROFILE_ASSEMBLY_EVENTS))
215 case MONO_PROFILE_START_LOAD:
216 if (assembly_start_load)
217 assembly_start_load (current_profiler, assembly);
219 case MONO_PROFILE_START_UNLOAD:
220 if (assembly_start_unload)
221 assembly_start_unload (current_profiler, assembly);
223 case MONO_PROFILE_END_UNLOAD:
224 if (assembly_end_unload)
225 assembly_end_unload (current_profiler, assembly);
228 g_assert_not_reached ();
233 mono_profiler_assembly_loaded (MonoAssembly *assembly, int result)
235 if ((mono_profiler_events & MONO_PROFILE_ASSEMBLY_EVENTS) && assembly_end_load)
236 assembly_end_load (current_profiler, assembly, result);
240 mono_profiler_module_event (MonoImage *module, int code)
242 if (!(mono_profiler_events & MONO_PROFILE_MODULE_EVENTS))
246 case MONO_PROFILE_START_LOAD:
247 if (module_start_load)
248 module_start_load (current_profiler, module);
250 case MONO_PROFILE_START_UNLOAD:
251 if (module_start_unload)
252 module_start_unload (current_profiler, module);
254 case MONO_PROFILE_END_UNLOAD:
255 if (module_end_unload)
256 module_end_unload (current_profiler, module);
259 g_assert_not_reached ();
264 mono_profiler_module_loaded (MonoImage *module, int result)
266 if ((mono_profiler_events & MONO_PROFILE_MODULE_EVENTS) && module_end_load)
267 module_end_load (current_profiler, module, result);
271 mono_profiler_class_event (MonoClass *klass, int code)
273 if (!(mono_profiler_events & MONO_PROFILE_CLASS_EVENTS))
277 case MONO_PROFILE_START_LOAD:
278 if (class_start_load)
279 class_start_load (current_profiler, klass);
281 case MONO_PROFILE_START_UNLOAD:
282 if (class_start_unload)
283 class_start_unload (current_profiler, klass);
285 case MONO_PROFILE_END_UNLOAD:
286 if (class_end_unload)
287 class_end_unload (current_profiler, klass);
290 g_assert_not_reached ();
295 mono_profiler_class_loaded (MonoClass *klass, int result)
297 if ((mono_profiler_events & MONO_PROFILE_CLASS_EVENTS) && class_end_load)
298 class_end_load (current_profiler, klass, result);
302 mono_profiler_appdomain_event (MonoDomain *domain, int code)
304 if (!(mono_profiler_events & MONO_PROFILE_APPDOMAIN_EVENTS))
308 case MONO_PROFILE_START_LOAD:
309 if (domain_start_load)
310 domain_start_load (current_profiler, domain);
312 case MONO_PROFILE_START_UNLOAD:
313 if (domain_start_unload)
314 domain_start_unload (current_profiler, domain);
316 case MONO_PROFILE_END_UNLOAD:
317 if (domain_end_unload)
318 domain_end_unload (current_profiler, domain);
321 g_assert_not_reached ();
326 mono_profiler_appdomain_loaded (MonoDomain *domain, int result)
328 if ((mono_profiler_events & MONO_PROFILE_APPDOMAIN_EVENTS) && domain_end_load)
329 domain_end_load (current_profiler, domain, result);
333 mono_profiler_shutdown (void)
335 if (current_profiler && shutdown_callback)
336 shutdown_callback (current_profiler);
339 static GHashTable *coverage_hash = NULL;
341 MonoProfileCoverageInfo*
342 mono_profiler_coverage_alloc (MonoMethod *method, int entries)
344 MonoProfileCoverageInfo *res;
346 if (coverage_filter_cb)
347 if (! (*coverage_filter_cb) (current_profiler, method))
350 EnterCriticalSection (&profiler_coverage_mutex);
352 coverage_hash = g_hash_table_new (NULL, NULL);
354 res = g_malloc0 (sizeof (MonoProfileCoverageInfo) + sizeof (void*) * 2 * entries);
356 res->entries = entries;
358 g_hash_table_insert (coverage_hash, method, res);
359 LeaveCriticalSection (&profiler_coverage_mutex);
364 /* safe only when the method antive code has been unloaded */
366 mono_profiler_coverage_free (MonoMethod *method)
368 MonoProfileCoverageInfo* info;
370 EnterCriticalSection (&profiler_coverage_mutex);
371 if (!coverage_hash) {
372 LeaveCriticalSection (&profiler_coverage_mutex);
376 info = g_hash_table_lookup (coverage_hash, method);
379 g_hash_table_remove (coverage_hash, method);
381 LeaveCriticalSection (&profiler_coverage_mutex);
385 mono_profiler_coverage_get (MonoProfiler *prof, MonoMethod *method, MonoProfileCoverageFunc func)
387 MonoProfileCoverageInfo* info;
390 unsigned char *start, *end, *cil_code;
391 MonoMethodHeader *header;
392 MonoProfileCoverageEntry entry;
394 EnterCriticalSection (&profiler_coverage_mutex);
395 info = g_hash_table_lookup (coverage_hash, method);
396 LeaveCriticalSection (&profiler_coverage_mutex);
401 header = ((MonoMethodNormal *)method)->header;
402 start = (unsigned char*)header->code;
403 end = start + header->code_size;
404 for (i = 0; i < info->entries; ++i) {
405 cil_code = info->data [i].cil_code;
406 if (cil_code && cil_code >= start && cil_code < end) {
407 offset = cil_code - start;
408 entry.iloffset = offset;
409 entry.method = method;
410 entry.counter = info->data [i].count;
411 /* the debug interface doesn't support column info, sigh */
413 entry.filename = mono_debug_source_location_from_il_offset (method, offset, &line);
422 * Small profiler extracted from mint: we should move it in a loadable module
423 * and improve it to do graphs and more accurate timestamping with rdtsc.
427 #define USE_WIN32COUNTER 0
431 unsigned int lows, highs, lowe, highe;
434 #define rdtsc(low,high) \
435 __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
440 rdtsc_elapsed (MonoRdtscTimer *t)
442 unsigned long long diff;
443 unsigned int highe = t->highe;
444 if (t->lowe < t->lows)
446 diff = (((unsigned long long) highe - t->highs) << 32) + (t->lowe - t->lows);
447 return ((double)diff / freq) / 1000000; /* have to return the result in seconds */
458 if (!(cpuinfo = fopen ("/proc/cpuinfo", "r")))
460 while (fgets (buf, sizeof(buf), cpuinfo)) {
461 if (sscanf (buf, "cpu MHz : %f", &val) == 1) {
462 /*printf ("got mh: %f\n", val);*/
465 if (strncmp (buf, "flags", 5) == 0) {
466 if (strstr (buf, "tsc")) {
468 /*printf ("have tsc\n");*/
473 return have_flag? have_freq: 0;
476 #define MONO_TIMER_STARTUP \
477 if (!(freq = have_rdtsc ())) g_error ("Compiled with TSC support, but none found");
478 #define MONO_TIMER_TYPE MonoRdtscTimer
479 #define MONO_TIMER_INIT(t)
480 #define MONO_TIMER_DESTROY(t)
481 #define MONO_TIMER_START(t) rdtsc ((t).lows, (t).highs);
482 #define MONO_TIMER_STOP(t) rdtsc ((t).lowe, (t).highe);
483 #define MONO_TIMER_ELAPSED(t) rdtsc_elapsed (&(t))
485 #elif USE_WIN32COUNTER
489 LARGE_INTEGER start, stop;
495 win32_elapsed (MonoWin32Timer *t)
497 LONGLONG diff = t->stop.QuadPart - t->start.QuadPart;
498 return ((double)diff / freq) / 1000000; /* have to return the result in seconds */
502 have_win32counter (void) {
505 if (!QueryPerformanceFrequency (&f))
510 #define MONO_TIMER_STARTUP \
511 if (!(freq = have_win32counter ())) g_error ("Compiled with Win32 counter support, but none found");
512 #define MONO_TIMER_TYPE MonoWin32Timer
513 #define MONO_TIMER_INIT(t)
514 #define MONO_TIMER_DESTROY(t)
515 #define MONO_TIMER_START(t) QueryPerformanceCounter (&(t).start)
516 #define MONO_TIMER_STOP(t) QueryPerformanceCounter (&(t).stop)
517 #define MONO_TIMER_ELAPSED(t) win32_elapsed (&(t))
522 GTimeVal start, stop;
526 timeval_elapsed (MonoGLibTimer *t)
528 if (t->start.tv_usec > t->stop.tv_usec) {
529 t->stop.tv_usec += G_USEC_PER_SEC;
532 return (t->stop.tv_sec - t->start.tv_sec)
533 + ((double)(t->stop.tv_usec - t->start.tv_usec))/ G_USEC_PER_SEC;
536 #define MONO_TIMER_STARTUP
537 #define MONO_TIMER_TYPE MonoGLibTimer
538 #define MONO_TIMER_INIT(t)
539 #define MONO_TIMER_DESTROY(t)
540 #define MONO_TIMER_START(t) g_get_current_time (&(t).start)
541 #define MONO_TIMER_STOP(t) g_get_current_time (&(t).stop)
542 #define MONO_TIMER_ELAPSED(t) timeval_elapsed (&(t))
545 typedef struct _AllocInfo AllocInfo;
546 typedef struct _CallerInfo CallerInfo;
547 typedef struct _LastCallerInfo LastCallerInfo;
549 struct _MonoProfiler {
551 MonoMemPool *mempool;
552 /* info about JIT time */
553 MONO_TIMER_TYPE jit_timer;
556 MonoMethod *max_jit_method;
559 /* tls_id is the id for the TLS slot where MonoProfiler is stored */
563 /* chain of callers for the current thread */
564 LastCallerInfo *callers;
565 /* LastCallerInfo nodes for faster allocation */
566 LastCallerInfo *cstorage;
573 AllocInfo *alloc_info;
574 CallerInfo *caller_info;
577 typedef struct _MethodCallProfile MethodCallProfile;
579 struct _MethodCallProfile {
580 MethodCallProfile *next;
581 MONO_TIMER_TYPE timer;
598 struct _LastCallerInfo {
599 LastCallerInfo *next;
601 MONO_TIMER_TYPE timer;
605 create_profiler (void)
607 MonoProfiler *prof = g_new0 (MonoProfiler, 1);
609 prof->methods = g_hash_table_new (NULL, NULL);
610 MONO_TIMER_INIT (prof->jit_timer);
611 prof->mempool = mono_mempool_new ();
615 #define GET_THREAD_PROF(prof) do {\
616 MonoProfiler *_tprofiler = TlsGetValue ((prof)->tls_id); \
618 _tprofiler = create_profiler (); \
619 prof->per_thread = g_slist_prepend (prof->per_thread, _tprofiler); \
620 TlsSetValue ((prof)->tls_id, _tprofiler); \
625 /* thread unsafe but faster variant */
626 #define GET_THREAD_PROF(prof)
630 compare_profile (MethodProfile *profa, MethodProfile *profb)
632 return (gint)((profb->total - profa->total)*1000);
636 build_profile (MonoMethod *m, MethodProfile *prof, GList **funcs)
639 *funcs = g_list_insert_sorted (*funcs, prof, (GCompareFunc)compare_profile);
643 method_get_name (MonoMethod* method)
647 sig = mono_signature_get_desc (method->signature, FALSE);
648 res = g_strdup_printf ("%s.%s::%s(%s)", method->klass->name_space, method->klass->name,
654 static void output_callers (MethodProfile *p);
657 output_profile (GList *funcs)
662 guint64 total_calls = 0;
665 g_print ("Time(ms) Count P/call(ms) Method name\n");
666 for (tmp = funcs; tmp; tmp = tmp->next) {
668 total_calls += p->count;
669 if (!(gint)(p->total*1000))
671 m = method_get_name (p->method);
672 printf ("########################\n");
673 printf ("% 8.3f ", (double) (p->total * 1000));
674 printf ("%7llu ", p->count);
675 printf ("% 8.3f ", (double) (p->total * 1000)/(double)p->count);
682 printf ("Total number of calls: %lld\n", total_calls);
691 compare_newobj_profile (NewobjProfile *profa, NewobjProfile *profb)
693 return (gint)profb->count - (gint)profa->count;
697 build_newobj_profile (MonoClass *class, MethodProfile *mprof, GList **funcs)
699 NewobjProfile *prof = g_new (NewobjProfile, 1);
704 /* we use the total amount of memory to sort */
705 for (tmp = mprof->alloc_info; tmp; tmp = tmp->next)
708 *funcs = g_list_insert_sorted (*funcs, prof, (GCompareFunc)compare_newobj_profile);
712 compare_caller (CallerInfo *a, CallerInfo *b)
714 return b->count - a->count;
718 compare_alloc (AllocInfo *a, AllocInfo *b)
720 return b->mem - a->mem;
724 sort_alloc_list (AllocInfo *ai)
728 for (tmp = ai; tmp; tmp = tmp->next) {
729 l = g_slist_insert_sorted (l, tmp, (GCompareFunc)compare_alloc);
735 sort_caller_list (CallerInfo *ai)
739 for (tmp = ai; tmp; tmp = tmp->next) {
740 l = g_slist_insert_sorted (l, tmp, (GCompareFunc)compare_caller);
746 output_callers (MethodProfile *p) {
747 guint total_callers, percent;
748 GSList *sorted, *tmps;
752 g_print (" Callers (with count) that contribute at least for 1%%:\n");
754 for (cinfo = p->caller_info; cinfo; cinfo = cinfo->next) {
755 total_callers += cinfo->count;
757 sorted = sort_caller_list (p->caller_info);
758 for (tmps = sorted; tmps; tmps = tmps->next) {
760 percent = (cinfo->count * 100)/total_callers;
763 m = method_get_name (cinfo->caller);
764 g_print (" %8d % 3d %% %s\n", cinfo->count, percent, m);
770 output_newobj_profile (GList *proflist)
781 GSList *sorted, *tmps;
783 g_print ("\nAllocation profiler\n");
786 g_print ("%-9s %s\n", "Total mem", "Method");
787 for (tmp = proflist; tmp; tmp = tmp->next) {
790 if (p->count < 50000)
793 m = method_get_name (mp->method);
794 g_print ("########################\n%8d KB %s\n", p->count / 1024, m);
796 sorted = sort_alloc_list (mp->alloc_info);
797 for (tmps = sorted; tmps; tmps = tmps->next) {
799 if (ainfo->mem < 50000)
801 klass = ainfo->klass;
804 klass = klass->element_class;
808 g_snprintf (buf, sizeof (buf), "%s.%s%s",
809 klass->name_space, klass->name, isarray);
810 g_print (" %8d KB %8d %-48s\n", ainfo->mem / 1024, ainfo->count, buf);
815 g_print ("Total memory allocated: %d KB\n", total / 1024);
819 merge_methods (MonoMethod *method, MethodProfile *profile, MonoProfiler *prof)
821 MethodProfile *mprof;
822 AllocInfo *talloc_info, *alloc_info;
823 CallerInfo *tcaller_info, *caller_info;
825 mprof = g_hash_table_lookup (prof->methods, method);
827 /* the master thread didn't see this method, just transfer the info as is */
828 g_hash_table_insert (prof->methods, method, profile);
831 /* merge the info from profile into mprof */
832 mprof->count += profile->count;
833 mprof->total += profile->total;
834 /* merge alloc info */
835 for (talloc_info = profile->alloc_info; talloc_info; talloc_info = talloc_info->next) {
836 for (alloc_info = mprof->alloc_info; alloc_info; alloc_info = alloc_info->next) {
837 if (alloc_info->klass == talloc_info->klass) {
838 /* mprof already has a record for the klass, merge */
839 alloc_info->count += talloc_info->count;
840 alloc_info->mem += talloc_info->mem;
845 /* mprof didn't have the info, just copy it over */
846 alloc_info = mono_mempool_alloc0 (prof->mempool, sizeof (AllocInfo));
847 *alloc_info = *talloc_info;
848 alloc_info->next = mprof->alloc_info;
849 mprof->alloc_info = alloc_info->next;
852 /* merge callers info */
853 for (tcaller_info = profile->caller_info; tcaller_info; tcaller_info = tcaller_info->next) {
854 for (caller_info = mprof->caller_info; caller_info; caller_info = caller_info->next) {
855 if (caller_info->caller == tcaller_info->caller) {
856 /* mprof already has a record for the caller method, merge */
857 caller_info->count += tcaller_info->count;
862 /* mprof didn't have the info, just copy it over */
863 caller_info = mono_mempool_alloc0 (prof->mempool, sizeof (CallerInfo));
864 *caller_info = *tcaller_info;
865 caller_info->next = mprof->caller_info;
866 mprof->caller_info = caller_info;
872 merge_thread_data (MonoProfiler *master, MonoProfiler *tprof)
874 master->jit_time += tprof->jit_time;
875 master->methods_jitted += tprof->methods_jitted;
876 if (master->max_jit_time < tprof->max_jit_time) {
877 master->max_jit_time = tprof->max_jit_time;
878 master->max_jit_method = tprof->max_jit_method;
881 g_hash_table_foreach (tprof->methods, (GHFunc)merge_methods, master);
885 simple_method_enter (MonoProfiler *prof, MonoMethod *method)
887 MethodProfile *profile_info;
888 LastCallerInfo *callinfo;
889 GET_THREAD_PROF (prof);
890 /*g_print ("enter %p %s::%s in %d (%p)\n", method, method->klass->name, method->name, GetCurrentThreadId (), prof);*/
891 if (!(profile_info = g_hash_table_lookup (prof->methods, method))) {
892 profile_info = mono_mempool_alloc0 (prof->mempool, sizeof (MethodProfile));
893 MONO_TIMER_INIT (profile_info->u.timer);
894 g_hash_table_insert (prof->methods, method, profile_info);
896 profile_info->count++;
899 MonoMethod *caller = prof->callers->method;
900 for (cinfo = profile_info->caller_info; cinfo; cinfo = cinfo->next) {
901 if (cinfo->caller == caller)
905 cinfo = mono_mempool_alloc0 (prof->mempool, sizeof (CallerInfo));
906 cinfo->caller = caller;
907 cinfo->next = profile_info->caller_info;
908 profile_info->caller_info = cinfo;
912 if (!(callinfo = prof->cstorage)) {
913 callinfo = mono_mempool_alloc (prof->mempool, sizeof (LastCallerInfo));
914 MONO_TIMER_INIT (callinfo->timer);
916 prof->cstorage = prof->cstorage->next;
918 callinfo->method = method;
919 callinfo->next = prof->callers;
920 prof->callers = callinfo;
921 MONO_TIMER_START (callinfo->timer);
925 simple_method_leave (MonoProfiler *prof, MonoMethod *method)
927 MethodProfile *profile_info;
928 LastCallerInfo *callinfo, *newcallinfo = NULL;
930 GET_THREAD_PROF (prof);
931 /*g_print ("leave %p %s::%s in %d (%p)\n", method, method->klass->name, method->name, GetCurrentThreadId (), prof);*/
932 callinfo = prof->callers;
933 /* should really not happen, but we don't catch exceptions events, yet ... */
935 MONO_TIMER_STOP (callinfo->timer);
936 profile_info = g_hash_table_lookup (prof->methods, callinfo->method);
938 profile_info->total += MONO_TIMER_ELAPSED (callinfo->timer);
939 newcallinfo = callinfo->next;
940 callinfo->next = prof->cstorage;
941 prof->cstorage = callinfo;
942 if (callinfo->method == method)
944 callinfo = newcallinfo;
946 prof->callers = newcallinfo;
950 simple_allocation (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
952 MethodProfile *profile_info;
955 GET_THREAD_PROF (prof);
957 MonoMethod *caller = prof->callers->method;
959 /* Otherwise all allocations are attributed to icall_wrapper_mono_object_new */
960 if (caller->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)
961 caller = prof->callers->next->method;
963 if (!(profile_info = g_hash_table_lookup (prof->methods, caller)))
964 g_assert_not_reached ();
966 return; /* fine for now */
969 for (tmp = profile_info->alloc_info; tmp; tmp = tmp->next) {
970 if (tmp->klass == klass)
974 tmp = mono_mempool_alloc0 (prof->mempool, sizeof (AllocInfo));
976 tmp->next = profile_info->alloc_info;
977 profile_info->alloc_info = tmp;
980 if (klass == mono_defaults.string_class) {
981 tmp->mem += sizeof (MonoString) + 2 * mono_string_length ((MonoString*)obj) + 2;
982 } else if (klass->parent == mono_defaults.array_class) {
983 tmp->mem += sizeof (MonoArray) + mono_array_element_size (klass) * mono_array_length ((MonoArray*)obj);
985 tmp->mem += mono_class_instance_size (klass);
990 simple_method_jit (MonoProfiler *prof, MonoMethod *method)
992 GET_THREAD_PROF (prof);
993 prof->methods_jitted++;
994 MONO_TIMER_START (prof->jit_timer);
998 simple_method_end_jit (MonoProfiler *prof, MonoMethod *method, int result)
1001 GET_THREAD_PROF (prof);
1002 MONO_TIMER_STOP (prof->jit_timer);
1003 jtime = MONO_TIMER_ELAPSED (prof->jit_timer);
1004 prof->jit_time += jtime;
1005 if (jtime > prof->max_jit_time) {
1006 prof->max_jit_time = jtime;
1007 prof->max_jit_method = method;
1012 simple_shutdown (MonoProfiler *prof)
1014 GList *profile = NULL;
1015 MonoProfiler *tprof;
1019 for (tmp = prof->per_thread; tmp; tmp = tmp->next) {
1021 merge_thread_data (prof, tprof);
1024 printf("Total time spent compiling %d methods (sec): %.4g\n", prof->methods_jitted, prof->jit_time);
1025 if (prof->max_jit_method) {
1026 str = method_get_name (prof->max_jit_method);
1027 printf("Slowest method to compile (sec): %.4g: %s\n", prof->max_jit_time, str);
1030 g_hash_table_foreach (prof->methods, (GHFunc)build_profile, &profile);
1031 output_profile (profile);
1032 g_list_free (profile);
1035 g_hash_table_foreach (prof->methods, (GHFunc)build_newobj_profile, &profile);
1036 output_newobj_profile (profile);
1037 g_list_free (profile);
1041 mono_profiler_install_simple (const char *desc)
1044 gchar **args, **ptr;
1045 MonoProfileFlags flags = MONO_PROFILE_ENTER_LEAVE|MONO_PROFILE_JIT_COMPILATION|MONO_PROFILE_ALLOCATIONS;
1051 if (strstr (desc, ":"))
1052 desc = strstr (desc, ":") + 1;
1055 args = g_strsplit (desc ? desc : "", ",", -1);
1057 for (ptr = args; ptr && *ptr; ptr++) {
1058 const char *arg = *ptr;
1060 if (!strcmp (arg, "-time"))
1061 flags &= ~MONO_PROFILE_ENTER_LEAVE;
1063 if (!strcmp (arg, "-alloc"))
1064 flags &= ~MONO_PROFILE_ALLOCATIONS;
1066 fprintf (stderr, "profiler : Unknown argument '%s'.\n", arg);
1072 prof = create_profiler ();
1073 prof->tls_id = TlsAlloc ();
1074 TlsSetValue (prof->tls_id, prof);
1076 mono_profiler_install (prof, simple_shutdown);
1077 /* later do also object creation */
1078 mono_profiler_install_enter_leave (simple_method_enter, simple_method_leave);
1079 mono_profiler_install_jit_compile (simple_method_jit, simple_method_end_jit);
1080 mono_profiler_install_allocation (simple_allocation);
1081 mono_profiler_set_events (flags);
1084 typedef void (*ProfilerInitializer) (const char*);
1085 #define INITIALIZER_NAME "mono_profiler_startup"
1088 mono_profiler_load (const char *desc)
1090 if (!desc || (strcmp ("default", desc) == 0) || (strncmp (desc, "default:", 8) == 0)) {
1091 mono_profiler_install_simple (desc);
1094 const char* col = strchr (desc, ':');
1099 mname = g_memdup (desc, col - desc);
1100 mname [col - desc] = 0;
1102 mname = g_strdup (desc);
1104 libname = g_strdup_printf ("mono-profiler-%s", mname);
1105 path = g_module_build_path (NULL, libname);
1106 pmodule = g_module_open (path, G_MODULE_BIND_LAZY);
1108 ProfilerInitializer func;
1109 if (!g_module_symbol (pmodule, INITIALIZER_NAME, (gpointer *)&func)) {
1110 g_warning ("Cannot find initializer function %s in profiler module: %s", INITIALIZER_NAME, libname);
1115 g_warning ("Error loading profiler module '%s': %s", libname, g_module_error ());