2004-11-07 Ben Maurer <bmaurer@ximian.com>
[mono-project.git] / mono / metadata / profiler.c
blob55f2377d414ef33dfd57a3c69c35883609be7b48
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"
7 #include <string.h>
8 #include <gmodule.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;
51 void
52 mono_profiler_install (MonoProfiler *prof, MonoProfileFunc callback)
54 if (current_profiler)
55 g_error ("profiler already setup");
56 current_profiler = prof;
57 shutdown_callback = callback;
58 InitializeCriticalSection (&profiler_coverage_mutex);
61 void
62 mono_profiler_set_events (MonoProfileFlags events)
64 mono_profiler_events = events;
67 MonoProfileFlags
68 mono_profiler_get_events (void)
70 return mono_profiler_events;
73 void
74 mono_profiler_install_enter_leave (MonoProfileMethodFunc enter, MonoProfileMethodFunc fleave)
76 method_enter = enter;
77 method_leave = fleave;
80 void
81 mono_profiler_install_jit_compile (MonoProfileMethodFunc start, MonoProfileMethodResult end)
83 jit_start = start;
84 jit_end = end;
87 void
88 mono_profiler_install_thread (MonoProfileThreadFunc start, MonoProfileThreadFunc end)
90 thread_start = start;
91 thread_end = end;
94 void
95 mono_profiler_install_transition (MonoProfileMethodResult callback)
97 man_unman_transition = callback;
100 void
101 mono_profiler_install_allocation (MonoProfileAllocFunc callback)
103 allocation_cb = callback;
106 void
107 mono_profiler_install_coverage_filter (MonoProfileCoverageFilterFunc callback)
109 coverage_filter_cb = callback;
112 void
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;
123 void
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;
133 void
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;
143 void
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;
153 void
154 mono_profiler_method_enter (MonoMethod *method)
156 if ((mono_profiler_events & MONO_PROFILE_ENTER_LEAVE) && method_enter)
157 method_enter (current_profiler, method);
160 void
161 mono_profiler_method_leave (MonoMethod *method)
163 if ((mono_profiler_events & MONO_PROFILE_ENTER_LEAVE) && method_leave)
164 method_leave (current_profiler, method);
167 void
168 mono_profiler_method_jit (MonoMethod *method)
170 if ((mono_profiler_events & MONO_PROFILE_JIT_COMPILATION) && jit_start)
171 jit_start (current_profiler, method);
174 void
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);
181 void
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);
188 void
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);
195 void
196 mono_profiler_thread_start (guint32 tid)
198 if ((mono_profiler_events & MONO_PROFILE_THREADS) && thread_start)
199 thread_start (current_profiler, tid);
202 void
203 mono_profiler_thread_end (guint32 tid)
205 if ((mono_profiler_events & MONO_PROFILE_THREADS) && thread_end)
206 thread_end (current_profiler, tid);
209 void
210 mono_profiler_assembly_event (MonoAssembly *assembly, int code)
212 if (!(mono_profiler_events & MONO_PROFILE_ASSEMBLY_EVENTS))
213 return;
215 switch (code) {
216 case MONO_PROFILE_START_LOAD:
217 if (assembly_start_load)
218 assembly_start_load (current_profiler, assembly);
219 break;
220 case MONO_PROFILE_START_UNLOAD:
221 if (assembly_start_unload)
222 assembly_start_unload (current_profiler, assembly);
223 break;
224 case MONO_PROFILE_END_UNLOAD:
225 if (assembly_end_unload)
226 assembly_end_unload (current_profiler, assembly);
227 break;
228 default:
229 g_assert_not_reached ();
233 void
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);
240 void
241 mono_profiler_module_event (MonoImage *module, int code)
243 if (!(mono_profiler_events & MONO_PROFILE_MODULE_EVENTS))
244 return;
246 switch (code) {
247 case MONO_PROFILE_START_LOAD:
248 if (module_start_load)
249 module_start_load (current_profiler, module);
250 break;
251 case MONO_PROFILE_START_UNLOAD:
252 if (module_start_unload)
253 module_start_unload (current_profiler, module);
254 break;
255 case MONO_PROFILE_END_UNLOAD:
256 if (module_end_unload)
257 module_end_unload (current_profiler, module);
258 break;
259 default:
260 g_assert_not_reached ();
264 void
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);
271 void
272 mono_profiler_class_event (MonoClass *klass, int code)
274 if (!(mono_profiler_events & MONO_PROFILE_CLASS_EVENTS))
275 return;
277 switch (code) {
278 case MONO_PROFILE_START_LOAD:
279 if (class_start_load)
280 class_start_load (current_profiler, klass);
281 break;
282 case MONO_PROFILE_START_UNLOAD:
283 if (class_start_unload)
284 class_start_unload (current_profiler, klass);
285 break;
286 case MONO_PROFILE_END_UNLOAD:
287 if (class_end_unload)
288 class_end_unload (current_profiler, klass);
289 break;
290 default:
291 g_assert_not_reached ();
295 void
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);
302 void
303 mono_profiler_appdomain_event (MonoDomain *domain, int code)
305 if (!(mono_profiler_events & MONO_PROFILE_APPDOMAIN_EVENTS))
306 return;
308 switch (code) {
309 case MONO_PROFILE_START_LOAD:
310 if (domain_start_load)
311 domain_start_load (current_profiler, domain);
312 break;
313 case MONO_PROFILE_START_UNLOAD:
314 if (domain_start_unload)
315 domain_start_unload (current_profiler, domain);
316 break;
317 case MONO_PROFILE_END_UNLOAD:
318 if (domain_end_unload)
319 domain_end_unload (current_profiler, domain);
320 break;
321 default:
322 g_assert_not_reached ();
326 void
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);
333 void
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))
349 return NULL;
351 EnterCriticalSection (&profiler_coverage_mutex);
352 if (!coverage_hash)
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);
362 return res;
365 /* safe only when the method antive code has been unloaded */
366 void
367 mono_profiler_coverage_free (MonoMethod *method)
369 MonoProfileCoverageInfo* info;
371 EnterCriticalSection (&profiler_coverage_mutex);
372 if (!coverage_hash) {
373 LeaveCriticalSection (&profiler_coverage_mutex);
374 return;
377 info = g_hash_table_lookup (coverage_hash, method);
378 if (info) {
379 g_free (info);
380 g_hash_table_remove (coverage_hash, method);
382 LeaveCriticalSection (&profiler_coverage_mutex);
385 void
386 mono_profiler_coverage_get (MonoProfiler *prof, MonoMethod *method, MonoProfileCoverageFunc func)
388 MonoProfileCoverageInfo* info;
389 int i, offset;
390 guint32 line, col;
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);
399 if (!info)
400 return;
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 */
413 col = line = 1;
414 entry.filename = mono_debug_source_location_from_il_offset (method, offset, &line);
415 entry.line = line;
416 entry.col = col;
417 func (prof, &entry);
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.
427 #define USE_X86TSC 0
428 #define USE_WIN32COUNTER 0
429 #if USE_X86TSC
431 typedef struct {
432 unsigned int lows, highs, lowe, highe;
433 } MonoRdtscTimer;
435 #define rdtsc(low,high) \
436 __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
438 static int freq;
440 static double
441 rdtsc_elapsed (MonoRdtscTimer *t)
443 unsigned long long diff;
444 unsigned int highe = t->highe;
445 if (t->lowe < t->lows)
446 highe--;
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 */
451 static int
452 have_rdtsc (void) {
453 char buf[256];
454 int have_freq = 0;
455 int have_flag = 0;
456 float val;
457 FILE *cpuinfo;
459 if (!(cpuinfo = fopen ("/proc/cpuinfo", "r")))
460 return 0;
461 while (fgets (buf, sizeof(buf), cpuinfo)) {
462 if (sscanf (buf, "cpu MHz : %f", &val) == 1) {
463 /*printf ("got mh: %f\n", val);*/
464 have_freq = val;
466 if (strncmp (buf, "flags", 5) == 0) {
467 if (strstr (buf, "tsc")) {
468 have_flag = 1;
469 /*printf ("have tsc\n");*/
473 fclose (cpuinfo);
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
487 #include <windows.h>
489 typedef struct {
490 LARGE_INTEGER start, stop;
491 } MonoWin32Timer;
493 static int freq;
495 static double
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 */
502 static int
503 have_win32counter (void) {
504 LARGE_INTEGER f;
506 if (!QueryPerformanceFrequency (&f))
507 return 0;
508 return f.LowPart;
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))
520 #else
522 typedef struct {
523 GTimeVal start, stop;
524 } MonoGLibTimer;
526 static double
527 timeval_elapsed (MonoGLibTimer *t)
529 if (t->start.tv_usec > t->stop.tv_usec) {
530 t->stop.tv_usec += G_USEC_PER_SEC;
531 t->stop.tv_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))
544 #endif
546 typedef struct _AllocInfo AllocInfo;
547 typedef struct _CallerInfo CallerInfo;
548 typedef struct _LastCallerInfo LastCallerInfo;
550 struct _MonoProfiler {
551 GHashTable *methods;
552 MonoMemPool *mempool;
553 /* info about JIT time */
554 MONO_TIMER_TYPE jit_timer;
555 double jit_time;
556 double max_jit_time;
557 MonoMethod *max_jit_method;
558 int methods_jitted;
560 GSList *per_thread;
562 /* chain of callers for the current thread */
563 LastCallerInfo *callers;
564 /* LastCallerInfo nodes for faster allocation */
565 LastCallerInfo *cstorage;
568 typedef struct {
569 MonoMethod *method;
570 guint64 count;
571 double total;
572 AllocInfo *alloc_info;
573 CallerInfo *caller_info;
574 } MethodProfile;
576 typedef struct _MethodCallProfile MethodCallProfile;
578 struct _MethodCallProfile {
579 MethodCallProfile *next;
580 MONO_TIMER_TYPE timer;
581 MonoMethod *method;
584 struct _AllocInfo {
585 AllocInfo *next;
586 MonoClass *klass;
587 guint count;
588 guint mem;
591 struct _CallerInfo {
592 CallerInfo *next;
593 MonoMethod *caller;
594 guint count;
597 struct _LastCallerInfo {
598 LastCallerInfo *next;
599 MonoMethod *method;
600 MONO_TIMER_TYPE timer;
603 static MonoProfiler*
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 ();
611 return prof;
613 #if 1
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 */
620 #else
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 ()
625 #endif
627 #define GET_THREAD_PROF(prof) do { \
628 MonoProfiler *_tprofiler = GET_PROFILER (); \
629 if (!_tprofiler) { \
630 _tprofiler = create_profiler (); \
631 prof->per_thread = g_slist_prepend (prof->per_thread, _tprofiler); \
632 SET_PROFILER (_tprofiler); \
634 prof = _tprofiler; \
635 } while (0)
636 #else
637 /* thread unsafe but faster variant */
638 #define GET_THREAD_PROF(prof)
639 #endif
641 static gint
642 compare_profile (MethodProfile *profa, MethodProfile *profb)
644 return (gint)((profb->total - profa->total)*1000);
647 static void
648 build_profile (MonoMethod *m, MethodProfile *prof, GList **funcs)
650 prof->method = m;
651 *funcs = g_list_insert_sorted (*funcs, prof, (GCompareFunc)compare_profile);
654 static char*
655 method_get_name (MonoMethod* method)
657 char *sig, *res;
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,
661 method->name, sig);
662 g_free (sig);
663 return res;
666 static void output_callers (MethodProfile *p);
668 static void
669 output_profile (GList *funcs)
671 GList *tmp;
672 MethodProfile *p;
673 char *m;
674 guint64 total_calls = 0;
676 if (funcs)
677 g_print ("Time(ms) Count P/call(ms) Method name\n");
678 for (tmp = funcs; tmp; tmp = tmp->next) {
679 p = tmp->data;
680 total_calls += p->count;
681 if (!(gint)(p->total*1000))
682 continue;
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);
688 printf (" %s\n", m);
690 g_free (m);
691 /* callers */
692 output_callers (p);
694 printf ("Total number of calls: %lld\n", total_calls);
697 typedef struct {
698 MethodProfile *mp;
699 guint count;
700 } NewobjProfile;
702 static gint
703 compare_newobj_profile (NewobjProfile *profa, NewobjProfile *profb)
705 return (gint)profb->count - (gint)profa->count;
708 static void
709 build_newobj_profile (MonoClass *class, MethodProfile *mprof, GList **funcs)
711 NewobjProfile *prof = g_new (NewobjProfile, 1);
712 AllocInfo *tmp;
713 guint count = 0;
715 prof->mp = mprof;
716 /* we use the total amount of memory to sort */
717 for (tmp = mprof->alloc_info; tmp; tmp = tmp->next)
718 count += tmp->mem;
719 prof->count = count;
720 *funcs = g_list_insert_sorted (*funcs, prof, (GCompareFunc)compare_newobj_profile);
723 static int
724 compare_caller (CallerInfo *a, CallerInfo *b)
726 return b->count - a->count;
729 static int
730 compare_alloc (AllocInfo *a, AllocInfo *b)
732 return b->mem - a->mem;
735 static GSList*
736 sort_alloc_list (AllocInfo *ai)
738 GSList *l = NULL;
739 AllocInfo *tmp;
740 for (tmp = ai; tmp; tmp = tmp->next) {
741 l = g_slist_insert_sorted (l, tmp, (GCompareFunc)compare_alloc);
743 return l;
746 static GSList*
747 sort_caller_list (CallerInfo *ai)
749 GSList *l = NULL;
750 CallerInfo *tmp;
751 for (tmp = ai; tmp; tmp = tmp->next) {
752 l = g_slist_insert_sorted (l, tmp, (GCompareFunc)compare_caller);
754 return l;
757 static void
758 output_callers (MethodProfile *p) {
759 guint total_callers, percent;
760 GSList *sorted, *tmps;
761 CallerInfo *cinfo;
762 char *m;
764 g_print (" Callers (with count) that contribute at least for 1%%:\n");
765 total_callers = 0;
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) {
771 cinfo = tmps->data;
772 percent = (cinfo->count * 100)/total_callers;
773 if (percent < 1)
774 continue;
775 m = method_get_name (cinfo->caller);
776 g_print (" %8d % 3d %% %s\n", cinfo->count, percent, m);
777 g_free (m);
781 static void
782 output_newobj_profile (GList *proflist)
784 GList *tmp;
785 NewobjProfile *p;
786 MethodProfile *mp;
787 AllocInfo *ainfo;
788 MonoClass *klass;
789 const char* isarray;
790 char buf [256];
791 char *m;
792 guint total = 0;
793 GSList *sorted, *tmps;
795 g_print ("\nAllocation profiler\n");
797 if (proflist)
798 g_print ("%-9s %s\n", "Total mem", "Method");
799 for (tmp = proflist; tmp; tmp = tmp->next) {
800 p = tmp->data;
801 total += p->count;
802 if (p->count < 50000)
803 continue;
804 mp = p->mp;
805 m = method_get_name (mp->method);
806 g_print ("########################\n%8d KB %s\n", p->count / 1024, m);
807 g_free (m);
808 sorted = sort_alloc_list (mp->alloc_info);
809 for (tmps = sorted; tmps; tmps = tmps->next) {
810 ainfo = tmps->data;
811 if (ainfo->mem < 50000)
812 continue;
813 klass = ainfo->klass;
814 if (klass->rank) {
815 isarray = "[]";
816 klass = klass->element_class;
817 } else {
818 isarray = "";
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);
824 /* callers */
825 output_callers (mp);
827 g_print ("Total memory allocated: %d KB\n", total / 1024);
830 static void
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);
838 if (!mprof) {
839 /* the master thread didn't see this method, just transfer the info as is */
840 g_hash_table_insert (prof->methods, method, profile);
841 return;
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;
853 break;
856 if (!alloc_info) {
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;
870 break;
873 if (!caller_info) {
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;
883 static void
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);
896 static void
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++;
909 if (prof->callers) {
910 CallerInfo *cinfo;
911 MonoMethod *caller = prof->callers->method;
912 for (cinfo = profile_info->caller_info; cinfo; cinfo = cinfo->next) {
913 if (cinfo->caller == caller)
914 break;
916 if (!cinfo) {
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;
922 cinfo->count++;
924 if (!(callinfo = prof->cstorage)) {
925 callinfo = mono_mempool_alloc (prof->mempool, sizeof (LastCallerInfo));
926 MONO_TIMER_INIT (callinfo->timer);
927 } else {
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);
936 static void
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 ... */
946 while (callinfo) {
947 MONO_TIMER_STOP (callinfo->timer);
948 profile_info = g_hash_table_lookup (prof->methods, callinfo->method);
949 if (profile_info)
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)
955 break;
956 callinfo = newcallinfo;
958 prof->callers = newcallinfo;
961 static void
962 simple_allocation (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
964 MethodProfile *profile_info;
965 AllocInfo *tmp;
967 GET_THREAD_PROF (prof);
968 if (prof->callers) {
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 ();
977 } else {
978 return; /* fine for now */
981 for (tmp = profile_info->alloc_info; tmp; tmp = tmp->next) {
982 if (tmp->klass == klass)
983 break;
985 if (!tmp) {
986 tmp = mono_mempool_alloc0 (prof->mempool, sizeof (AllocInfo));
987 tmp->klass = klass;
988 tmp->next = profile_info->alloc_info;
989 profile_info->alloc_info = tmp;
991 tmp->count++;
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);
996 } else {
997 tmp->mem += mono_class_instance_size (klass);
1001 static void
1002 simple_method_jit (MonoProfiler *prof, MonoMethod *method)
1004 GET_THREAD_PROF (prof);
1005 prof->methods_jitted++;
1006 MONO_TIMER_START (prof->jit_timer);
1009 static void
1010 simple_method_end_jit (MonoProfiler *prof, MonoMethod *method, int result)
1012 double jtime;
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;
1023 static void
1024 simple_shutdown (MonoProfiler *prof)
1026 GList *profile = NULL;
1027 MonoProfiler *tprof;
1028 GSList *tmp;
1029 char *str;
1031 for (tmp = prof->per_thread; tmp; tmp = tmp->next) {
1032 tprof = tmp->data;
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);
1040 g_free (str);
1042 g_hash_table_foreach (prof->methods, (GHFunc)build_profile, &profile);
1043 output_profile (profile);
1044 g_list_free (profile);
1045 profile = NULL;
1047 g_hash_table_foreach (prof->methods, (GHFunc)build_newobj_profile, &profile);
1048 output_newobj_profile (profile);
1049 g_list_free (profile);
1052 static void
1053 mono_profiler_install_simple (const char *desc)
1055 MonoProfiler *prof;
1056 gchar **args, **ptr;
1057 MonoProfileFlags flags = MONO_PROFILE_ENTER_LEAVE|MONO_PROFILE_JIT_COMPILATION|MONO_PROFILE_ALLOCATIONS;
1059 MONO_TIMER_STARTUP;
1061 if (desc) {
1062 /* Parse options */
1063 if (strstr (desc, ":"))
1064 desc = strstr (desc, ":") + 1;
1065 else
1066 desc = NULL;
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;
1074 else
1075 if (!strcmp (arg, "-alloc"))
1076 flags &= ~MONO_PROFILE_ALLOCATIONS;
1077 else {
1078 fprintf (stderr, "profiler : Unknown argument '%s'.\n", arg);
1079 return;
1084 prof = create_profiler ();
1085 ALLOC_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"
1099 void
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);
1104 } else {
1105 GModule *pmodule;
1106 const char* col = strchr (desc, ':');
1107 char* libname;
1108 char* path;
1109 char *mname;
1110 if (col != NULL) {
1111 mname = g_memdup (desc, col - desc);
1112 mname [col - desc] = 0;
1113 } else {
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);
1119 if (pmodule) {
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);
1123 } else {
1124 func (desc);
1126 } else {
1127 g_warning ("Error loading profiler module '%s': %s", libname, g_module_error ());
1130 g_free (libname);
1131 g_free (mname);
1132 g_free (path);