2007-06-22 Dick Porter <dick@ximian.com>
[mono.git] / mono / metadata / profiler.c
1 /*
2  * profiler.c: Profiler interface for Mono
3  *
4  * Author:
5  *   Paolo Molaro (lupus@ximian.com)
6  *
7  * (C) 2001-2003 Ximian, Inc.
8  * (C) 2003-2006 Novell, Inc.
9  */
10
11 #include "config.h"
12 #include "mono/metadata/profiler-private.h"
13 #include "mono/metadata/debug-helpers.h"
14 #include "mono/metadata/mono-debug.h"
15 #include "mono/metadata/debug-mono-symfile.h"
16 #include "mono/metadata/metadata-internals.h"
17 #include "mono/metadata/class-internals.h"
18 #include "mono/metadata/domain-internals.h"
19 #include "mono/metadata/gc-internal.h"
20 #include "mono/io-layer/io-layer.h"
21 #include "mono/utils/mono-dl.h"
22 #include <string.h>
23 #ifdef HAVE_UNISTD_H
24 #include <unistd.h>
25 #endif
26 #ifdef HAVE_SYS_TIME_H
27 #include <sys/time.h>
28 #endif
29 #ifdef HAVE_BACKTRACE_SYMBOLS
30 #include <execinfo.h>
31 #endif
32
33 static MonoProfiler * current_profiler = NULL;
34
35 static MonoProfileAppDomainFunc   domain_start_load;
36 static MonoProfileAppDomainResult domain_end_load;
37 static MonoProfileAppDomainFunc   domain_start_unload;
38 static MonoProfileAppDomainFunc   domain_end_unload;
39
40 static MonoProfileAssemblyFunc   assembly_start_load;
41 static MonoProfileAssemblyResult assembly_end_load;
42 static MonoProfileAssemblyFunc   assembly_start_unload;
43 static MonoProfileAssemblyFunc   assembly_end_unload;
44
45 static MonoProfileModuleFunc   module_start_load;
46 static MonoProfileModuleResult module_end_load;
47 static MonoProfileModuleFunc   module_start_unload;
48 static MonoProfileModuleFunc   module_end_unload;
49
50 static MonoProfileClassFunc   class_start_load;
51 static MonoProfileClassResult class_end_load;
52 static MonoProfileClassFunc   class_start_unload;
53 static MonoProfileClassFunc   class_end_unload;
54
55 static MonoProfileMethodFunc   jit_start;
56 static MonoProfileMethodResult jit_end;
57 static MonoProfileJitResult    jit_end2;
58 static MonoProfileMethodResult man_unman_transition;
59 static MonoProfileAllocFunc    allocation_cb;
60 static MonoProfileStatFunc     statistical_cb;
61 static MonoProfileMethodFunc   method_enter;
62 static MonoProfileMethodFunc   method_leave;
63
64 static MonoProfileExceptionFunc exception_throw_cb;
65 static MonoProfileMethodFunc exception_method_leave_cb;
66 static MonoProfileExceptionClauseFunc exception_clause_cb;
67
68 static MonoProfileThreadFunc   thread_start;
69 static MonoProfileThreadFunc   thread_end;
70
71 static MonoProfileCoverageFilterFunc coverage_filter_cb;
72
73 static MonoProfileFunc shutdown_callback;
74
75 static MonoProfileGCFunc        gc_event;
76 static MonoProfileGCResizeFunc  gc_heap_resize;
77
78 #define mono_profiler_coverage_lock() EnterCriticalSection (&profiler_coverage_mutex)
79 #define mono_profiler_coverage_unlock() LeaveCriticalSection (&profiler_coverage_mutex)
80 static CRITICAL_SECTION profiler_coverage_mutex;
81
82 /* this is directly accessible to other mono libs. */
83 MonoProfileFlags mono_profiler_events;
84
85 /**
86  * mono_profiler_install:
87  * @prof: a MonoProfiler structure pointer, or a pointer to a derived structure.
88  * @callback: the function to invoke at shutdown
89  *
90  * Use mono_profiler_install to activate profiling in the Mono runtime.
91  * Typically developers of new profilers will create a new structure whose
92  * first field is a MonoProfiler and put any extra information that they need
93  * to access from the various profiling callbacks there.
94  *
95  */
96 void
97 mono_profiler_install (MonoProfiler *prof, MonoProfileFunc callback)
98 {
99         if (current_profiler)
100                 g_error ("profiler already setup");
101         current_profiler = prof;
102         shutdown_callback = callback;
103         InitializeCriticalSection (&profiler_coverage_mutex);
104 }
105
106 /**
107  * mono_profiler_set_events:
108  * @events: an ORed set of values made up of MONO_PROFILER_ flags
109  *
110  * The events descriped in the @events argument is a set of flags
111  * that represent which profiling events must be triggered.  For
112  * example if you have registered a set of methods for tracking
113  * JIT compilation start and end with mono_profiler_install_jit_compile,
114  * you will want to pass the MONO_PROFILE_JIT_COMPILATION flag to
115  * this routine.
116  *
117  * You can call mono_profile_set_events more than once and you can
118  * do this at runtime to modify which methods are invoked.
119  */
120 void
121 mono_profiler_set_events (MonoProfileFlags events)
122 {
123         mono_profiler_events = events;
124 }
125
126 /**
127  * mono_profiler_get_events:
128  *
129  * Returns a list of active events that will be intercepted. 
130  */
131 MonoProfileFlags
132 mono_profiler_get_events (void)
133 {
134         return mono_profiler_events;
135 }
136
137 /**
138  * mono_profiler_install_enter_leave:
139  * @enter: the routine to be called on each method entry
140  * @fleave: the routine to be called each time a method returns
141  *
142  * Use this routine to install routines that will be called everytime
143  * a method enters and leaves.   The routines will receive as an argument
144  * the MonoMethod representing the method that is entering or leaving.
145  */
146 void
147 mono_profiler_install_enter_leave (MonoProfileMethodFunc enter, MonoProfileMethodFunc fleave)
148 {
149         method_enter = enter;
150         method_leave = fleave;
151 }
152
153 /**
154  * mono_profiler_install_jit_compile:
155  * @start: the routine to be called when the JIT process starts.
156  * @end: the routine to be called when the JIT process ends.
157  *
158  * Use this routine to install routines that will be called when JIT 
159  * compilation of a method starts and completes.
160  */
161 void 
162 mono_profiler_install_jit_compile (MonoProfileMethodFunc start, MonoProfileMethodResult end)
163 {
164         jit_start = start;
165         jit_end = end;
166 }
167
168 void 
169 mono_profiler_install_jit_end (MonoProfileJitResult end)
170 {
171         jit_end2 = end;
172 }
173
174 void 
175 mono_profiler_install_thread (MonoProfileThreadFunc start, MonoProfileThreadFunc end)
176 {
177         thread_start = start;
178         thread_end = end;
179 }
180
181 void 
182 mono_profiler_install_transition (MonoProfileMethodResult callback)
183 {
184         man_unman_transition = callback;
185 }
186
187 void 
188 mono_profiler_install_allocation (MonoProfileAllocFunc callback)
189 {
190         allocation_cb = callback;
191 }
192
193 void 
194 mono_profiler_install_statistical (MonoProfileStatFunc callback)
195 {
196         statistical_cb = callback;
197 }
198
199 void mono_profiler_install_exception (MonoProfileExceptionFunc throw_callback, MonoProfileMethodFunc exc_method_leave, MonoProfileExceptionClauseFunc clause_callback)
200 {
201         exception_throw_cb = throw_callback;
202         exception_method_leave_cb = exc_method_leave;
203         exception_clause_cb = clause_callback;
204 }
205
206 void 
207 mono_profiler_install_coverage_filter (MonoProfileCoverageFilterFunc callback)
208 {
209         coverage_filter_cb = callback;
210 }
211
212 void 
213 mono_profiler_install_appdomain   (MonoProfileAppDomainFunc start_load, MonoProfileAppDomainResult end_load,
214                                    MonoProfileAppDomainFunc start_unload, MonoProfileAppDomainFunc end_unload)
215
216 {
217         domain_start_load = start_load;
218         domain_end_load = end_load;
219         domain_start_unload = start_unload;
220         domain_end_unload = end_unload;
221 }
222
223 void 
224 mono_profiler_install_assembly    (MonoProfileAssemblyFunc start_load, MonoProfileAssemblyResult end_load,
225                                    MonoProfileAssemblyFunc start_unload, MonoProfileAssemblyFunc end_unload)
226 {
227         assembly_start_load = start_load;
228         assembly_end_load = end_load;
229         assembly_start_unload = start_unload;
230         assembly_end_unload = end_unload;
231 }
232
233 void 
234 mono_profiler_install_module      (MonoProfileModuleFunc start_load, MonoProfileModuleResult end_load,
235                                    MonoProfileModuleFunc start_unload, MonoProfileModuleFunc end_unload)
236 {
237         module_start_load = start_load;
238         module_end_load = end_load;
239         module_start_unload = start_unload;
240         module_end_unload = end_unload;
241 }
242
243 void
244 mono_profiler_install_class       (MonoProfileClassFunc start_load, MonoProfileClassResult end_load,
245                                    MonoProfileClassFunc start_unload, MonoProfileClassFunc end_unload)
246 {
247         class_start_load = start_load;
248         class_end_load = end_load;
249         class_start_unload = start_unload;
250         class_end_unload = end_unload;
251 }
252
253 void
254 mono_profiler_method_enter (MonoMethod *method)
255 {
256         if ((mono_profiler_events & MONO_PROFILE_ENTER_LEAVE) && method_enter)
257                 method_enter (current_profiler, method);
258 }
259
260 void
261 mono_profiler_method_leave (MonoMethod *method)
262 {
263         if ((mono_profiler_events & MONO_PROFILE_ENTER_LEAVE) && method_leave)
264                 method_leave (current_profiler, method);
265 }
266
267 void 
268 mono_profiler_method_jit (MonoMethod *method)
269 {
270         if ((mono_profiler_events & MONO_PROFILE_JIT_COMPILATION) && jit_start)
271                 jit_start (current_profiler, method);
272 }
273
274 void 
275 mono_profiler_method_end_jit (MonoMethod *method, MonoJitInfo* jinfo, int result)
276 {
277         if ((mono_profiler_events & MONO_PROFILE_JIT_COMPILATION)) {
278                 if (jit_end)
279                         jit_end (current_profiler, method, result);
280                 if (jit_end2)
281                         jit_end2 (current_profiler, method, jinfo, result);
282         }
283 }
284
285 void 
286 mono_profiler_code_transition (MonoMethod *method, int result)
287 {
288         if ((mono_profiler_events & MONO_PROFILE_TRANSITIONS) && man_unman_transition)
289                 man_unman_transition (current_profiler, method, result);
290 }
291
292 void 
293 mono_profiler_allocation (MonoObject *obj, MonoClass *klass)
294 {
295         if ((mono_profiler_events & MONO_PROFILE_ALLOCATIONS) && allocation_cb)
296                 allocation_cb (current_profiler, obj, klass);
297 }
298
299 void
300 mono_profiler_stat_hit (guchar *ip, void *context)
301 {
302         if ((mono_profiler_events & MONO_PROFILE_STATISTICAL) && statistical_cb)
303                 statistical_cb (current_profiler, ip, context);
304 }
305
306 void
307 mono_profiler_exception_thrown (MonoObject *exception)
308 {
309         if ((mono_profiler_events & MONO_PROFILE_EXCEPTIONS) && exception_throw_cb)
310                 exception_throw_cb (current_profiler, exception);
311 }
312
313 void
314 mono_profiler_exception_method_leave (MonoMethod *method)
315 {
316         if ((mono_profiler_events & MONO_PROFILE_EXCEPTIONS) && exception_method_leave_cb)
317                 exception_method_leave_cb (current_profiler, method);
318 }
319
320 void
321 mono_profiler_exception_clause_handler (MonoMethod *method, int clause_type, int clause_num)
322 {
323         if ((mono_profiler_events & MONO_PROFILE_EXCEPTIONS) && exception_clause_cb)
324                 exception_clause_cb (current_profiler, method, clause_type, clause_num);
325 }
326
327 void
328 mono_profiler_thread_start (gsize tid)
329 {
330         if ((mono_profiler_events & MONO_PROFILE_THREADS) && thread_start)
331                 thread_start (current_profiler, tid);
332 }
333
334 void 
335 mono_profiler_thread_end (gsize tid)
336 {
337         if ((mono_profiler_events & MONO_PROFILE_THREADS) && thread_end)
338                 thread_end (current_profiler, tid);
339 }
340
341 void 
342 mono_profiler_assembly_event  (MonoAssembly *assembly, int code)
343 {
344         if (!(mono_profiler_events & MONO_PROFILE_ASSEMBLY_EVENTS))
345                 return;
346         
347         switch (code) {
348         case MONO_PROFILE_START_LOAD:
349                 if (assembly_start_load)
350                         assembly_start_load (current_profiler, assembly);
351                 break;
352         case MONO_PROFILE_START_UNLOAD:
353                 if (assembly_start_unload)
354                         assembly_start_unload (current_profiler, assembly);
355                 break;
356         case MONO_PROFILE_END_UNLOAD:
357                 if (assembly_end_unload)
358                         assembly_end_unload (current_profiler, assembly);
359                 break;
360         default:
361                 g_assert_not_reached ();
362         }
363 }
364
365 void 
366 mono_profiler_assembly_loaded (MonoAssembly *assembly, int result)
367 {
368         if ((mono_profiler_events & MONO_PROFILE_ASSEMBLY_EVENTS) && assembly_end_load)
369                 assembly_end_load (current_profiler, assembly, result);
370 }
371
372 void 
373 mono_profiler_module_event  (MonoImage *module, int code)
374 {
375         if (!(mono_profiler_events & MONO_PROFILE_MODULE_EVENTS))
376                 return;
377         
378         switch (code) {
379         case MONO_PROFILE_START_LOAD:
380                 if (module_start_load)
381                         module_start_load (current_profiler, module);
382                 break;
383         case MONO_PROFILE_START_UNLOAD:
384                 if (module_start_unload)
385                         module_start_unload (current_profiler, module);
386                 break;
387         case MONO_PROFILE_END_UNLOAD:
388                 if (module_end_unload)
389                         module_end_unload (current_profiler, module);
390                 break;
391         default:
392                 g_assert_not_reached ();
393         }
394 }
395
396 void 
397 mono_profiler_module_loaded (MonoImage *module, int result)
398 {
399         if ((mono_profiler_events & MONO_PROFILE_MODULE_EVENTS) && module_end_load)
400                 module_end_load (current_profiler, module, result);
401 }
402
403 void 
404 mono_profiler_class_event  (MonoClass *klass, int code)
405 {
406         if (!(mono_profiler_events & MONO_PROFILE_CLASS_EVENTS))
407                 return;
408         
409         switch (code) {
410         case MONO_PROFILE_START_LOAD:
411                 if (class_start_load)
412                         class_start_load (current_profiler, klass);
413                 break;
414         case MONO_PROFILE_START_UNLOAD:
415                 if (class_start_unload)
416                         class_start_unload (current_profiler, klass);
417                 break;
418         case MONO_PROFILE_END_UNLOAD:
419                 if (class_end_unload)
420                         class_end_unload (current_profiler, klass);
421                 break;
422         default:
423                 g_assert_not_reached ();
424         }
425 }
426
427 void 
428 mono_profiler_class_loaded (MonoClass *klass, int result)
429 {
430         if ((mono_profiler_events & MONO_PROFILE_CLASS_EVENTS) && class_end_load)
431                 class_end_load (current_profiler, klass, result);
432 }
433
434 void 
435 mono_profiler_appdomain_event  (MonoDomain *domain, int code)
436 {
437         if (!(mono_profiler_events & MONO_PROFILE_APPDOMAIN_EVENTS))
438                 return;
439         
440         switch (code) {
441         case MONO_PROFILE_START_LOAD:
442                 if (domain_start_load)
443                         domain_start_load (current_profiler, domain);
444                 break;
445         case MONO_PROFILE_START_UNLOAD:
446                 if (domain_start_unload)
447                         domain_start_unload (current_profiler, domain);
448                 break;
449         case MONO_PROFILE_END_UNLOAD:
450                 if (domain_end_unload)
451                         domain_end_unload (current_profiler, domain);
452                 break;
453         default:
454                 g_assert_not_reached ();
455         }
456 }
457
458 void 
459 mono_profiler_appdomain_loaded (MonoDomain *domain, int result)
460 {
461         if ((mono_profiler_events & MONO_PROFILE_APPDOMAIN_EVENTS) && domain_end_load)
462                 domain_end_load (current_profiler, domain, result);
463 }
464
465 void 
466 mono_profiler_shutdown (void)
467 {
468         if (current_profiler && shutdown_callback)
469                 shutdown_callback (current_profiler);
470 }
471
472 void
473 mono_profiler_gc_heap_resize (gint64 new_size)
474 {
475         if ((mono_profiler_events & MONO_PROFILE_GC) && gc_heap_resize)
476                 gc_heap_resize (current_profiler, new_size);
477 }
478
479 void
480 mono_profiler_gc_event (MonoGCEvent event, int generation)
481 {
482         if ((mono_profiler_events & MONO_PROFILE_GC) && gc_event)
483                 gc_event (current_profiler, event, generation);
484 }
485
486 void
487 mono_profiler_install_gc (MonoProfileGCFunc callback, MonoProfileGCResizeFunc heap_resize_callback)
488 {
489         mono_gc_enable_events ();
490         gc_event = callback;
491         gc_heap_resize = heap_resize_callback;
492 }
493
494 static GHashTable *coverage_hash = NULL;
495
496 MonoProfileCoverageInfo* 
497 mono_profiler_coverage_alloc (MonoMethod *method, int entries)
498 {
499         MonoProfileCoverageInfo *res;
500
501         if (coverage_filter_cb)
502                 if (! (*coverage_filter_cb) (current_profiler, method))
503                         return NULL;
504
505         mono_profiler_coverage_lock ();
506         if (!coverage_hash)
507                 coverage_hash = g_hash_table_new (NULL, NULL);
508
509         res = g_malloc0 (sizeof (MonoProfileCoverageInfo) + sizeof (void*) * 2 * entries);
510
511         res->entries = entries;
512
513         g_hash_table_insert (coverage_hash, method, res);
514         mono_profiler_coverage_unlock ();
515
516         return res;
517 }
518
519 /* safe only when the method antive code has been unloaded */
520 void
521 mono_profiler_coverage_free (MonoMethod *method)
522 {
523         MonoProfileCoverageInfo* info;
524
525         mono_profiler_coverage_lock ();
526         if (!coverage_hash) {
527                 mono_profiler_coverage_unlock ();
528                 return;
529         }
530
531         info = g_hash_table_lookup (coverage_hash, method);
532         if (info) {
533                 g_free (info);
534                 g_hash_table_remove (coverage_hash, method);
535         }
536         mono_profiler_coverage_unlock ();
537 }
538
539 /**
540  * mono_profiler_coverage_get:
541  * @prof: The profiler handle, installed with mono_profiler_install
542  * @method: the method to gather information from.
543  * @func: A routine that will be called back with the results
544  *
545  * If the MONO_PROFILER_INS_COVERAGE flag was active during JIT compilation
546  * it is posisble to obtain coverage information about a give method.
547  *
548  * The function @func will be invoked repeatedly with instances of the
549  * MonoProfileCoverageEntry structure.
550  */
551 void 
552 mono_profiler_coverage_get (MonoProfiler *prof, MonoMethod *method, MonoProfileCoverageFunc func)
553 {
554         MonoProfileCoverageInfo* info;
555         int i, offset;
556         guint32 code_size;
557         const unsigned char *start, *end, *cil_code;
558         MonoMethodHeader *header;
559         MonoProfileCoverageEntry entry;
560         MonoDebugMethodInfo *debug_minfo;
561
562         mono_profiler_coverage_lock ();
563         info = g_hash_table_lookup (coverage_hash, method);
564         mono_profiler_coverage_unlock ();
565
566         if (!info)
567                 return;
568
569         header = mono_method_get_header (method);
570         start = mono_method_header_get_code (header, &code_size, NULL);
571         debug_minfo = mono_debug_lookup_method (method);
572
573         end = start + code_size;
574         for (i = 0; i < info->entries; ++i) {
575                 cil_code = info->data [i].cil_code;
576                 if (cil_code && cil_code >= start && cil_code < end) {
577                         char *fname = NULL;
578                         offset = cil_code - start;
579                         entry.iloffset = offset;
580                         entry.method = method;
581                         entry.counter = info->data [i].count;
582                         entry.line = entry.col = 1;
583                         entry.filename = NULL;
584                         if (debug_minfo) {
585                                 MonoDebugSourceLocation *location;
586
587                                 location = mono_debug_symfile_lookup_location (debug_minfo, offset);
588                                 if (location) {
589                                         entry.line = location->row;
590                                         entry.col = location->column;
591                                         entry.filename = fname = g_strdup (location->source_file);
592                                         mono_debug_free_source_location (location);
593                                 }
594                         }
595
596                         func (prof, &entry);
597                         g_free (fname);
598                 }
599         }
600 }
601
602 #ifndef DISABLE_PROFILER
603 /*
604  * Small profiler extracted from mint: we should move it in a loadable module
605  * and improve it to do graphs and more accurate timestamping with rdtsc.
606  */
607
608 static FILE* poutput = NULL;
609
610 #define USE_X86TSC 0
611 #define USE_WIN32COUNTER 0
612 #if USE_X86TSC
613
614 typedef struct {
615         unsigned int lows, highs, lowe, highe;
616 } MonoRdtscTimer;
617
618 #define rdtsc(low,high) \
619         __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
620
621 static int freq;
622
623 static double
624 rdtsc_elapsed (MonoRdtscTimer *t)
625 {
626         unsigned long long diff;
627         unsigned int highe = t->highe;
628         if (t->lowe < t->lows)
629                 highe--;
630         diff = (((unsigned long long) highe - t->highs) << 32) + (t->lowe - t->lows);
631         return ((double)diff / freq) / 1000000; /* have to return the result in seconds */
632 }
633
634 static int 
635 have_rdtsc (void) {
636         char buf[256];
637         int have_freq = 0;
638         int have_flag = 0;
639         float val;
640         FILE *cpuinfo;
641
642         if (!(cpuinfo = fopen ("/proc/cpuinfo", "r")))
643                 return 0;
644         while (fgets (buf, sizeof(buf), cpuinfo)) {
645                 if (sscanf (buf, "cpu MHz : %f", &val) == 1) {
646                         /*printf ("got mh: %f\n", val);*/
647                         have_freq = val;
648                 }
649                 if (strncmp (buf, "flags", 5) == 0) {
650                         if (strstr (buf, "tsc")) {
651                                 have_flag = 1;
652                                 /*printf ("have tsc\n");*/
653                         }
654                 }
655         }
656         fclose (cpuinfo);
657         return have_flag? have_freq: 0;
658 }
659
660 #define MONO_TIMER_STARTUP      \
661         if (!(freq = have_rdtsc ())) g_error ("Compiled with TSC support, but none found");
662 #define MONO_TIMER_TYPE  MonoRdtscTimer
663 #define MONO_TIMER_INIT(t)
664 #define MONO_TIMER_DESTROY(t)
665 #define MONO_TIMER_START(t) rdtsc ((t).lows, (t).highs);
666 #define MONO_TIMER_STOP(t) rdtsc ((t).lowe, (t).highe);
667 #define MONO_TIMER_ELAPSED(t) rdtsc_elapsed (&(t))
668
669 #elif USE_WIN32COUNTER
670 #include <windows.h>
671
672 typedef struct {
673         LARGE_INTEGER start, stop;
674 } MonoWin32Timer;
675
676 static int freq;
677
678 static double
679 win32_elapsed (MonoWin32Timer *t)
680 {
681         LONGLONG diff = t->stop.QuadPart - t->start.QuadPart;
682         return ((double)diff / freq) / 1000000; /* have to return the result in seconds */
683 }
684
685 static int 
686 have_win32counter (void) {
687         LARGE_INTEGER f;
688
689         if (!QueryPerformanceFrequency (&f))
690                 return 0;
691         return f.LowPart;
692 }
693
694 #define MONO_TIMER_STARTUP      \
695         if (!(freq = have_win32counter ())) g_error ("Compiled with Win32 counter support, but none found");
696 #define MONO_TIMER_TYPE  MonoWin32Timer
697 #define MONO_TIMER_INIT(t)
698 #define MONO_TIMER_DESTROY(t)
699 #define MONO_TIMER_START(t) QueryPerformanceCounter (&(t).start)
700 #define MONO_TIMER_STOP(t) QueryPerformanceCounter (&(t).stop)
701 #define MONO_TIMER_ELAPSED(t) win32_elapsed (&(t))
702
703 #else
704
705 typedef struct {
706         GTimeVal start, stop;
707 } MonoGLibTimer;
708
709 static double
710 timeval_elapsed (MonoGLibTimer *t)
711 {
712         if (t->start.tv_usec > t->stop.tv_usec) {
713                 t->stop.tv_usec += G_USEC_PER_SEC;
714                 t->stop.tv_sec--;
715         }
716         return (t->stop.tv_sec - t->start.tv_sec) 
717                 + ((double)(t->stop.tv_usec - t->start.tv_usec))/ G_USEC_PER_SEC;
718 }
719
720 #define MONO_TIMER_STARTUP
721 #define MONO_TIMER_TYPE MonoGLibTimer
722 #define MONO_TIMER_INIT(t)
723 #define MONO_TIMER_DESTROY(t)
724 #define MONO_TIMER_START(t) g_get_current_time (&(t).start)
725 #define MONO_TIMER_STOP(t) g_get_current_time (&(t).stop)
726 #define MONO_TIMER_ELAPSED(t) timeval_elapsed (&(t))
727 #endif
728
729 typedef struct _AllocInfo AllocInfo;
730 typedef struct _CallerInfo CallerInfo;
731 typedef struct _LastCallerInfo LastCallerInfo;
732
733 struct _MonoProfiler {
734         GHashTable *methods;
735         MonoMemPool *mempool;
736         /* info about JIT time */
737         MONO_TIMER_TYPE jit_timer;
738         double      jit_time;
739         double      max_jit_time;
740         MonoMethod *max_jit_method;
741         int         methods_jitted;
742         
743         GSList     *per_thread;
744         
745         /* chain of callers for the current thread */
746         LastCallerInfo *callers;
747         /* LastCallerInfo nodes for faster allocation */
748         LastCallerInfo *cstorage;
749 };
750
751 typedef struct {
752         MonoMethod *method;
753         guint64 count;
754         double total;
755         AllocInfo *alloc_info;
756         CallerInfo *caller_info;
757 } MethodProfile;
758
759 typedef struct _MethodCallProfile MethodCallProfile;
760
761 struct _MethodCallProfile {
762         MethodCallProfile *next;
763         MONO_TIMER_TYPE timer;
764         MonoMethod *method;
765 };
766
767 struct _AllocInfo {
768         AllocInfo *next;
769         MonoClass *klass;
770         guint64 count;
771         guint64 mem;
772 };
773
774 struct _CallerInfo {
775         CallerInfo *next;
776         MonoMethod *caller;
777         guint count;
778 };
779
780 struct _LastCallerInfo {
781         LastCallerInfo *next;
782         MonoMethod *method;
783         MONO_TIMER_TYPE timer;
784 };
785
786 static MonoProfiler*
787 create_profiler (void)
788 {
789         MonoProfiler *prof = g_new0 (MonoProfiler, 1);
790
791         prof->methods = g_hash_table_new (mono_aligned_addr_hash, NULL);
792         MONO_TIMER_INIT (prof->jit_timer);
793         prof->mempool = mono_mempool_new ();
794         return prof;
795 }
796 #if 1
797
798 #ifdef HAVE_KW_THREAD
799         static __thread MonoProfiler * tls_profiler;
800 #       define GET_PROFILER() tls_profiler
801 #       define SET_PROFILER(x) tls_profiler = (x)
802 #       define ALLOC_PROFILER() /* nop */
803 #else
804         static guint32 profiler_thread_id = -1;
805 #       define GET_PROFILER() ((MonoProfiler *)TlsGetValue (profiler_thread_id))
806 #       define SET_PROFILER(x) TlsSetValue (profiler_thread_id, x);
807 #       define ALLOC_PROFILER() profiler_thread_id = TlsAlloc ()
808 #endif
809
810 #define GET_THREAD_PROF(prof) do {                                                           \
811                 MonoProfiler *_tprofiler = GET_PROFILER ();                                  \
812                 if (!_tprofiler) {                                                           \
813                         _tprofiler = create_profiler ();                                     \
814                         prof->per_thread = g_slist_prepend (prof->per_thread, _tprofiler);   \
815                         SET_PROFILER (_tprofiler);                                           \
816                 }                                                                            \
817                 prof = _tprofiler;                                                           \
818         } while (0)
819 #else
820 /* thread unsafe but faster variant */
821 #define GET_THREAD_PROF(prof)
822 #endif
823
824 static gint
825 compare_profile (MethodProfile *profa, MethodProfile *profb)
826 {
827         return (gint)((profb->total - profa->total)*1000);
828 }
829
830 static void
831 build_profile (MonoMethod *m, MethodProfile *prof, GList **funcs)
832 {
833         prof->method = m;
834         *funcs = g_list_insert_sorted (*funcs, prof, (GCompareFunc)compare_profile);
835 }
836
837 static char*
838 method_get_name (MonoMethod* method)
839 {
840         char *sig, *res;
841         
842         sig = mono_signature_get_desc (mono_method_signature (method), FALSE);
843         res = g_strdup_printf ("%s%s%s::%s(%s)", method->klass->name_space,
844                         method->klass->name_space ? "." : "", method->klass->name,
845                 method->name, sig);
846         g_free (sig);
847         return res;
848 }
849
850 static void output_callers (MethodProfile *p);
851
852 /* This isn't defined on older glib versions and on some platforms */
853 #ifndef G_GUINT64_FORMAT
854 #define G_GUINT64_FORMAT "ul"
855 #endif
856 #ifndef G_GINT64_FORMAT
857 #define G_GINT64_FORMAT "lld"
858 #endif
859
860 static void
861 output_profile (GList *funcs)
862 {
863         GList *tmp;
864         MethodProfile *p;
865         char *m;
866         guint64 total_calls = 0;
867
868         if (funcs)
869                 fprintf (poutput, "Time(ms) Count   P/call(ms) Method name\n");
870         for (tmp = funcs; tmp; tmp = tmp->next) {
871                 p = tmp->data;
872                 total_calls += p->count;
873                 if (!(gint)(p->total*1000))
874                         continue;
875                 m = method_get_name (p->method);
876                 fprintf (poutput, "########################\n");
877                 fprintf (poutput, "% 8.3f ", (double) (p->total * 1000));
878                 fprintf (poutput, "%7" G_GUINT64_FORMAT " ", (unsigned long long)p->count);
879                 fprintf (poutput, "% 8.3f ", (double) (p->total * 1000)/(double)p->count);
880                 fprintf (poutput, "  %s\n", m);
881
882                 g_free (m);
883                 /* callers */
884                 output_callers (p);
885         }
886         fprintf (poutput, "Total number of calls: %" G_GINT64_FORMAT "\n", (long long)total_calls);
887 }
888
889 typedef struct {
890         MethodProfile *mp;
891         guint64 count;
892 } NewobjProfile;
893
894 static gint
895 compare_newobj_profile (NewobjProfile *profa, NewobjProfile *profb)
896 {
897         if (profb->count == profa->count)
898                 return 0;
899         else
900                 return profb->count > profa->count ? 1 : -1;
901 }
902
903 static void
904 build_newobj_profile (MonoClass *class, MethodProfile *mprof, GList **funcs)
905 {
906         NewobjProfile *prof = g_new (NewobjProfile, 1);
907         AllocInfo *tmp;
908         guint64 count = 0;
909         
910         prof->mp = mprof;
911         /* we use the total amount of memory to sort */
912         for (tmp = mprof->alloc_info; tmp; tmp = tmp->next)
913                 count += tmp->mem;
914         prof->count = count;
915         *funcs = g_list_insert_sorted (*funcs, prof, (GCompareFunc)compare_newobj_profile);
916 }
917
918 static int
919 compare_caller (CallerInfo *a, CallerInfo *b)
920 {
921         return b->count - a->count;
922 }
923
924 static int
925 compare_alloc (AllocInfo *a, AllocInfo *b)
926 {
927         return b->mem - a->mem;
928 }
929
930 static GSList*
931 sort_alloc_list (AllocInfo *ai)
932 {
933         GSList *l = NULL;
934         AllocInfo *tmp;
935         for (tmp = ai; tmp; tmp = tmp->next) {
936                 l = g_slist_insert_sorted (l, tmp, (GCompareFunc)compare_alloc);
937         }
938         return l;
939 }
940
941 static GSList*
942 sort_caller_list (CallerInfo *ai)
943 {
944         GSList *l = NULL;
945         CallerInfo *tmp;
946         for (tmp = ai; tmp; tmp = tmp->next) {
947                 l = g_slist_insert_sorted (l, tmp, (GCompareFunc)compare_caller);
948         }
949         return l;
950 }
951
952 static void
953 output_callers (MethodProfile *p) {
954         guint total_callers, percent;
955         GSList *sorted, *tmps;
956         CallerInfo *cinfo;
957         char *m;
958         
959         fprintf (poutput, "  Callers (with count) that contribute at least for 1%%:\n");
960         total_callers = 0;
961         for (cinfo = p->caller_info; cinfo; cinfo = cinfo->next) {
962                 total_callers += cinfo->count;
963         }
964         sorted = sort_caller_list (p->caller_info);
965         for (tmps = sorted; tmps; tmps = tmps->next) {
966                 cinfo = tmps->data;
967                 percent = (cinfo->count * 100)/total_callers;
968                 if (percent < 1)
969                         continue;
970                 m = method_get_name (cinfo->caller);
971                 fprintf (poutput, "    %8d % 3d %% %s\n", cinfo->count, percent, m);
972                 g_free (m);
973         }
974 }
975
976 static void
977 output_newobj_profile (GList *proflist)
978 {
979         GList *tmp;
980         NewobjProfile *p;
981         MethodProfile *mp;
982         AllocInfo *ainfo;
983         MonoClass *klass;
984         const char* isarray;
985         char buf [256];
986         char *m;
987         guint64 total = 0;
988         GSList *sorted, *tmps;
989
990         fprintf (poutput, "\nAllocation profiler\n");
991
992         if (proflist)
993                 fprintf (poutput, "%-9s %s\n", "Total mem", "Method");
994         for (tmp = proflist; tmp; tmp = tmp->next) {
995                 p = tmp->data;
996                 total += p->count;
997                 if (p->count < 50000)
998                         continue;
999                 mp = p->mp;
1000                 m = method_get_name (mp->method);
1001                 fprintf (poutput, "########################\n%8" G_GUINT64_FORMAT " KB %s\n", (p->count / 1024), m);
1002                 g_free (m);
1003                 sorted = sort_alloc_list (mp->alloc_info);
1004                 for (tmps = sorted; tmps; tmps = tmps->next) {
1005                         ainfo = tmps->data;
1006                         if (ainfo->mem < 50000)
1007                                 continue;
1008                         klass = ainfo->klass;
1009                         if (klass->rank) {
1010                                 isarray = "[]";
1011                                 klass = klass->element_class;
1012                         } else {
1013                                 isarray = "";
1014                         }
1015                         g_snprintf (buf, sizeof (buf), "%s%s%s%s",
1016                                 klass->name_space, klass->name_space ? "." : "", klass->name, isarray);
1017                         fprintf (poutput, "    %8" G_GUINT64_FORMAT " KB %8" G_GUINT64_FORMAT " %-48s\n", (ainfo->mem / 1024), ainfo->count, buf);
1018                 }
1019                 /* callers */
1020                 output_callers (mp);
1021         }
1022         fprintf (poutput, "Total memory allocated: %" G_GUINT64_FORMAT " KB\n", total / 1024);
1023 }
1024
1025 static void
1026 merge_methods (MonoMethod *method, MethodProfile *profile, MonoProfiler *prof)
1027 {
1028         MethodProfile *mprof;
1029         AllocInfo *talloc_info, *alloc_info;
1030         CallerInfo *tcaller_info, *caller_info;
1031
1032         mprof = g_hash_table_lookup (prof->methods, method);
1033         if (!mprof) {
1034                 /* the master thread didn't see this method, just transfer the info as is */
1035                 g_hash_table_insert (prof->methods, method, profile);
1036                 return;
1037         }
1038         /* merge the info from profile into mprof */
1039         mprof->count += profile->count;
1040         mprof->total += profile->total;
1041         /* merge alloc info */
1042         for (talloc_info = profile->alloc_info; talloc_info; talloc_info = talloc_info->next) {
1043                 for (alloc_info = mprof->alloc_info; alloc_info; alloc_info = alloc_info->next) {
1044                         if (alloc_info->klass == talloc_info->klass) {
1045                                 /* mprof already has a record for the klass, merge */
1046                                 alloc_info->count += talloc_info->count;
1047                                 alloc_info->mem += talloc_info->mem;
1048                                 break;
1049                         }
1050                 }
1051                 if (!alloc_info) {
1052                         /* mprof didn't have the info, just copy it over */
1053                         alloc_info = mono_mempool_alloc0 (prof->mempool, sizeof (AllocInfo));
1054                         *alloc_info = *talloc_info;
1055                         alloc_info->next = mprof->alloc_info;
1056                         mprof->alloc_info = alloc_info->next;
1057                 }
1058         }
1059         /* merge callers info */
1060         for (tcaller_info = profile->caller_info; tcaller_info; tcaller_info = tcaller_info->next) {
1061                 for (caller_info = mprof->caller_info; caller_info; caller_info = caller_info->next) {
1062                         if (caller_info->caller == tcaller_info->caller) {
1063                                 /* mprof already has a record for the caller method, merge */
1064                                 caller_info->count += tcaller_info->count;
1065                                 break;
1066                         }
1067                 }
1068                 if (!caller_info) {
1069                         /* mprof didn't have the info, just copy it over */
1070                         caller_info = mono_mempool_alloc0 (prof->mempool, sizeof (CallerInfo));
1071                         *caller_info = *tcaller_info;
1072                         caller_info->next = mprof->caller_info;
1073                         mprof->caller_info = caller_info;
1074                 }
1075         }
1076 }
1077
1078 static void
1079 merge_thread_data (MonoProfiler *master, MonoProfiler *tprof)
1080 {
1081         master->jit_time += tprof->jit_time;
1082         master->methods_jitted += tprof->methods_jitted;
1083         if (master->max_jit_time < tprof->max_jit_time) {
1084                 master->max_jit_time = tprof->max_jit_time;
1085                 master->max_jit_method = tprof->max_jit_method;
1086         }
1087
1088         g_hash_table_foreach (tprof->methods, (GHFunc)merge_methods, master);
1089 }
1090
1091 static void
1092 simple_method_enter (MonoProfiler *prof, MonoMethod *method)
1093 {
1094         MethodProfile *profile_info;
1095         LastCallerInfo *callinfo;
1096         GET_THREAD_PROF (prof);
1097         /*g_print ("enter %p %s::%s in %d (%p)\n", method, method->klass->name, method->name, GetCurrentThreadId (), prof);*/
1098         if (!(profile_info = g_hash_table_lookup (prof->methods, method))) {
1099                 profile_info = mono_mempool_alloc0 (prof->mempool, sizeof (MethodProfile));
1100                 MONO_TIMER_INIT (profile_info->u.timer);
1101                 g_hash_table_insert (prof->methods, method, profile_info);
1102         }
1103         profile_info->count++;
1104         if (prof->callers) {
1105                 CallerInfo *cinfo;
1106                 MonoMethod *caller = prof->callers->method;
1107                 for (cinfo = profile_info->caller_info; cinfo; cinfo = cinfo->next) {
1108                         if (cinfo->caller == caller)
1109                                 break;
1110                 }
1111                 if (!cinfo) {
1112                         cinfo = mono_mempool_alloc0 (prof->mempool, sizeof (CallerInfo));
1113                         cinfo->caller = caller;
1114                         cinfo->next = profile_info->caller_info;
1115                         profile_info->caller_info = cinfo;
1116                 }
1117                 cinfo->count++;
1118         }
1119         if (!(callinfo = prof->cstorage)) {
1120                 callinfo = mono_mempool_alloc (prof->mempool, sizeof (LastCallerInfo));
1121                 MONO_TIMER_INIT (callinfo->timer);
1122         } else {
1123                 prof->cstorage = prof->cstorage->next;
1124         }
1125         callinfo->method = method;
1126         callinfo->next = prof->callers;
1127         prof->callers = callinfo;
1128         MONO_TIMER_START (callinfo->timer);
1129 }
1130
1131 static void
1132 simple_method_leave (MonoProfiler *prof, MonoMethod *method)
1133 {
1134         MethodProfile *profile_info;
1135         LastCallerInfo *callinfo, *newcallinfo = NULL;
1136         
1137         GET_THREAD_PROF (prof);
1138         /*g_print ("leave %p %s::%s in %d (%p)\n", method, method->klass->name, method->name, GetCurrentThreadId (), prof);*/
1139         callinfo = prof->callers;
1140         /* should really not happen, but we don't catch exceptions events, yet ... */
1141         while (callinfo) {
1142                 MONO_TIMER_STOP (callinfo->timer);
1143                 profile_info = g_hash_table_lookup (prof->methods, callinfo->method);
1144                 if (profile_info)
1145                         profile_info->total += MONO_TIMER_ELAPSED (callinfo->timer);
1146                 newcallinfo = callinfo->next;
1147                 callinfo->next = prof->cstorage;
1148                 prof->cstorage = callinfo;
1149                 if (callinfo->method == method)
1150                         break;
1151                 callinfo = newcallinfo;
1152         }
1153         prof->callers = newcallinfo;
1154 }
1155
1156 static void
1157 simple_allocation (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
1158 {
1159         MethodProfile *profile_info;
1160         AllocInfo *tmp;
1161
1162         GET_THREAD_PROF (prof);
1163         if (prof->callers) {
1164                 MonoMethod *caller = prof->callers->method;
1165
1166                 /* Otherwise all allocations are attributed to icall_wrapper_mono_object_new */
1167                 if (caller->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)
1168                         caller = prof->callers->next->method;
1169
1170                 if (!(profile_info = g_hash_table_lookup (prof->methods, caller)))
1171                         g_assert_not_reached ();
1172         } else {
1173                 return; /* fine for now */
1174         }
1175
1176         for (tmp = profile_info->alloc_info; tmp; tmp = tmp->next) {
1177                 if (tmp->klass == klass)
1178                         break;
1179         }
1180         if (!tmp) {
1181                 tmp = mono_mempool_alloc0 (prof->mempool, sizeof (AllocInfo));
1182                 tmp->klass = klass;
1183                 tmp->next = profile_info->alloc_info;
1184                 profile_info->alloc_info = tmp;
1185         }
1186         tmp->count++;
1187         tmp->mem += mono_object_get_size (obj);
1188 }
1189
1190 static void
1191 simple_method_jit (MonoProfiler *prof, MonoMethod *method)
1192 {
1193         GET_THREAD_PROF (prof);
1194         prof->methods_jitted++;
1195         MONO_TIMER_START (prof->jit_timer);
1196 }
1197
1198 static void
1199 simple_method_end_jit (MonoProfiler *prof, MonoMethod *method, int result)
1200 {
1201         double jtime;
1202         GET_THREAD_PROF (prof);
1203         MONO_TIMER_STOP (prof->jit_timer);
1204         jtime = MONO_TIMER_ELAPSED (prof->jit_timer);
1205         prof->jit_time += jtime;
1206         if (jtime > prof->max_jit_time) {
1207                 prof->max_jit_time = jtime;
1208                 prof->max_jit_method = method;
1209         }
1210 }
1211
1212 /* about 10 minutes of samples */
1213 #define MAX_PROF_SAMPLES (1000*60*10)
1214 static int prof_counts = 0;
1215 static int prof_ucounts = 0;
1216 static gpointer* prof_addresses = NULL;
1217 static GHashTable *prof_table = NULL;
1218
1219 static void
1220 simple_stat_hit (MonoProfiler *prof, guchar *ip, void *context)
1221 {
1222         int pos;
1223
1224         if (prof_counts >= MAX_PROF_SAMPLES)
1225                 return;
1226         pos = InterlockedIncrement (&prof_counts);
1227         prof_addresses [pos - 1] = ip;
1228 }
1229
1230 static int
1231 compare_methods_prof (gconstpointer a, gconstpointer b)
1232 {
1233         int ca = GPOINTER_TO_UINT (g_hash_table_lookup (prof_table, a));
1234         int cb = GPOINTER_TO_UINT (g_hash_table_lookup (prof_table, b));
1235         return cb-ca;
1236 }
1237
1238 static void
1239 prof_foreach (char *method, gpointer c, gpointer data)
1240 {
1241         GList **list = data;
1242         *list = g_list_insert_sorted (*list, method, compare_methods_prof);
1243 }
1244
1245 typedef struct Addr2LineData Addr2LineData;
1246
1247 struct Addr2LineData {
1248         Addr2LineData *next;
1249         FILE *pipein;
1250         FILE *pipeout;
1251         char *binary;
1252         int child_pid;
1253 };
1254
1255 static Addr2LineData *addr2line_pipes = NULL;
1256
1257 static char*
1258 try_addr2line (const char* binary, gpointer ip)
1259 {
1260         char buf [1024];
1261         char *res;
1262         Addr2LineData *addr2line;
1263
1264         for (addr2line = addr2line_pipes; addr2line; addr2line = addr2line->next) {
1265                 if (strcmp (binary, addr2line->binary) == 0)
1266                         break;
1267         }
1268         if (!addr2line) {
1269                 const char *addr_argv[] = {"addr2line", "-f", "-e", binary, NULL};
1270                 int child_pid;
1271                 int ch_in, ch_out;
1272 #ifdef __linux__
1273                 char monobin [1024];
1274                 /* non-linux platforms will need different code here */
1275                 if (strcmp (binary, "mono") == 0) {
1276                         int count = readlink ("/proc/self/exe", monobin, sizeof (monobin));
1277                         if (count >= 0 && count < sizeof (monobin)) {
1278                                 monobin [count] = 0;
1279                                 addr_argv [3] = monobin;
1280                         }
1281                 }
1282 #endif
1283                 if (!g_spawn_async_with_pipes (NULL, (char**)addr_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
1284                                 &child_pid, &ch_in, &ch_out, NULL, NULL)) {
1285                         return g_strdup (binary);
1286                 }
1287                 addr2line = g_new0 (Addr2LineData, 1);
1288                 addr2line->child_pid = child_pid;
1289                 addr2line->binary = g_strdup (binary);
1290                 addr2line->pipein = fdopen (ch_in, "w");
1291                 addr2line->pipeout = fdopen (ch_out, "r");
1292                 addr2line->next = addr2line_pipes;
1293                 addr2line_pipes = addr2line;
1294         }
1295         fprintf (addr2line->pipein, "%p\n", ip);
1296         fflush (addr2line->pipein);
1297         /* we first get the func name and then file:lineno in a second line */
1298         if (fgets (buf, sizeof (buf), addr2line->pipeout) && buf [0] != '?') {
1299                 char *end = strchr (buf, '\n');
1300                 if (end)
1301                         *end = 0;
1302                 res = g_strdup_printf ("%s(%s", binary, buf);
1303                 /* discard the filename/line info */
1304                 fgets (buf, sizeof (buf), addr2line->pipeout);
1305         } else {
1306                 res = g_strdup (binary);
1307         }
1308         return res;
1309 }
1310
1311 static void
1312 stat_prof_report (void)
1313 {
1314         MonoJitInfo *ji;
1315         int count = prof_counts;
1316         int i, c;
1317         char *mn;
1318         gpointer ip;
1319         GList *tmp, *sorted = NULL;
1320         int pcount = ++ prof_counts;
1321
1322         prof_counts = MAX_PROF_SAMPLES;
1323         for (i = 0; i < count; ++i) {
1324                 ip = prof_addresses [i];
1325                 ji = mono_jit_info_table_find (mono_domain_get (), ip);
1326                 if (ji) {
1327                         mn = mono_method_full_name (ji->method, TRUE);
1328                 } else {
1329 #ifdef HAVE_BACKTRACE_SYMBOLS
1330                         char **names;
1331                         char *send;
1332                         int no_func;
1333                         prof_ucounts++;
1334                         names = backtrace_symbols (&ip, 1);
1335                         send = strchr (names [0], '+');
1336                         if (send) {
1337                                 *send = 0;
1338                                 no_func = 0;
1339                         } else {
1340                                 no_func = 1;
1341                         }
1342                         send = strchr (names [0], '[');
1343                         if (send)
1344                                 *send = 0;
1345                         if (no_func && names [0][0]) {
1346                                 char *endp = strchr (names [0], 0);
1347                                 while (--endp >= names [0] && g_ascii_isspace (*endp))
1348                                         *endp = 0;
1349                                 mn = try_addr2line (names [0], ip);
1350                         } else {
1351                                 mn = g_strdup (names [0]);
1352                         }
1353                         free (names);
1354 #else
1355                         prof_ucounts++;
1356                         mn = g_strdup_printf ("unmanaged [%p]", ip);
1357 #endif
1358                 }
1359                 c = GPOINTER_TO_UINT (g_hash_table_lookup (prof_table, mn));
1360                 c++;
1361                 g_hash_table_insert (prof_table, mn, GUINT_TO_POINTER (c));
1362                 if (c > 1)
1363                         g_free (mn);
1364         }
1365         fprintf (poutput, "prof counts: total/unmanaged: %d/%d\n", pcount, prof_ucounts);
1366         g_hash_table_foreach (prof_table, (GHFunc)prof_foreach, &sorted);
1367         for (tmp = sorted; tmp; tmp = tmp->next) {
1368                 double perc;
1369                 c = GPOINTER_TO_UINT (g_hash_table_lookup (prof_table, tmp->data));
1370                 perc = c*100.0/count;
1371                 fprintf (poutput, "%7d\t%5.2f %% %s\n", c, perc, (char*)tmp->data);
1372         }
1373         g_list_free (sorted);
1374 }
1375
1376 static void
1377 simple_appdomain_unload (MonoProfiler *prof, MonoDomain *domain)
1378 {
1379         /* FIXME: we should actually record partial data for each domain, 
1380          * since the ip->ji->method mappings are going away at domain unload time.
1381          */
1382         if (domain == mono_get_root_domain ())
1383                 stat_prof_report ();
1384 }
1385
1386 static void
1387 simple_shutdown (MonoProfiler *prof)
1388 {
1389         GList *profile = NULL;
1390         MonoProfiler *tprof;
1391         GSList *tmp;
1392         char *str;
1393
1394         for (tmp = prof->per_thread; tmp; tmp = tmp->next) {
1395                 tprof = tmp->data;
1396                 merge_thread_data (prof, tprof);
1397         }
1398
1399         fprintf (poutput, "Total time spent compiling %d methods (sec): %.4g\n", prof->methods_jitted, prof->jit_time);
1400         if (prof->max_jit_method) {
1401                 str = method_get_name (prof->max_jit_method);
1402                 fprintf (poutput, "Slowest method to compile (sec): %.4g: %s\n", prof->max_jit_time, str);
1403                 g_free (str);
1404         }
1405         g_hash_table_foreach (prof->methods, (GHFunc)build_profile, &profile);
1406         output_profile (profile);
1407         g_list_free (profile);
1408         profile = NULL;
1409                 
1410         g_hash_table_foreach (prof->methods, (GHFunc)build_newobj_profile, &profile);
1411         output_newobj_profile (profile);
1412         g_list_free (profile);
1413
1414         g_free (prof_addresses);
1415         prof_addresses = NULL;
1416         g_hash_table_destroy (prof_table);
1417 }
1418
1419 static void
1420 mono_profiler_install_simple (const char *desc)
1421 {
1422         MonoProfiler *prof;
1423         gchar **args, **ptr;
1424         MonoProfileFlags flags = 0;
1425
1426         MONO_TIMER_STARTUP;
1427         poutput = stdout;
1428
1429         if (!desc)
1430                 desc = "alloc,time,jit";
1431
1432         if (desc) {
1433                 /* Parse options */
1434                 if (strstr (desc, ":"))
1435                         desc = strstr (desc, ":") + 1;
1436                 else
1437                         desc = "alloc,time,jit";
1438                 args = g_strsplit (desc, ",", -1);
1439
1440                 for (ptr = args; ptr && *ptr; ptr++) {
1441                         const char *arg = *ptr;
1442
1443                         if (!strcmp (arg, "time"))
1444                                 flags |= MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_EXCEPTIONS;
1445                         else if (!strcmp (arg, "alloc"))
1446                                 flags |= MONO_PROFILE_ALLOCATIONS;
1447                         else if (!strcmp (arg, "stat"))
1448                                 flags |= MONO_PROFILE_STATISTICAL | MONO_PROFILE_APPDOMAIN_EVENTS;
1449                         else if (!strcmp (arg, "jit"))
1450                                 flags |= MONO_PROFILE_JIT_COMPILATION;
1451                         else if (strncmp (arg, "file=", 5) == 0) {
1452                                 poutput = fopen (arg + 5, "wb");
1453                                 if (!poutput) {
1454                                         poutput = stdout;
1455                                         fprintf (stderr, "profiler : cannot open profile output file '%s'.\n", arg + 5);
1456                                 }
1457                         } else {
1458                                 fprintf (stderr, "profiler : Unknown argument '%s'.\n", arg);
1459                                 return;
1460                         }
1461                 }
1462         }
1463         if (flags & MONO_PROFILE_ALLOCATIONS)
1464                 flags |= MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_EXCEPTIONS;
1465         if (!flags)
1466                 flags = MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_JIT_COMPILATION | MONO_PROFILE_EXCEPTIONS;
1467
1468         prof = create_profiler ();
1469         ALLOC_PROFILER ();
1470         SET_PROFILER (prof);
1471
1472         /* statistical profiler data */
1473         prof_addresses = g_new0 (gpointer, MAX_PROF_SAMPLES);
1474         prof_table = g_hash_table_new (g_str_hash, g_str_equal);
1475
1476         mono_profiler_install (prof, simple_shutdown);
1477         mono_profiler_install_enter_leave (simple_method_enter, simple_method_leave);
1478         mono_profiler_install_exception (NULL, simple_method_leave, NULL);
1479         mono_profiler_install_jit_compile (simple_method_jit, simple_method_end_jit);
1480         mono_profiler_install_allocation (simple_allocation);
1481         mono_profiler_install_appdomain (NULL, NULL, simple_appdomain_unload, NULL);
1482         mono_profiler_install_statistical (simple_stat_hit);
1483         mono_profiler_set_events (flags);
1484 }
1485
1486 #endif /* DISABLE_PROFILER */
1487
1488 typedef void (*ProfilerInitializer) (const char*);
1489 #define INITIALIZER_NAME "mono_profiler_startup"
1490
1491 /**
1492  * mono_profiler_load:
1493  * @desc: arguments to configure the profiler
1494  *
1495  * Invoke this method to initialize the profiler.   This will drive the
1496  * loading of the internal ("default") or any external profilers.
1497  *
1498  * This routine is invoked by Mono's driver, but must be called manually
1499  * if you embed Mono into your application.
1500  */
1501 void 
1502 mono_profiler_load (const char *desc)
1503 {
1504 #ifndef DISABLE_PROFILER
1505         if (!desc || (strcmp ("default", desc) == 0) || (strncmp (desc, "default:", 8) == 0)) {
1506                 mono_profiler_install_simple (desc);
1507                 return;
1508         }
1509 #else
1510         if (!desc) {
1511                 desc = "default";
1512         }
1513 #endif
1514         {
1515                 MonoDl *pmodule;
1516                 const char* col = strchr (desc, ':');
1517                 char* libname;
1518                 char* path;
1519                 char *mname;
1520                 char *err;
1521                 void *iter;
1522                 if (col != NULL) {
1523                         mname = g_memdup (desc, col - desc);
1524                         mname [col - desc] = 0;
1525                 } else {
1526                         mname = g_strdup (desc);
1527                 }
1528                 libname = g_strdup_printf ("mono-profiler-%s", mname);
1529                 iter = NULL;
1530                 err = NULL;
1531                 while ((path = mono_dl_build_path (NULL, libname, &iter))) {
1532                         g_free (err);
1533                         pmodule = mono_dl_open (path, MONO_DL_LAZY, &err);
1534                         if (pmodule) {
1535                                 ProfilerInitializer func;
1536                                 if ((err = mono_dl_symbol (pmodule, INITIALIZER_NAME, (gpointer *)&func))) {
1537                                         g_warning ("Cannot find initializer function %s in profiler module: %s (%s)", INITIALIZER_NAME, libname, err);
1538                                         g_free (err);
1539                                         err = NULL;
1540                                 } else {
1541                                         func (desc);
1542                                 }
1543                                 break;
1544                         }
1545                         g_free (path);
1546                 }
1547                 if (!pmodule) {
1548                         g_warning ("Error loading profiler module '%s': %s", libname, err);
1549                         g_free (err);
1550                 }
1551                 g_free (libname);
1552                 g_free (mname);
1553                 g_free (path);
1554         }
1555 }
1556