2003-11-24 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / metadata / profiler.c
1
2 #include "mono/metadata/profiler-private.h"
3 #include "mono/metadata/debug-helpers.h"
4 #include "mono/metadata/mono-debug.h"
5 #include <string.h>
6 #include <gmodule.h>
7
8 static MonoProfiler * current_profiler = NULL;
9
10 static MonoProfileAppDomainFunc   domain_start_load;
11 static MonoProfileAppDomainResult domain_end_load;
12 static MonoProfileAppDomainFunc   domain_start_unload;
13 static MonoProfileAppDomainFunc   domain_end_unload;
14
15 static MonoProfileAssemblyFunc   assembly_start_load;
16 static MonoProfileAssemblyResult assembly_end_load;
17 static MonoProfileAssemblyFunc   assembly_start_unload;
18 static MonoProfileAssemblyFunc   assembly_end_unload;
19
20 static MonoProfileModuleFunc   module_start_load;
21 static MonoProfileModuleResult module_end_load;
22 static MonoProfileModuleFunc   module_start_unload;
23 static MonoProfileModuleFunc   module_end_unload;
24
25 static MonoProfileClassFunc   class_start_load;
26 static MonoProfileClassResult class_end_load;
27 static MonoProfileClassFunc   class_start_unload;
28 static MonoProfileClassFunc   class_end_unload;
29
30 static MonoProfileMethodFunc   jit_start;
31 static MonoProfileMethodResult jit_end;
32 static MonoProfileMethodResult man_unman_transition;
33 static MonoProfileAllocFunc    allocation_cb;
34 static MonoProfileMethodFunc   method_enter;
35 static MonoProfileMethodFunc   method_leave;
36
37 static MonoProfileThreadFunc   thread_start;
38 static MonoProfileThreadFunc   thread_end;
39
40 static MonoProfileCoverageFilterFunc coverage_filter_cb;
41
42 static MonoProfileFunc shutdown_callback;
43
44 /* this is directly accessible to other mono libs. */
45 MonoProfileFlags mono_profiler_events;
46
47 void
48 mono_profiler_install (MonoProfiler *prof, MonoProfileFunc callback)
49 {
50         if (current_profiler)
51                 g_error ("profiler already setup");
52         current_profiler = prof;
53         shutdown_callback = callback;
54 }
55
56 void
57 mono_profiler_set_events (MonoProfileFlags events)
58 {
59         mono_profiler_events = events;
60 }
61
62 MonoProfileFlags
63 mono_profiler_get_events (void)
64 {
65         return mono_profiler_events;
66 }
67
68 void
69 mono_profiler_install_enter_leave (MonoProfileMethodFunc enter, MonoProfileMethodFunc fleave)
70 {
71         method_enter = enter;
72         method_leave = fleave;
73 }
74
75 void 
76 mono_profiler_install_jit_compile (MonoProfileMethodFunc start, MonoProfileMethodResult end)
77 {
78         jit_start = start;
79         jit_end = end;
80 }
81
82 void 
83 mono_profiler_install_thread (MonoProfileThreadFunc start, MonoProfileThreadFunc end)
84 {
85         thread_start = start;
86         thread_end = end;
87 }
88
89 void 
90 mono_profiler_install_transition (MonoProfileMethodResult callback)
91 {
92         man_unman_transition = callback;
93 }
94
95 void 
96 mono_profiler_install_allocation (MonoProfileAllocFunc callback)
97 {
98         allocation_cb = callback;
99 }
100
101 void 
102 mono_profiler_install_coverage_filter (MonoProfileCoverageFilterFunc callback)
103 {
104         coverage_filter_cb = callback;
105 }
106
107 void 
108 mono_profiler_install_appdomain   (MonoProfileAppDomainFunc start_load, MonoProfileAppDomainResult end_load,
109                                    MonoProfileAppDomainFunc start_unload, MonoProfileAppDomainFunc end_unload)
110
111 {
112         domain_start_load = start_load;
113         domain_end_load = end_load;
114         domain_start_unload = start_unload;
115         domain_end_unload = end_unload;
116 }
117
118 void 
119 mono_profiler_install_assembly    (MonoProfileAssemblyFunc start_load, MonoProfileAssemblyResult end_load,
120                                    MonoProfileAssemblyFunc start_unload, MonoProfileAssemblyFunc end_unload)
121 {
122         assembly_start_load = start_load;
123         assembly_end_load = end_load;
124         assembly_start_unload = start_unload;
125         assembly_end_unload = end_unload;
126 }
127
128 void 
129 mono_profiler_install_module      (MonoProfileModuleFunc start_load, MonoProfileModuleResult end_load,
130                                    MonoProfileModuleFunc start_unload, MonoProfileModuleFunc end_unload)
131 {
132         module_start_load = start_load;
133         module_end_load = end_load;
134         module_start_unload = start_unload;
135         module_end_unload = end_unload;
136 }
137
138 void
139 mono_profiler_install_class       (MonoProfileClassFunc start_load, MonoProfileClassResult end_load,
140                                    MonoProfileClassFunc start_unload, MonoProfileClassFunc end_unload)
141 {
142         class_start_load = start_load;
143         class_end_load = end_load;
144         class_start_unload = start_unload;
145         class_end_unload = end_unload;
146 }
147
148 void
149 mono_profiler_method_enter (MonoMethod *method)
150 {
151         if ((mono_profiler_events & MONO_PROFILE_ENTER_LEAVE) && method_enter)
152                 method_enter (current_profiler, method);
153 }
154
155 void
156 mono_profiler_method_leave (MonoMethod *method)
157 {
158         if ((mono_profiler_events & MONO_PROFILE_ENTER_LEAVE) && method_leave)
159                 method_leave (current_profiler, method);
160 }
161
162 void 
163 mono_profiler_method_jit (MonoMethod *method)
164 {
165         if ((mono_profiler_events & MONO_PROFILE_JIT_COMPILATION) && jit_start)
166                 jit_start (current_profiler, method);
167 }
168
169 void 
170 mono_profiler_method_end_jit (MonoMethod *method, int result)
171 {
172         if ((mono_profiler_events & MONO_PROFILE_JIT_COMPILATION) && jit_end)
173                 jit_end (current_profiler, method, result);
174 }
175
176 void 
177 mono_profiler_code_transition (MonoMethod *method, int result)
178 {
179         if ((mono_profiler_events & MONO_PROFILE_TRANSITIONS) && man_unman_transition)
180                 man_unman_transition (current_profiler, method, result);
181 }
182
183 void 
184 mono_profiler_allocation (MonoObject *obj, MonoClass *klass)
185 {
186         if ((mono_profiler_events & MONO_PROFILE_ALLOCATIONS) && allocation_cb)
187                 allocation_cb (current_profiler, obj, klass);
188 }
189
190 void
191 mono_profiler_thread_start (guint32 tid)
192 {
193         if ((mono_profiler_events & MONO_PROFILE_THREADS) && thread_start)
194                 thread_start (current_profiler, tid);
195 }
196
197 void 
198 mono_profiler_thread_end (guint32 tid)
199 {
200         if ((mono_profiler_events & MONO_PROFILE_THREADS) && thread_end)
201                 thread_end (current_profiler, tid);
202 }
203
204 void 
205 mono_profiler_assembly_event  (MonoAssembly *assembly, int code)
206 {
207         if (!(mono_profiler_events & MONO_PROFILE_ASSEMBLY_EVENTS))
208                 return;
209         
210         switch (code) {
211         case MONO_PROFILE_START_LOAD:
212                 if (assembly_start_load)
213                         assembly_start_load (current_profiler, assembly);
214                 break;
215         case MONO_PROFILE_START_UNLOAD:
216                 if (assembly_start_unload)
217                         assembly_start_unload (current_profiler, assembly);
218                 break;
219         case MONO_PROFILE_END_UNLOAD:
220                 if (assembly_end_unload)
221                         assembly_end_unload (current_profiler, assembly);
222                 break;
223         default:
224                 g_assert_not_reached ();
225         }
226 }
227
228 void 
229 mono_profiler_assembly_loaded (MonoAssembly *assembly, int result)
230 {
231         if ((mono_profiler_events & MONO_PROFILE_ASSEMBLY_EVENTS) && assembly_end_load)
232                 assembly_end_load (current_profiler, assembly, result);
233 }
234
235 void 
236 mono_profiler_module_event  (MonoImage *module, int code)
237 {
238         if (!(mono_profiler_events & MONO_PROFILE_MODULE_EVENTS))
239                 return;
240         
241         switch (code) {
242         case MONO_PROFILE_START_LOAD:
243                 if (module_start_load)
244                         module_start_load (current_profiler, module);
245                 break;
246         case MONO_PROFILE_START_UNLOAD:
247                 if (module_start_unload)
248                         module_start_unload (current_profiler, module);
249                 break;
250         case MONO_PROFILE_END_UNLOAD:
251                 if (module_end_unload)
252                         module_end_unload (current_profiler, module);
253                 break;
254         default:
255                 g_assert_not_reached ();
256         }
257 }
258
259 void 
260 mono_profiler_module_loaded (MonoImage *module, int result)
261 {
262         if ((mono_profiler_events & MONO_PROFILE_MODULE_EVENTS) && module_end_load)
263                 module_end_load (current_profiler, module, result);
264 }
265
266 void 
267 mono_profiler_class_event  (MonoClass *klass, int code)
268 {
269         if (!(mono_profiler_events & MONO_PROFILE_CLASS_EVENTS))
270                 return;
271         
272         switch (code) {
273         case MONO_PROFILE_START_LOAD:
274                 if (class_start_load)
275                         class_start_load (current_profiler, klass);
276                 break;
277         case MONO_PROFILE_START_UNLOAD:
278                 if (class_start_unload)
279                         class_start_unload (current_profiler, klass);
280                 break;
281         case MONO_PROFILE_END_UNLOAD:
282                 if (class_end_unload)
283                         class_end_unload (current_profiler, klass);
284                 break;
285         default:
286                 g_assert_not_reached ();
287         }
288 }
289
290 void 
291 mono_profiler_class_loaded (MonoClass *klass, int result)
292 {
293         if ((mono_profiler_events & MONO_PROFILE_CLASS_EVENTS) && class_end_load)
294                 class_end_load (current_profiler, klass, result);
295 }
296
297 void 
298 mono_profiler_appdomain_event  (MonoDomain *domain, int code)
299 {
300         if (!(mono_profiler_events & MONO_PROFILE_APPDOMAIN_EVENTS))
301                 return;
302         
303         switch (code) {
304         case MONO_PROFILE_START_LOAD:
305                 if (domain_start_load)
306                         domain_start_load (current_profiler, domain);
307                 break;
308         case MONO_PROFILE_START_UNLOAD:
309                 if (domain_start_unload)
310                         domain_start_unload (current_profiler, domain);
311                 break;
312         case MONO_PROFILE_END_UNLOAD:
313                 if (domain_end_unload)
314                         domain_end_unload (current_profiler, domain);
315                 break;
316         default:
317                 g_assert_not_reached ();
318         }
319 }
320
321 void 
322 mono_profiler_appdomain_loaded (MonoDomain *domain, int result)
323 {
324         if ((mono_profiler_events & MONO_PROFILE_APPDOMAIN_EVENTS) && domain_end_load)
325                 domain_end_load (current_profiler, domain, result);
326 }
327
328 void 
329 mono_profiler_shutdown (void)
330 {
331         if (current_profiler && shutdown_callback)
332                 shutdown_callback (current_profiler);
333 }
334
335 static GHashTable *coverage_hash = NULL;
336
337 MonoProfileCoverageInfo* 
338 mono_profiler_coverage_alloc (MonoMethod *method, int entries)
339 {
340         MonoProfileCoverageInfo *res;
341
342         if (coverage_filter_cb)
343                 if (! (*coverage_filter_cb) (current_profiler, method))
344                         return NULL;
345
346         if (!coverage_hash)
347                 coverage_hash = g_hash_table_new (NULL, NULL);
348
349         res = g_malloc0 (sizeof (MonoProfileCoverageInfo) + sizeof (void*) * 2 * entries);
350
351         res->entries = entries;
352
353         g_hash_table_insert (coverage_hash, method, res);
354
355         return res;
356 }
357
358 /* safe only when the method antive code has been unloaded */
359 void
360 mono_profiler_coverage_free (MonoMethod *method)
361 {
362         MonoProfileCoverageInfo* info;
363         if (!coverage_hash)
364                 return;
365         info = g_hash_table_lookup (coverage_hash, method);
366         if (!info)
367                 return;
368         g_free (info);
369         g_hash_table_remove (coverage_hash, method);
370 }
371
372 void 
373 mono_profiler_coverage_get (MonoProfiler *prof, MonoMethod *method, MonoProfileCoverageFunc func)
374 {
375         MonoProfileCoverageInfo* info;
376         int i, offset;
377         guint32 line, col;
378         unsigned char *start, *end, *cil_code;
379         MonoMethodHeader *header;
380         MonoProfileCoverageEntry entry;
381
382         info = g_hash_table_lookup (coverage_hash, method);
383         if (!info)
384                 return;
385
386         header = ((MonoMethodNormal *)method)->header;
387         start = (unsigned char*)header->code;
388         end = start + header->code_size;
389         for (i = 0; i < info->entries; ++i) {
390                 cil_code = info->data [i].cil_code;
391                 if (cil_code && cil_code >= start && cil_code < end) {
392                         offset = cil_code - start;
393                         entry.iloffset = offset;
394                         entry.method = method;
395                         entry.counter = info->data [i].count;
396                         /* the debug interface doesn't support column info, sigh */
397                         col = line = 1;
398                         entry.filename = mono_debug_source_location_from_il_offset (method, offset, &line);
399                         entry.line = line;
400                         entry.col = col;
401                         func (prof, &entry);
402                 }
403         }
404 }
405
406 /*
407  * Small profiler extracted from mint: we should move it in a loadable module
408  * and improve it to do graphs and more accurate timestamping with rdtsc.
409  */
410
411 #define USE_X86TSC 0
412 #define USE_WIN32COUNTER 0
413 #if USE_X86TSC
414
415 typedef struct {
416         unsigned int lows, highs, lowe, highe;
417 } MonoRdtscTimer;
418
419 #define rdtsc(low,high) \
420         __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
421
422 static int freq;
423
424 static double
425 rdtsc_elapsed (MonoRdtscTimer *t)
426 {
427         unsigned long long diff;
428         unsigned int highe = t->highe;
429         if (t->lowe < t->lows)
430                 highe--;
431         diff = (((unsigned long long) highe - t->highs) << 32) + (t->lowe - t->lows);
432         return ((double)diff / freq) / 1000000; /* have to return the result in seconds */
433 }
434
435 static int 
436 have_rdtsc (void) {
437         char buf[256];
438         int have_freq = 0;
439         int have_flag = 0;
440         float val;
441         FILE *cpuinfo;
442
443         if (!(cpuinfo = fopen ("/proc/cpuinfo", "r")))
444                 return 0;
445         while (fgets (buf, sizeof(buf), cpuinfo)) {
446                 if (sscanf (buf, "cpu MHz : %f", &val) == 1) {
447                         /*printf ("got mh: %f\n", val);*/
448                         have_freq = val;
449                 }
450                 if (strncmp (buf, "flags", 5) == 0) {
451                         if (strstr (buf, "tsc")) {
452                                 have_flag = 1;
453                                 /*printf ("have tsc\n");*/
454                         }
455                 }
456         }
457         fclose (cpuinfo);
458         return have_flag? have_freq: 0;
459 }
460
461 #define MONO_TIMER_STARTUP      \
462         if (!(freq = have_rdtsc ())) g_error ("Compiled with TSC support, but none found");
463 #define MONO_TIMER_TYPE  MonoRdtscTimer
464 #define MONO_TIMER_INIT(t)
465 #define MONO_TIMER_DESTROY(t)
466 #define MONO_TIMER_START(t) rdtsc ((t).lows, (t).highs);
467 #define MONO_TIMER_STOP(t) rdtsc ((t).lowe, (t).highe);
468 #define MONO_TIMER_ELAPSED(t) rdtsc_elapsed (&(t))
469
470 #elif USE_WIN32COUNTER
471 #include <windows.h>
472
473 typedef struct {
474         LARGE_INTEGER start, stop;
475 } MonoWin32Timer;
476
477 static int freq;
478
479 static double
480 win32_elapsed (MonoWin32Timer *t)
481 {
482         LONGLONG diff = t->stop.QuadPart - t->start.QuadPart;
483         return ((double)diff / freq) / 1000000; /* have to return the result in seconds */
484 }
485
486 static int 
487 have_win32counter (void) {
488         LARGE_INTEGER f;
489
490         if (!QueryPerformanceFrequency (&f))
491                 return 0;
492         return f.LowPart;
493 }
494
495 #define MONO_TIMER_STARTUP      \
496         if (!(freq = have_win32counter ())) g_error ("Compiled with Win32 counter support, but none found");
497 #define MONO_TIMER_TYPE  MonoWin32Timer
498 #define MONO_TIMER_INIT(t)
499 #define MONO_TIMER_DESTROY(t)
500 #define MONO_TIMER_START(t) QueryPerformanceCounter (&(t).start)
501 #define MONO_TIMER_STOP(t) QueryPerformanceCounter (&(t).stop)
502 #define MONO_TIMER_ELAPSED(t) win32_elapsed (&(t))
503
504 #else
505
506 typedef struct {
507         GTimeVal start, stop;
508 } MonoGLibTimer;
509
510 static double
511 timeval_elapsed (MonoGLibTimer *t)
512 {
513         if (t->start.tv_usec > t->stop.tv_usec) {
514                 t->stop.tv_usec += G_USEC_PER_SEC;
515                 t->stop.tv_sec--;
516         }
517         return (t->stop.tv_sec - t->start.tv_sec) 
518                 + ((double)(t->stop.tv_usec - t->start.tv_usec))/ G_USEC_PER_SEC;
519 }
520
521 #define MONO_TIMER_STARTUP
522 #define MONO_TIMER_TYPE MonoGLibTimer
523 #define MONO_TIMER_INIT(t)
524 #define MONO_TIMER_DESTROY(t)
525 #define MONO_TIMER_START(t) g_get_current_time (&(t).start)
526 #define MONO_TIMER_STOP(t) g_get_current_time (&(t).stop)
527 #define MONO_TIMER_ELAPSED(t) timeval_elapsed (&(t))
528 #endif
529
530 typedef struct _AllocInfo AllocInfo;
531 typedef struct _CallerInfo CallerInfo;
532 typedef struct _LastCallerInfo LastCallerInfo;
533
534 struct _MonoProfiler {
535         GHashTable *methods;
536         MonoMemPool *mempool;
537         /* info about JIT time */
538         MONO_TIMER_TYPE jit_timer;
539         double      jit_time;
540         double      max_jit_time;
541         MonoMethod *max_jit_method;
542         int         methods_jitted;
543         
544         /* tls_id is the id for the TLS slot where MonoProfiler is stored */
545         int         tls_id;
546         GSList     *per_thread;
547         
548         /* chain of callers for the current thread */
549         LastCallerInfo *callers;
550         /* LastCallerInfo nodes for faster allocation */
551         LastCallerInfo *cstorage;
552 };
553
554 typedef struct {
555         MonoMethod *method;
556         guint64 count;
557         double total;
558         AllocInfo *alloc_info;
559         CallerInfo *caller_info;
560 } MethodProfile;
561
562 typedef struct _MethodCallProfile MethodCallProfile;
563
564 struct _MethodCallProfile {
565         MethodCallProfile *next;
566         MONO_TIMER_TYPE timer;
567         MonoMethod *method;
568 };
569
570 struct _AllocInfo {
571         AllocInfo *next;
572         MonoClass *klass;
573         guint count;
574         guint mem;
575 };
576
577 struct _CallerInfo {
578         CallerInfo *next;
579         MonoMethod *caller;
580         guint count;
581 };
582
583 struct _LastCallerInfo {
584         LastCallerInfo *next;
585         MonoMethod *method;
586         MONO_TIMER_TYPE timer;
587 };
588
589 static MonoProfiler*
590 create_profiler (void)
591 {
592         MonoProfiler *prof = g_new0 (MonoProfiler, 1);
593
594         prof->methods = g_hash_table_new (NULL, NULL);
595         MONO_TIMER_INIT (prof->jit_timer);
596         prof->mempool = mono_mempool_new ();
597         return prof;
598 }
599 #if 1
600 #define GET_THREAD_PROF(prof) do {\
601                 MonoProfiler *_tprofiler = TlsGetValue ((prof)->tls_id);        \
602                 if (!_tprofiler) {      \
603                         _tprofiler = create_profiler ();        \
604                         prof->per_thread = g_slist_prepend (prof->per_thread, _tprofiler);      \
605                         TlsSetValue ((prof)->tls_id, _tprofiler);       \
606                 }       \
607                 prof = _tprofiler;      \
608         } while (0)
609 #else
610 /* thread unsafe but faster variant */
611 #define GET_THREAD_PROF(prof)
612 #endif
613
614 static gint
615 compare_profile (MethodProfile *profa, MethodProfile *profb)
616 {
617         return (gint)((profb->total - profa->total)*1000);
618 }
619
620 static void
621 build_profile (MonoMethod *m, MethodProfile *prof, GList **funcs)
622 {
623         prof->method = m;
624         *funcs = g_list_insert_sorted (*funcs, prof, (GCompareFunc)compare_profile);
625 }
626
627 static char*
628 method_get_name (MonoMethod* method)
629 {
630         char *sig, *res;
631         
632         sig = mono_signature_get_desc (method->signature, FALSE);
633         res = g_strdup_printf ("%s.%s::%s(%s)", method->klass->name_space, method->klass->name,
634                 method->name, sig);
635         g_free (sig);
636         return res;
637 }
638
639 static void output_callers (MethodProfile *p);
640
641 static void
642 output_profile (GList *funcs)
643 {
644         GList *tmp;
645         MethodProfile *p;
646         char *m;
647         guint64 total_calls = 0;
648
649         if (funcs)
650                 g_print ("Time(ms) Count   P/call(ms) Method name\n");
651         for (tmp = funcs; tmp; tmp = tmp->next) {
652                 p = tmp->data;
653                 total_calls += p->count;
654                 if (!(gint)(p->total*1000))
655                         continue;
656                 m = method_get_name (p->method);
657                 printf ("########################\n% 8.3f %7llu % 8.3f  %s\n",
658                         (double)(p->total*1000), p->count, (double)(p->total*1000)/(double)p->count, m);
659                 g_free (m);
660                 /* callers */
661                 output_callers (p);
662         }
663         printf ("Total number of calls: %lld\n", total_calls);
664 }
665
666 typedef struct {
667         MethodProfile *mp;
668         guint count;
669 } NewobjProfile;
670
671 static gint
672 compare_newobj_profile (NewobjProfile *profa, NewobjProfile *profb)
673 {
674         return (gint)profb->count - (gint)profa->count;
675 }
676
677 static void
678 build_newobj_profile (MonoClass *class, MethodProfile *mprof, GList **funcs)
679 {
680         NewobjProfile *prof = g_new (NewobjProfile, 1);
681         AllocInfo *tmp;
682         guint count = 0;
683         
684         prof->mp = mprof;
685         /* we use the total amount of memory to sort */
686         for (tmp = mprof->alloc_info; tmp; tmp = tmp->next)
687                 count += tmp->mem;
688         prof->count = count;
689         *funcs = g_list_insert_sorted (*funcs, prof, (GCompareFunc)compare_newobj_profile);
690 }
691
692 static int
693 compare_caller (CallerInfo *a, CallerInfo *b)
694 {
695         return b->count - a->count;
696 }
697
698 static int
699 compare_alloc (AllocInfo *a, AllocInfo *b)
700 {
701         return b->mem - a->mem;
702 }
703
704 static GSList*
705 sort_alloc_list (AllocInfo *ai)
706 {
707         GSList *l = NULL;
708         AllocInfo *tmp;
709         for (tmp = ai; tmp; tmp = tmp->next) {
710                 l = g_slist_insert_sorted (l, tmp, (GCompareFunc)compare_alloc);
711         }
712         return l;
713 }
714
715 static GSList*
716 sort_caller_list (CallerInfo *ai)
717 {
718         GSList *l = NULL;
719         CallerInfo *tmp;
720         for (tmp = ai; tmp; tmp = tmp->next) {
721                 l = g_slist_insert_sorted (l, tmp, (GCompareFunc)compare_caller);
722         }
723         return l;
724 }
725
726 static void
727 output_callers (MethodProfile *p) {
728         guint total_callers, percent;
729         GSList *sorted, *tmps;
730         CallerInfo *cinfo;
731         char *m;
732         
733         g_print ("  Callers (with count) that contribute at least for 1%%:\n");
734         total_callers = 0;
735         for (cinfo = p->caller_info; cinfo; cinfo = cinfo->next) {
736                 total_callers += cinfo->count;
737         }
738         sorted = sort_caller_list (p->caller_info);
739         for (tmps = sorted; tmps; tmps = tmps->next) {
740                 cinfo = tmps->data;
741                 percent = (cinfo->count * 100)/total_callers;
742                 if (percent < 1)
743                         continue;
744                 m = method_get_name (cinfo->caller);
745                 g_print ("    %8d % 3d %% %s\n", cinfo->count, percent, m);
746                 g_free (m);
747         }
748 }
749
750 static void
751 output_newobj_profile (GList *proflist)
752 {
753         GList *tmp;
754         NewobjProfile *p;
755         MethodProfile *mp;
756         AllocInfo *ainfo;
757         MonoClass *klass;
758         const char* isarray;
759         char buf [256];
760         char *m;
761         guint total = 0;
762         GSList *sorted, *tmps;
763
764         g_print ("\nAllocation profiler\n");
765
766         if (proflist)
767                 g_print ("%-9s %s\n", "Total mem", "Method");
768         for (tmp = proflist; tmp; tmp = tmp->next) {
769                 p = tmp->data;
770                 total += p->count;
771                 if (p->count < 50000)
772                         continue;
773                 mp = p->mp;
774                 m = method_get_name (mp->method);
775                 g_print ("########################\n%8d KB %s\n", p->count / 1024, m);
776                 g_free (m);
777                 sorted = sort_alloc_list (mp->alloc_info);
778                 for (tmps = sorted; tmps; tmps = tmps->next) {
779                         ainfo = tmps->data;
780                         if (ainfo->mem < 50000)
781                                 continue;
782                         klass = ainfo->klass;
783                         if (klass->rank) {
784                                 isarray = "[]";
785                                 klass = klass->element_class;
786                         } else {
787                                 isarray = "";
788                         }
789                         g_snprintf (buf, sizeof (buf), "%s.%s%s",
790                                 klass->name_space, klass->name, isarray);
791                         g_print ("    %8d KB %8d %-48s\n", ainfo->mem / 1024, ainfo->count, buf);
792                 }
793                 /* callers */
794                 output_callers (mp);
795         }
796         g_print ("Total memory allocated: %d KB\n", total / 1024);
797 }
798
799 static void
800 merge_methods (MonoMethod *method, MethodProfile *profile, MonoProfiler *prof)
801 {
802         MethodProfile *mprof;
803         AllocInfo *talloc_info, *alloc_info;
804         CallerInfo *tcaller_info, *caller_info;
805         
806         mprof = g_hash_table_lookup (prof->methods, method);
807         if (!mprof) {
808                 /* the master thread didn't see this method, just transfer the info as is */
809                 g_hash_table_insert (prof->methods, method, profile);
810                 return;
811         }
812         /* merge the info from profile into mprof */
813         mprof->count += profile->count;
814         mprof->total += profile->total;
815         /* merge alloc info */
816         for (talloc_info = profile->alloc_info; talloc_info; talloc_info = talloc_info->next) {
817                 for (alloc_info = mprof->alloc_info; alloc_info; alloc_info = alloc_info->next) {
818                         if (alloc_info->klass == talloc_info->klass) {
819                                 /* mprof already has a record for the klass, merge */
820                                 alloc_info->count += talloc_info->count;
821                                 alloc_info->mem += talloc_info->mem;
822                                 break;
823                         }
824                 }
825                 if (!alloc_info) {
826                         /* mprof didn't have the info, just copy it over */
827                         alloc_info = mono_mempool_alloc0 (prof->mempool, sizeof (AllocInfo));
828                         *alloc_info = *talloc_info;
829                         alloc_info->next = mprof->alloc_info;
830                         mprof->alloc_info = alloc_info->next;
831                 }
832         }
833         /* merge callers info */
834         for (tcaller_info = profile->caller_info; tcaller_info; tcaller_info = tcaller_info->next) {
835                 for (caller_info = mprof->caller_info; caller_info; caller_info = caller_info->next) {
836                         if (caller_info->caller == tcaller_info->caller) {
837                                 /* mprof already has a record for the caller method, merge */
838                                 caller_info->count += tcaller_info->count;
839                                 break;
840                         }
841                 }
842                 if (!caller_info) {
843                         /* mprof didn't have the info, just copy it over */
844                         caller_info = mono_mempool_alloc0 (prof->mempool, sizeof (CallerInfo));
845                         *caller_info = *tcaller_info;
846                         caller_info->next = mprof->caller_info;
847                         mprof->caller_info = caller_info->next;
848                 }
849         }
850 }
851
852 static void
853 merge_thread_data (MonoProfiler *master, MonoProfiler *tprof)
854 {
855         master->jit_time += tprof->jit_time;
856         master->methods_jitted += tprof->methods_jitted;
857         if (master->max_jit_time < tprof->max_jit_time) {
858                 master->max_jit_time = tprof->max_jit_time;
859                 master->max_jit_method = tprof->max_jit_method;
860         }
861
862         g_hash_table_foreach (tprof->methods, (GHFunc)merge_methods, master);
863 }
864
865 static void
866 simple_method_enter (MonoProfiler *prof, MonoMethod *method)
867 {
868         MethodProfile *profile_info;
869         LastCallerInfo *callinfo;
870         GET_THREAD_PROF (prof);
871         /*g_print ("enter %p %s::%s in %d (%p)\n", method, method->klass->name, method->name, GetCurrentThreadId (), prof);*/
872         if (!(profile_info = g_hash_table_lookup (prof->methods, method))) {
873                 profile_info = mono_mempool_alloc0 (prof->mempool, sizeof (MethodProfile));
874                 MONO_TIMER_INIT (profile_info->u.timer);
875                 g_hash_table_insert (prof->methods, method, profile_info);
876         }
877         profile_info->count++;
878         if (prof->callers) {
879                 CallerInfo *cinfo;
880                 MonoMethod *caller = prof->callers->method;
881                 for (cinfo = profile_info->caller_info; cinfo; cinfo = cinfo->next) {
882                         if (cinfo->caller == caller)
883                                 break;
884                 }
885                 if (!cinfo) {
886                         cinfo = mono_mempool_alloc0 (prof->mempool, sizeof (CallerInfo));
887                         cinfo->caller = caller;
888                         cinfo->next = profile_info->caller_info;
889                         profile_info->caller_info = cinfo;
890                 }
891                 cinfo->count++;
892         }
893         if (!(callinfo = prof->cstorage)) {
894                 callinfo = mono_mempool_alloc (prof->mempool, sizeof (LastCallerInfo));
895                 MONO_TIMER_INIT (callinfo->timer);
896         } else {
897                 prof->cstorage = prof->cstorage->next;
898         }
899         callinfo->method = method;
900         callinfo->next = prof->callers;
901         prof->callers = callinfo;
902         MONO_TIMER_START (callinfo->timer);
903 }
904
905 static void
906 simple_method_leave (MonoProfiler *prof, MonoMethod *method)
907 {
908         MethodProfile *profile_info;
909         LastCallerInfo *callinfo, *newcallinfo = NULL;
910         
911         GET_THREAD_PROF (prof);
912         /*g_print ("leave %p %s::%s in %d (%p)\n", method, method->klass->name, method->name, GetCurrentThreadId (), prof);*/
913         callinfo = prof->callers;
914         /* should really not happen, but we don't catch exceptions events, yet ... */
915         while (callinfo) {
916                 MONO_TIMER_STOP (callinfo->timer);
917                 profile_info = g_hash_table_lookup (prof->methods, callinfo->method);
918                 if (profile_info)
919                         profile_info->total += MONO_TIMER_ELAPSED (callinfo->timer);
920                 newcallinfo = callinfo->next;
921                 callinfo->next = prof->cstorage;
922                 prof->cstorage = callinfo;
923                 if (callinfo->method == method)
924                         break;
925                 callinfo = newcallinfo;
926         }
927         prof->callers = newcallinfo;
928 }
929
930 static void
931 simple_allocation (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
932 {
933         MethodProfile *profile_info;
934         AllocInfo *tmp;
935
936         GET_THREAD_PROF (prof);
937         if (prof->callers) {
938                 if (!(profile_info = g_hash_table_lookup (prof->methods, prof->callers->method)))
939                         g_assert_not_reached ();
940         } else {
941                 return; /* fine for now */
942         }
943
944         for (tmp = profile_info->alloc_info; tmp; tmp = tmp->next) {
945                 if (tmp->klass == klass)
946                         break;
947         }
948         if (!tmp) {
949                 tmp = mono_mempool_alloc0 (prof->mempool, sizeof (AllocInfo));
950                 tmp->klass = klass;
951                 tmp->next = profile_info->alloc_info;
952                 profile_info->alloc_info = tmp;
953         }
954         tmp->count++;
955         if (klass == mono_defaults.string_class) {
956                 tmp->mem += sizeof (MonoString) + 2 * mono_string_length ((MonoString*)obj) + 2;
957         } else if (klass->parent == mono_defaults.array_class) {
958                 tmp->mem += sizeof (MonoArray) + mono_array_element_size (klass) * mono_array_length ((MonoArray*)obj);
959         } else {
960                 tmp->mem += mono_class_instance_size (klass);
961         }
962 }
963
964 static void
965 simple_method_jit (MonoProfiler *prof, MonoMethod *method)
966 {
967         GET_THREAD_PROF (prof);
968         prof->methods_jitted++;
969         MONO_TIMER_START (prof->jit_timer);
970 }
971
972 static void
973 simple_method_end_jit (MonoProfiler *prof, MonoMethod *method, int result)
974 {
975         double jtime;
976         GET_THREAD_PROF (prof);
977         MONO_TIMER_STOP (prof->jit_timer);
978         jtime = MONO_TIMER_ELAPSED (prof->jit_timer);
979         prof->jit_time += jtime;
980         if (jtime > prof->max_jit_time) {
981                 prof->max_jit_time = jtime;
982                 prof->max_jit_method = method;
983         }
984 }
985
986 static void
987 simple_shutdown (MonoProfiler *prof)
988 {
989         GList *profile = NULL;
990         MonoProfiler *tprof;
991         GSList *tmp;
992         char *str;
993
994         for (tmp = prof->per_thread; tmp; tmp = tmp->next) {
995                 tprof = tmp->data;
996                 merge_thread_data (prof, tprof);
997         }
998
999         printf("Total time spent compiling %d methods (sec): %.4g\n", prof->methods_jitted, prof->jit_time);
1000         if (prof->max_jit_method) {
1001                 str = method_get_name (prof->max_jit_method);
1002                 printf("Slowest method to compile (sec): %.4g: %s\n", prof->max_jit_time, str);
1003                 g_free (str);
1004         }
1005         g_hash_table_foreach (prof->methods, (GHFunc)build_profile, &profile);
1006         output_profile (profile);
1007         g_list_free (profile);
1008         profile = NULL;
1009                 
1010         g_hash_table_foreach (prof->methods, (GHFunc)build_newobj_profile, &profile);
1011         output_newobj_profile (profile);
1012         g_list_free (profile);
1013 }
1014
1015 static void
1016 mono_profiler_install_simple (const char *desc)
1017 {
1018         MonoProfiler *prof;
1019         gchar **args, **ptr;
1020         MonoProfileFlags flags = MONO_PROFILE_ENTER_LEAVE|MONO_PROFILE_JIT_COMPILATION|MONO_PROFILE_ALLOCATIONS;
1021
1022         MONO_TIMER_STARTUP;
1023
1024         if (desc) {
1025                 /* Parse options */
1026                 if (strstr (desc, ":"))
1027                         desc = strstr (desc, ":") + 1;
1028                 else
1029                         desc = NULL;
1030                 args = g_strsplit (desc ? desc : "", ",", -1);
1031
1032                 for (ptr = args; ptr && *ptr; ptr++) {
1033                         const char *arg = *ptr;
1034
1035                         if (!strcmp (arg, "-time"))
1036                                 flags &= ~MONO_PROFILE_ENTER_LEAVE;
1037                         else
1038                            if (!strcmp (arg, "-alloc"))
1039                                    flags &= ~MONO_PROFILE_ALLOCATIONS;
1040                            else {
1041                                    fprintf (stderr, "profiler : Unknown argument '%s'.\n", arg);
1042                                    return;
1043                            }
1044                 }
1045         }
1046
1047         prof = create_profiler ();
1048         prof->tls_id = TlsAlloc ();
1049         TlsSetValue (prof->tls_id, prof);
1050
1051         mono_profiler_install (prof, simple_shutdown);
1052         /* later do also object creation */
1053         mono_profiler_install_enter_leave (simple_method_enter, simple_method_leave);
1054         mono_profiler_install_jit_compile (simple_method_jit, simple_method_end_jit);
1055         mono_profiler_install_allocation (simple_allocation);
1056         mono_profiler_set_events (flags);
1057 }
1058
1059 typedef void (*ProfilerInitializer) (const char*);
1060 #define INITIALIZER_NAME "mono_profiler_startup"
1061
1062 void 
1063 mono_profiler_load (const char *desc)
1064 {
1065         if (!desc || (strcmp ("default", desc) == 0) || (strncmp (desc, "default:", 8) == 0)) {
1066                 mono_profiler_install_simple (desc);
1067         } else {
1068                 GModule *pmodule;
1069                 const char* col = strchr (desc, ':');
1070                 char* libname;
1071                 char* path;
1072                 char *mname;
1073                 if (col != NULL) {
1074                         mname = g_memdup (desc, col - desc);
1075                         mname [col - desc] = 0;
1076                 } else {
1077                         mname = g_strdup (desc);
1078                 }
1079                 libname = g_strdup_printf ("mono-profiler-%s", mname);
1080                 path = g_module_build_path (NULL, libname);
1081                 pmodule = g_module_open (path, G_MODULE_BIND_LAZY);
1082                 if (pmodule) {
1083                         ProfilerInitializer func;
1084                         if (!g_module_symbol (pmodule, INITIALIZER_NAME, (gpointer *)&func)) {
1085                                 g_warning ("Cannot find initializer function %s in profiler module: %s", INITIALIZER_NAME, libname);
1086                         } else {
1087                                 func (desc);
1088                         }
1089                 } else {
1090                         g_warning ("Error loading profiler module '%s': %s", libname, g_module_error ());
1091                 }
1092
1093                 g_free (libname);
1094                 g_free (mname);
1095                 g_free (path);
1096         }
1097 }
1098