Mon Mar 8 17:30:44 CET 2010 Paolo Molaro <lupus@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  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
8  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
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 typedef struct _ProfilerDesc ProfilerDesc;
34 struct _ProfilerDesc {
35         ProfilerDesc *next;
36         MonoProfiler *profiler;
37         MonoProfileFlags events;
38
39         MonoProfileAppDomainFunc   domain_start_load;
40         MonoProfileAppDomainResult domain_end_load;
41         MonoProfileAppDomainFunc   domain_start_unload;
42         MonoProfileAppDomainFunc   domain_end_unload;
43
44         MonoProfileAssemblyFunc   assembly_start_load;
45         MonoProfileAssemblyResult assembly_end_load;
46         MonoProfileAssemblyFunc   assembly_start_unload;
47         MonoProfileAssemblyFunc   assembly_end_unload;
48
49         MonoProfileModuleFunc   module_start_load;
50         MonoProfileModuleResult module_end_load;
51         MonoProfileModuleFunc   module_start_unload;
52         MonoProfileModuleFunc   module_end_unload;
53
54         MonoProfileClassFunc   class_start_load;
55         MonoProfileClassResult class_end_load;
56         MonoProfileClassFunc   class_start_unload;
57         MonoProfileClassFunc   class_end_unload;
58
59         MonoProfileMethodFunc   jit_start;
60         MonoProfileMethodResult jit_end;
61         MonoProfileJitResult    jit_end2;
62         MonoProfileMethodFunc   method_free;
63         MonoProfileMethodFunc   method_start_invoke;
64         MonoProfileMethodFunc   method_end_invoke;
65         MonoProfileMethodResult man_unman_transition;
66         MonoProfileAllocFunc    allocation_cb;
67         MonoProfileMonitorFunc  monitor_event_cb;
68         MonoProfileStatFunc     statistical_cb;
69         MonoProfileStatCallChainFunc statistical_call_chain_cb;
70         int                     statistical_call_chain_depth;
71         MonoProfilerCallChainStrategy  statistical_call_chain_strategy;
72         MonoProfileMethodFunc   method_enter;
73         MonoProfileMethodFunc   method_leave;
74
75         MonoProfileExceptionFunc        exception_throw_cb;
76         MonoProfileMethodFunc exception_method_leave_cb;
77         MonoProfileExceptionClauseFunc exception_clause_cb;
78
79         MonoProfileIomapFunc iomap_cb;
80
81         MonoProfileThreadFunc   thread_start;
82         MonoProfileThreadFunc   thread_end;
83
84         MonoProfileCoverageFilterFunc coverage_filter_cb;
85
86         MonoProfileFunc shutdown_callback;
87
88         MonoProfileGCFunc        gc_event;
89         MonoProfileGCResizeFunc  gc_heap_resize;
90         MonoProfileGCMoveFunc    gc_moves;
91
92         MonoProfileFunc          runtime_initialized_event;
93
94         MonoProfilerCodeChunkNew code_chunk_new;
95         MonoProfilerCodeChunkDestroy code_chunk_destroy;
96         MonoProfilerCodeBufferNew code_buffer_new;
97 };
98
99 static ProfilerDesc *prof_list = NULL;
100
101 #define mono_profiler_coverage_lock() EnterCriticalSection (&profiler_coverage_mutex)
102 #define mono_profiler_coverage_unlock() LeaveCriticalSection (&profiler_coverage_mutex)
103 static CRITICAL_SECTION profiler_coverage_mutex;
104
105 /* this is directly accessible to other mono libs.
106  * It is the ORed value of all the profiler's events.
107  */
108 MonoProfileFlags mono_profiler_events;
109
110 /**
111  * mono_profiler_install:
112  * @prof: a MonoProfiler structure pointer, or a pointer to a derived structure.
113  * @callback: the function to invoke at shutdown
114  *
115  * Use mono_profiler_install to activate profiling in the Mono runtime.
116  * Typically developers of new profilers will create a new structure whose
117  * first field is a MonoProfiler and put any extra information that they need
118  * to access from the various profiling callbacks there.
119  *
120  */
121 void
122 mono_profiler_install (MonoProfiler *prof, MonoProfileFunc callback)
123 {
124         ProfilerDesc *desc = g_new0 (ProfilerDesc, 1);
125         if (!prof_list)
126                 InitializeCriticalSection (&profiler_coverage_mutex);
127         desc->profiler = prof;
128         desc->shutdown_callback = callback;
129         desc->next = prof_list;
130         prof_list = desc;
131 }
132
133 /**
134  * mono_profiler_set_events:
135  * @events: an ORed set of values made up of MONO_PROFILER_ flags
136  *
137  * The events descriped in the @events argument is a set of flags
138  * that represent which profiling events must be triggered.  For
139  * example if you have registered a set of methods for tracking
140  * JIT compilation start and end with mono_profiler_install_jit_compile,
141  * you will want to pass the MONO_PROFILE_JIT_COMPILATION flag to
142  * this routine.
143  *
144  * You can call mono_profile_set_events more than once and you can
145  * do this at runtime to modify which methods are invoked.
146  */
147 void
148 mono_profiler_set_events (MonoProfileFlags events)
149 {
150         ProfilerDesc *prof;
151         MonoProfileFlags value = 0;
152         if (prof_list)
153                 prof_list->events = events;
154         for (prof = prof_list; prof; prof = prof->next)
155                 value |= prof->events;
156         mono_profiler_events = value;
157 }
158
159 /**
160  * mono_profiler_get_events:
161  *
162  * Returns a list of active events that will be intercepted. 
163  */
164 MonoProfileFlags
165 mono_profiler_get_events (void)
166 {
167         return mono_profiler_events;
168 }
169
170 /**
171  * mono_profiler_install_enter_leave:
172  * @enter: the routine to be called on each method entry
173  * @fleave: the routine to be called each time a method returns
174  *
175  * Use this routine to install routines that will be called everytime
176  * a method enters and leaves.   The routines will receive as an argument
177  * the MonoMethod representing the method that is entering or leaving.
178  */
179 void
180 mono_profiler_install_enter_leave (MonoProfileMethodFunc enter, MonoProfileMethodFunc fleave)
181 {
182         if (!prof_list)
183                 return;
184         prof_list->method_enter = enter;
185         prof_list->method_leave = fleave;
186 }
187
188 /**
189  * mono_profiler_install_jit_compile:
190  * @start: the routine to be called when the JIT process starts.
191  * @end: the routine to be called when the JIT process ends.
192  *
193  * Use this routine to install routines that will be called when JIT 
194  * compilation of a method starts and completes.
195  */
196 void 
197 mono_profiler_install_jit_compile (MonoProfileMethodFunc start, MonoProfileMethodResult end)
198 {
199         if (!prof_list)
200                 return;
201         prof_list->jit_start = start;
202         prof_list->jit_end = end;
203 }
204
205 void 
206 mono_profiler_install_jit_end (MonoProfileJitResult end)
207 {
208         if (!prof_list)
209                 return;
210         prof_list->jit_end2 = end;
211 }
212
213 void 
214 mono_profiler_install_method_free (MonoProfileMethodFunc callback)
215 {
216         if (!prof_list)
217                 return;
218         prof_list->method_free = callback;
219 }
220
221 void
222 mono_profiler_install_method_invoke (MonoProfileMethodFunc start, MonoProfileMethodFunc end)
223 {
224         if (!prof_list)
225                 return;
226         prof_list->method_start_invoke = start;
227         prof_list->method_end_invoke = end;
228 }
229
230 void 
231 mono_profiler_install_thread (MonoProfileThreadFunc start, MonoProfileThreadFunc end)
232 {
233         if (!prof_list)
234                 return;
235         prof_list->thread_start = start;
236         prof_list->thread_end = end;
237 }
238
239 void 
240 mono_profiler_install_transition (MonoProfileMethodResult callback)
241 {
242         if (!prof_list)
243                 return;
244         prof_list->man_unman_transition = callback;
245 }
246
247 void 
248 mono_profiler_install_allocation (MonoProfileAllocFunc callback)
249 {
250         if (!prof_list)
251                 return;
252         prof_list->allocation_cb = callback;
253 }
254
255 void
256 mono_profiler_install_monitor  (MonoProfileMonitorFunc callback)
257 {
258         if (!prof_list)
259                 return;
260         prof_list->monitor_event_cb = callback;
261 }
262
263 void 
264 mono_profiler_install_statistical (MonoProfileStatFunc callback)
265 {
266         if (!prof_list)
267                 return;
268         prof_list->statistical_cb = callback;
269 }
270
271 void 
272 mono_profiler_install_statistical_call_chain (MonoProfileStatCallChainFunc callback, int call_chain_depth, MonoProfilerCallChainStrategy call_chain_strategy) {
273         if (!prof_list)
274                 return;
275         if (call_chain_depth > MONO_PROFILER_MAX_STAT_CALL_CHAIN_DEPTH) {
276                 call_chain_depth = MONO_PROFILER_MAX_STAT_CALL_CHAIN_DEPTH;
277         }
278         if ((call_chain_strategy >= MONO_PROFILER_CALL_CHAIN_INVALID) || (call_chain_strategy < MONO_PROFILER_CALL_CHAIN_NONE)) {
279                 call_chain_strategy = MONO_PROFILER_CALL_CHAIN_NONE;
280         }
281         prof_list->statistical_call_chain_cb = callback;
282         prof_list->statistical_call_chain_depth = call_chain_depth;
283         prof_list->statistical_call_chain_strategy = call_chain_strategy;
284 }
285
286 int
287 mono_profiler_stat_get_call_chain_depth (void) {
288         if (prof_list && prof_list->statistical_call_chain_cb != NULL) {
289                 return prof_list->statistical_call_chain_depth;
290         } else {
291                 return 0;
292         }
293 }
294
295 MonoProfilerCallChainStrategy
296 mono_profiler_stat_get_call_chain_strategy (void) {
297         if (prof_list && prof_list->statistical_call_chain_cb != NULL) {
298                 return prof_list->statistical_call_chain_strategy;
299         } else {
300                 return MONO_PROFILER_CALL_CHAIN_NONE;
301         }
302 }
303
304 void mono_profiler_install_exception (MonoProfileExceptionFunc throw_callback, MonoProfileMethodFunc exc_method_leave, MonoProfileExceptionClauseFunc clause_callback)
305 {
306         if (!prof_list)
307                 return;
308         prof_list->exception_throw_cb = throw_callback;
309         prof_list->exception_method_leave_cb = exc_method_leave;
310         prof_list->exception_clause_cb = clause_callback;
311 }
312
313 void 
314 mono_profiler_install_coverage_filter (MonoProfileCoverageFilterFunc callback)
315 {
316         if (!prof_list)
317                 return;
318         prof_list->coverage_filter_cb = callback;
319 }
320
321 void 
322 mono_profiler_install_appdomain   (MonoProfileAppDomainFunc start_load, MonoProfileAppDomainResult end_load,
323                                    MonoProfileAppDomainFunc start_unload, MonoProfileAppDomainFunc end_unload)
324
325 {
326         if (!prof_list)
327                 return;
328         prof_list->domain_start_load = start_load;
329         prof_list->domain_end_load = end_load;
330         prof_list->domain_start_unload = start_unload;
331         prof_list->domain_end_unload = end_unload;
332 }
333
334 void 
335 mono_profiler_install_assembly    (MonoProfileAssemblyFunc start_load, MonoProfileAssemblyResult end_load,
336                                    MonoProfileAssemblyFunc start_unload, MonoProfileAssemblyFunc end_unload)
337 {
338         if (!prof_list)
339                 return;
340         prof_list->assembly_start_load = start_load;
341         prof_list->assembly_end_load = end_load;
342         prof_list->assembly_start_unload = start_unload;
343         prof_list->assembly_end_unload = end_unload;
344 }
345
346 void 
347 mono_profiler_install_module      (MonoProfileModuleFunc start_load, MonoProfileModuleResult end_load,
348                                    MonoProfileModuleFunc start_unload, MonoProfileModuleFunc end_unload)
349 {
350         if (!prof_list)
351                 return;
352         prof_list->module_start_load = start_load;
353         prof_list->module_end_load = end_load;
354         prof_list->module_start_unload = start_unload;
355         prof_list->module_end_unload = end_unload;
356 }
357
358 void
359 mono_profiler_install_class       (MonoProfileClassFunc start_load, MonoProfileClassResult end_load,
360                                    MonoProfileClassFunc start_unload, MonoProfileClassFunc end_unload)
361 {
362         if (!prof_list)
363                 return;
364         prof_list->class_start_load = start_load;
365         prof_list->class_end_load = end_load;
366         prof_list->class_start_unload = start_unload;
367         prof_list->class_end_unload = end_unload;
368 }
369
370 void
371 mono_profiler_method_enter (MonoMethod *method)
372 {
373         ProfilerDesc *prof;
374         for (prof = prof_list; prof; prof = prof->next) {
375                 if ((prof->events & MONO_PROFILE_ENTER_LEAVE) && prof->method_enter)
376                         prof->method_enter (prof->profiler, method);
377         }
378 }
379
380 void
381 mono_profiler_method_leave (MonoMethod *method)
382 {
383         ProfilerDesc *prof;
384         for (prof = prof_list; prof; prof = prof->next) {
385                 if ((prof->events & MONO_PROFILE_ENTER_LEAVE) && prof->method_leave)
386                         prof->method_leave (prof->profiler, method);
387         }
388 }
389
390 void 
391 mono_profiler_method_jit (MonoMethod *method)
392 {
393         ProfilerDesc *prof;
394         for (prof = prof_list; prof; prof = prof->next) {
395                 if ((prof->events & MONO_PROFILE_JIT_COMPILATION) && prof->jit_start)
396                         prof->jit_start (prof->profiler, method);
397         }
398 }
399
400 void 
401 mono_profiler_method_end_jit (MonoMethod *method, MonoJitInfo* jinfo, int result)
402 {
403         ProfilerDesc *prof;
404         for (prof = prof_list; prof; prof = prof->next) {
405                 if ((prof->events & MONO_PROFILE_JIT_COMPILATION)) {
406                         if (prof->jit_end)
407                                 prof->jit_end (prof->profiler, method, result);
408                         if (prof->jit_end2)
409                                 prof->jit_end2 (prof->profiler, method, jinfo, result);
410                 }
411         }
412 }
413
414 void 
415 mono_profiler_method_free (MonoMethod *method)
416 {
417         ProfilerDesc *prof;
418         for (prof = prof_list; prof; prof = prof->next) {
419                 if ((prof->events & MONO_PROFILE_METHOD_EVENTS) && prof->method_free)
420                         prof->method_free (prof->profiler, method);
421         }
422 }
423
424 void
425 mono_profiler_method_start_invoke (MonoMethod *method)
426 {
427         ProfilerDesc *prof;
428         for (prof = prof_list; prof; prof = prof->next) {
429                 if ((prof->events & MONO_PROFILE_METHOD_EVENTS) && prof->method_start_invoke)
430                         prof->method_start_invoke (prof->profiler, method);
431         }
432 }
433
434 void
435 mono_profiler_method_end_invoke (MonoMethod *method)
436 {
437         ProfilerDesc *prof;
438         for (prof = prof_list; prof; prof = prof->next) {
439                 if ((prof->events & MONO_PROFILE_METHOD_EVENTS) && prof->method_end_invoke)
440                         prof->method_end_invoke (prof->profiler, method);
441         }
442 }
443
444 void 
445 mono_profiler_code_transition (MonoMethod *method, int result)
446 {
447         ProfilerDesc *prof;
448         for (prof = prof_list; prof; prof = prof->next) {
449                 if ((prof->events & MONO_PROFILE_TRANSITIONS) && prof->man_unman_transition)
450                         prof->man_unman_transition (prof->profiler, method, result);
451         }
452 }
453
454 void 
455 mono_profiler_allocation (MonoObject *obj, MonoClass *klass)
456 {
457         ProfilerDesc *prof;
458         for (prof = prof_list; prof; prof = prof->next) {
459                 if ((prof->events & MONO_PROFILE_ALLOCATIONS) && prof->allocation_cb)
460                         prof->allocation_cb (prof->profiler, obj, klass);
461         }
462 }
463
464 void
465 mono_profiler_monitor_event      (MonoObject *obj, MonoProfilerMonitorEvent event) {
466         ProfilerDesc *prof;
467         for (prof = prof_list; prof; prof = prof->next) {
468                 if ((prof->events & MONO_PROFILE_MONITOR_EVENTS) && prof->monitor_event_cb)
469                         prof->monitor_event_cb (prof->profiler, obj, event);
470         }
471 }
472
473 void
474 mono_profiler_stat_hit (guchar *ip, void *context)
475 {
476         ProfilerDesc *prof;
477         for (prof = prof_list; prof; prof = prof->next) {
478                 if ((prof->events & MONO_PROFILE_STATISTICAL) && prof->statistical_cb)
479                         prof->statistical_cb (prof->profiler, ip, context);
480         }
481 }
482
483 void
484 mono_profiler_stat_call_chain (int call_chain_depth, guchar **ips, void *context)
485 {
486         ProfilerDesc *prof;
487         for (prof = prof_list; prof; prof = prof->next) {
488                 if ((prof->events & MONO_PROFILE_STATISTICAL) && prof->statistical_call_chain_cb)
489                         prof->statistical_call_chain_cb (prof->profiler, call_chain_depth, ips, context);
490         }
491 }
492
493 void
494 mono_profiler_exception_thrown (MonoObject *exception)
495 {
496         ProfilerDesc *prof;
497         for (prof = prof_list; prof; prof = prof->next) {
498                 if ((prof->events & MONO_PROFILE_EXCEPTIONS) && prof->exception_throw_cb)
499                         prof->exception_throw_cb (prof->profiler, exception);
500         }
501 }
502
503 void
504 mono_profiler_exception_method_leave (MonoMethod *method)
505 {
506         ProfilerDesc *prof;
507         for (prof = prof_list; prof; prof = prof->next) {
508                 if ((prof->events & MONO_PROFILE_EXCEPTIONS) && prof->exception_method_leave_cb)
509                         prof->exception_method_leave_cb (prof->profiler, method);
510         }
511 }
512
513 void
514 mono_profiler_exception_clause_handler (MonoMethod *method, int clause_type, int clause_num)
515 {
516         ProfilerDesc *prof;
517         for (prof = prof_list; prof; prof = prof->next) {
518                 if ((prof->events & MONO_PROFILE_EXCEPTIONS) && prof->exception_clause_cb)
519                         prof->exception_clause_cb (prof->profiler, method, clause_type, clause_num);
520         }
521 }
522
523 void
524 mono_profiler_thread_start (gsize tid)
525 {
526         ProfilerDesc *prof;
527         for (prof = prof_list; prof; prof = prof->next) {
528                 if ((prof->events & MONO_PROFILE_THREADS) && prof->thread_start)
529                         prof->thread_start (prof->profiler, tid);
530         }
531 }
532
533 void 
534 mono_profiler_thread_end (gsize tid)
535 {
536         ProfilerDesc *prof;
537         for (prof = prof_list; prof; prof = prof->next) {
538                 if ((prof->events & MONO_PROFILE_THREADS) && prof->thread_end)
539                         prof->thread_end (prof->profiler, tid);
540         }
541 }
542
543 void 
544 mono_profiler_assembly_event  (MonoAssembly *assembly, int code)
545 {
546         ProfilerDesc *prof;
547         for (prof = prof_list; prof; prof = prof->next) {
548                 if (!(prof->events & MONO_PROFILE_ASSEMBLY_EVENTS))
549                         continue;
550
551                 switch (code) {
552                 case MONO_PROFILE_START_LOAD:
553                         if (prof->assembly_start_load)
554                                 prof->assembly_start_load (prof->profiler, assembly);
555                         break;
556                 case MONO_PROFILE_START_UNLOAD:
557                         if (prof->assembly_start_unload)
558                                 prof->assembly_start_unload (prof->profiler, assembly);
559                         break;
560                 case MONO_PROFILE_END_UNLOAD:
561                         if (prof->assembly_end_unload)
562                                 prof->assembly_end_unload (prof->profiler, assembly);
563                         break;
564                 default:
565                         g_assert_not_reached ();
566                 }
567         }
568 }
569
570 void 
571 mono_profiler_assembly_loaded (MonoAssembly *assembly, int result)
572 {
573         ProfilerDesc *prof;
574         for (prof = prof_list; prof; prof = prof->next) {
575                 if ((prof->events & MONO_PROFILE_ASSEMBLY_EVENTS) && prof->assembly_end_load)
576                         prof->assembly_end_load (prof->profiler, assembly, result);
577         }
578 }
579
580 void mono_profiler_iomap (char *report, const char *pathname, const char *new_pathname)
581 {
582         ProfilerDesc *prof;
583         for (prof = prof_list; prof; prof = prof->next) {
584                 if ((prof->events & MONO_PROFILE_IOMAP_EVENTS) && prof->iomap_cb)
585                         prof->iomap_cb (prof->profiler, report, pathname, new_pathname);
586         }
587 }
588
589 void 
590 mono_profiler_module_event  (MonoImage *module, int code)
591 {
592         ProfilerDesc *prof;
593         for (prof = prof_list; prof; prof = prof->next) {
594                 if (!(prof->events & MONO_PROFILE_MODULE_EVENTS))
595                         continue;
596
597                 switch (code) {
598                 case MONO_PROFILE_START_LOAD:
599                         if (prof->module_start_load)
600                                 prof->module_start_load (prof->profiler, module);
601                         break;
602                 case MONO_PROFILE_START_UNLOAD:
603                         if (prof->module_start_unload)
604                                 prof->module_start_unload (prof->profiler, module);
605                         break;
606                 case MONO_PROFILE_END_UNLOAD:
607                         if (prof->module_end_unload)
608                                 prof->module_end_unload (prof->profiler, module);
609                         break;
610                 default:
611                         g_assert_not_reached ();
612                 }
613         }
614 }
615
616 void 
617 mono_profiler_module_loaded (MonoImage *module, int result)
618 {
619         ProfilerDesc *prof;
620         for (prof = prof_list; prof; prof = prof->next) {
621                 if ((prof->events & MONO_PROFILE_MODULE_EVENTS) && prof->module_end_load)
622                         prof->module_end_load (prof->profiler, module, result);
623         }
624 }
625
626 void 
627 mono_profiler_class_event  (MonoClass *klass, int code)
628 {
629         ProfilerDesc *prof;
630         for (prof = prof_list; prof; prof = prof->next) {
631                 if (!(prof->events & MONO_PROFILE_CLASS_EVENTS))
632                         continue;
633
634                 switch (code) {
635                 case MONO_PROFILE_START_LOAD:
636                         if (prof->class_start_load)
637                                 prof->class_start_load (prof->profiler, klass);
638                         break;
639                 case MONO_PROFILE_START_UNLOAD:
640                         if (prof->class_start_unload)
641                                 prof->class_start_unload (prof->profiler, klass);
642                         break;
643                 case MONO_PROFILE_END_UNLOAD:
644                         if (prof->class_end_unload)
645                                 prof->class_end_unload (prof->profiler, klass);
646                         break;
647                 default:
648                         g_assert_not_reached ();
649                 }
650         }
651 }
652
653 void 
654 mono_profiler_class_loaded (MonoClass *klass, int result)
655 {
656         ProfilerDesc *prof;
657         for (prof = prof_list; prof; prof = prof->next) {
658                 if ((prof->events & MONO_PROFILE_CLASS_EVENTS) && prof->class_end_load)
659                         prof->class_end_load (prof->profiler, klass, result);
660         }
661 }
662
663 void 
664 mono_profiler_appdomain_event  (MonoDomain *domain, int code)
665 {
666         ProfilerDesc *prof;
667         for (prof = prof_list; prof; prof = prof->next) {
668                 if (!(prof->events & MONO_PROFILE_APPDOMAIN_EVENTS))
669                         continue;
670
671                 switch (code) {
672                 case MONO_PROFILE_START_LOAD:
673                         if (prof->domain_start_load)
674                                 prof->domain_start_load (prof->profiler, domain);
675                         break;
676                 case MONO_PROFILE_START_UNLOAD:
677                         if (prof->domain_start_unload)
678                                 prof->domain_start_unload (prof->profiler, domain);
679                         break;
680                 case MONO_PROFILE_END_UNLOAD:
681                         if (prof->domain_end_unload)
682                                 prof->domain_end_unload (prof->profiler, domain);
683                         break;
684                 default:
685                         g_assert_not_reached ();
686                 }
687         }
688 }
689
690 void 
691 mono_profiler_appdomain_loaded (MonoDomain *domain, int result)
692 {
693         ProfilerDesc *prof;
694         for (prof = prof_list; prof; prof = prof->next) {
695                 if ((prof->events & MONO_PROFILE_APPDOMAIN_EVENTS) && prof->domain_end_load)
696                         prof->domain_end_load (prof->profiler, domain, result);
697         }
698 }
699
700 void 
701 mono_profiler_shutdown (void)
702 {
703         ProfilerDesc *prof;
704         for (prof = prof_list; prof; prof = prof->next) {
705                 if (prof->shutdown_callback)
706                         prof->shutdown_callback (prof->profiler);
707         }
708 }
709
710 void
711 mono_profiler_gc_heap_resize (gint64 new_size)
712 {
713         ProfilerDesc *prof;
714         for (prof = prof_list; prof; prof = prof->next) {
715                 if ((prof->events & MONO_PROFILE_GC) && prof->gc_heap_resize)
716                         prof->gc_heap_resize (prof->profiler, new_size);
717         }
718 }
719
720 void
721 mono_profiler_gc_event (MonoGCEvent event, int generation)
722 {
723         ProfilerDesc *prof;
724         for (prof = prof_list; prof; prof = prof->next) {
725                 if ((prof->events & MONO_PROFILE_GC) && prof->gc_event)
726                         prof->gc_event (prof->profiler, event, generation);
727         }
728 }
729
730 void
731 mono_profiler_gc_moves (void **objects, int num)
732 {
733         ProfilerDesc *prof;
734         for (prof = prof_list; prof; prof = prof->next) {
735                 if ((prof->events & MONO_PROFILE_GC_MOVES) && prof->gc_moves)
736                         prof->gc_moves (prof->profiler, objects, num);
737         }
738 }
739
740 void
741 mono_profiler_install_gc (MonoProfileGCFunc callback, MonoProfileGCResizeFunc heap_resize_callback)
742 {
743         mono_gc_enable_events ();
744         if (!prof_list)
745                 return;
746         prof_list->gc_event = callback;
747         prof_list->gc_heap_resize = heap_resize_callback;
748 }
749
750 /**
751  * mono_profiler_install_gc_moves:
752  * @callback: callback function
753  *
754  * Install the @callback function that the GC will call when moving objects.
755  * The callback receives an array of pointers and the number of elements
756  * in the array. Every even element in the array is the original object location
757  * and the following odd element is the new location of the object in memory.
758  * So the number of elements argument will always be a multiple of 2.
759  * Since this callback happens during the GC, it is a restricted environment:
760  * no locks can be taken and the object pointers can be inspected only once
761  * the GC is finished (of course the original location pointers will not
762  * point to valid objects anymore).
763  */
764 void
765 mono_profiler_install_gc_moves (MonoProfileGCMoveFunc callback)
766 {
767         if (!prof_list)
768                 return;
769         prof_list->gc_moves = callback;
770 }
771
772 void
773 mono_profiler_install_runtime_initialized (MonoProfileFunc runtime_initialized_callback)
774 {
775         if (!prof_list)
776                 return;
777         prof_list->runtime_initialized_event = runtime_initialized_callback;
778 }
779
780 void
781 mono_profiler_runtime_initialized (void) {
782         ProfilerDesc *prof;
783         for (prof = prof_list; prof; prof = prof->next) {
784                 if (prof->runtime_initialized_event)
785                         prof->runtime_initialized_event (prof->profiler);
786         }
787 }
788
789 void
790 mono_profiler_install_code_chunk_new (MonoProfilerCodeChunkNew callback) {
791         if (!prof_list)
792                 return;
793         prof_list->code_chunk_new = callback;
794 }
795 void
796 mono_profiler_code_chunk_new (gpointer chunk, int size) {
797         ProfilerDesc *prof;
798         for (prof = prof_list; prof; prof = prof->next) {
799                 if (prof->code_chunk_new)
800                         prof->code_chunk_new (prof->profiler, chunk, size);
801         }
802 }
803
804 void
805 mono_profiler_install_code_chunk_destroy (MonoProfilerCodeChunkDestroy callback) {
806         if (!prof_list)
807                 return;
808         prof_list->code_chunk_destroy = callback;
809 }
810 void
811 mono_profiler_code_chunk_destroy (gpointer chunk) {
812         ProfilerDesc *prof;
813         for (prof = prof_list; prof; prof = prof->next) {
814                 if (prof->code_chunk_destroy)
815                         prof->code_chunk_destroy (prof->profiler, chunk);
816         }
817 }
818
819 void
820 mono_profiler_install_code_buffer_new (MonoProfilerCodeBufferNew callback) {
821         if (!prof_list)
822                 return;
823         prof_list->code_buffer_new = callback;
824 }
825
826 void
827 mono_profiler_install_iomap (MonoProfileIomapFunc callback)
828 {
829         if (!prof_list)
830                 return;
831         prof_list->iomap_cb = callback;
832 }
833
834 void
835 mono_profiler_code_buffer_new (gpointer buffer, int size, MonoProfilerCodeBufferType type, void *data) {
836         ProfilerDesc *prof;
837         for (prof = prof_list; prof; prof = prof->next) {
838                 if (prof->code_buffer_new)
839                         prof->code_buffer_new (prof->profiler, buffer, size, type, data);
840         }
841 }
842
843 static GHashTable *coverage_hash = NULL;
844
845 MonoProfileCoverageInfo* 
846 mono_profiler_coverage_alloc (MonoMethod *method, int entries)
847 {
848         MonoProfileCoverageInfo *res;
849         int instrument = FALSE;
850         ProfilerDesc *prof;
851
852         for (prof = prof_list; prof; prof = prof->next) {
853                 /* note that we call the filter on all the profilers even if just
854                  * a single one would be enough to instrument a method
855                  */
856                 if (prof->coverage_filter_cb)
857                         if (prof->coverage_filter_cb (prof->profiler, method))
858                                 instrument = TRUE;
859         }
860         if (!instrument)
861                 return NULL;
862
863         mono_profiler_coverage_lock ();
864         if (!coverage_hash)
865                 coverage_hash = g_hash_table_new (NULL, NULL);
866
867         res = g_malloc0 (sizeof (MonoProfileCoverageInfo) + sizeof (void*) * 2 * entries);
868
869         res->entries = entries;
870
871         g_hash_table_insert (coverage_hash, method, res);
872         mono_profiler_coverage_unlock ();
873
874         return res;
875 }
876
877 /* safe only when the method antive code has been unloaded */
878 void
879 mono_profiler_coverage_free (MonoMethod *method)
880 {
881         MonoProfileCoverageInfo* info;
882
883         mono_profiler_coverage_lock ();
884         if (!coverage_hash) {
885                 mono_profiler_coverage_unlock ();
886                 return;
887         }
888
889         info = g_hash_table_lookup (coverage_hash, method);
890         if (info) {
891                 g_free (info);
892                 g_hash_table_remove (coverage_hash, method);
893         }
894         mono_profiler_coverage_unlock ();
895 }
896
897 /**
898  * mono_profiler_coverage_get:
899  * @prof: The profiler handle, installed with mono_profiler_install
900  * @method: the method to gather information from.
901  * @func: A routine that will be called back with the results
902  *
903  * If the MONO_PROFILER_INS_COVERAGE flag was active during JIT compilation
904  * it is posisble to obtain coverage information about a give method.
905  *
906  * The function @func will be invoked repeatedly with instances of the
907  * MonoProfileCoverageEntry structure.
908  */
909 void 
910 mono_profiler_coverage_get (MonoProfiler *prof, MonoMethod *method, MonoProfileCoverageFunc func)
911 {
912         MonoProfileCoverageInfo* info;
913         int i, offset;
914         guint32 code_size;
915         const unsigned char *start, *end, *cil_code;
916         MonoMethodHeader *header;
917         MonoProfileCoverageEntry entry;
918         MonoDebugMethodInfo *debug_minfo;
919
920         mono_profiler_coverage_lock ();
921         info = g_hash_table_lookup (coverage_hash, method);
922         mono_profiler_coverage_unlock ();
923
924         if (!info)
925                 return;
926
927         header = mono_method_get_header (method);
928         start = mono_method_header_get_code (header, &code_size, NULL);
929         debug_minfo = mono_debug_lookup_method (method);
930
931         end = start + code_size;
932         for (i = 0; i < info->entries; ++i) {
933                 cil_code = info->data [i].cil_code;
934                 if (cil_code && cil_code >= start && cil_code < end) {
935                         char *fname = NULL;
936                         offset = cil_code - start;
937                         entry.iloffset = offset;
938                         entry.method = method;
939                         entry.counter = info->data [i].count;
940                         entry.line = entry.col = 1;
941                         entry.filename = NULL;
942                         if (debug_minfo) {
943                                 MonoDebugSourceLocation *location;
944
945                                 location = mono_debug_symfile_lookup_location (debug_minfo, offset);
946                                 if (location) {
947                                         entry.line = location->row;
948                                         entry.col = location->column;
949                                         entry.filename = fname = g_strdup (location->source_file);
950                                         mono_debug_free_source_location (location);
951                                 }
952                         }
953
954                         func (prof, &entry);
955                         g_free (fname);
956                 }
957         }
958         mono_metadata_free_mh (header);
959 }
960
961 #ifndef DISABLE_PROFILER
962 /*
963  * Small profiler extracted from mint: we should move it in a loadable module
964  * and improve it to do graphs and more accurate timestamping with rdtsc.
965  */
966
967 static FILE* poutput = NULL;
968
969 #define USE_X86TSC 0
970 #define USE_WIN32COUNTER 0
971 #if USE_X86TSC
972
973 typedef struct {
974         unsigned int lows, highs, lowe, highe;
975 } MonoRdtscTimer;
976
977 #define rdtsc(low,high) \
978         __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
979
980 static int freq;
981
982 static double
983 rdtsc_elapsed (MonoRdtscTimer *t)
984 {
985         unsigned long long diff;
986         unsigned int highe = t->highe;
987         if (t->lowe < t->lows)
988                 highe--;
989         diff = (((unsigned long long) highe - t->highs) << 32) + (t->lowe - t->lows);
990         return ((double)diff / freq) / 1000000; /* have to return the result in seconds */
991 }
992
993 static int 
994 have_rdtsc (void) {
995         char buf[256];
996         int have_freq = 0;
997         int have_flag = 0;
998         float val;
999         FILE *cpuinfo;
1000
1001         if (!(cpuinfo = fopen ("/proc/cpuinfo", "r")))
1002                 return 0;
1003         while (fgets (buf, sizeof(buf), cpuinfo)) {
1004                 if (sscanf (buf, "cpu MHz : %f", &val) == 1) {
1005                         /*printf ("got mh: %f\n", val);*/
1006                         have_freq = val;
1007                 }
1008                 if (strncmp (buf, "flags", 5) == 0) {
1009                         if (strstr (buf, "tsc")) {
1010                                 have_flag = 1;
1011                                 /*printf ("have tsc\n");*/
1012                         }
1013                 }
1014         }
1015         fclose (cpuinfo);
1016         return have_flag? have_freq: 0;
1017 }
1018
1019 #define MONO_TIMER_STARTUP      \
1020         if (!(freq = have_rdtsc ())) g_error ("Compiled with TSC support, but none found");
1021 #define MONO_TIMER_TYPE  MonoRdtscTimer
1022 #define MONO_TIMER_INIT(t)
1023 #define MONO_TIMER_DESTROY(t)
1024 #define MONO_TIMER_START(t) rdtsc ((t).lows, (t).highs);
1025 #define MONO_TIMER_STOP(t) rdtsc ((t).lowe, (t).highe);
1026 #define MONO_TIMER_ELAPSED(t) rdtsc_elapsed (&(t))
1027
1028 #elif USE_WIN32COUNTER
1029 #include <windows.h>
1030
1031 typedef struct {
1032         LARGE_INTEGER start, stop;
1033 } MonoWin32Timer;
1034
1035 static int freq;
1036
1037 static double
1038 win32_elapsed (MonoWin32Timer *t)
1039 {
1040         LONGLONG diff = t->stop.QuadPart - t->start.QuadPart;
1041         return ((double)diff / freq) / 1000000; /* have to return the result in seconds */
1042 }
1043
1044 static int 
1045 have_win32counter (void) {
1046         LARGE_INTEGER f;
1047
1048         if (!QueryPerformanceFrequency (&f))
1049                 return 0;
1050         return f.LowPart;
1051 }
1052
1053 #define MONO_TIMER_STARTUP      \
1054         if (!(freq = have_win32counter ())) g_error ("Compiled with Win32 counter support, but none found");
1055 #define MONO_TIMER_TYPE  MonoWin32Timer
1056 #define MONO_TIMER_INIT(t)
1057 #define MONO_TIMER_DESTROY(t)
1058 #define MONO_TIMER_START(t) QueryPerformanceCounter (&(t).start)
1059 #define MONO_TIMER_STOP(t) QueryPerformanceCounter (&(t).stop)
1060 #define MONO_TIMER_ELAPSED(t) win32_elapsed (&(t))
1061
1062 #else
1063
1064 typedef struct {
1065         GTimeVal start, stop;
1066 } MonoGLibTimer;
1067
1068 static double
1069 timeval_elapsed (MonoGLibTimer *t)
1070 {
1071         if (t->start.tv_usec > t->stop.tv_usec) {
1072                 t->stop.tv_usec += G_USEC_PER_SEC;
1073                 t->stop.tv_sec--;
1074         }
1075         return (t->stop.tv_sec - t->start.tv_sec) 
1076                 + ((double)(t->stop.tv_usec - t->start.tv_usec))/ G_USEC_PER_SEC;
1077 }
1078
1079 #define MONO_TIMER_STARTUP
1080 #define MONO_TIMER_TYPE MonoGLibTimer
1081 #define MONO_TIMER_INIT(t)
1082 #define MONO_TIMER_DESTROY(t)
1083 #define MONO_TIMER_START(t) g_get_current_time (&(t).start)
1084 #define MONO_TIMER_STOP(t) g_get_current_time (&(t).stop)
1085 #define MONO_TIMER_ELAPSED(t) timeval_elapsed (&(t))
1086 #endif
1087
1088 typedef struct _AllocInfo AllocInfo;
1089 typedef struct _CallerInfo CallerInfo;
1090 typedef struct _LastCallerInfo LastCallerInfo;
1091
1092 struct _MonoProfiler {
1093         GHashTable *methods;
1094         MonoMemPool *mempool;
1095         GSList *domains;
1096         /* info about JIT time */
1097         MONO_TIMER_TYPE jit_timer;
1098         double      jit_time;
1099         double      max_jit_time;
1100         MonoMethod *max_jit_method;
1101         int         methods_jitted;
1102         
1103         GSList     *per_thread;
1104         
1105         /* chain of callers for the current thread */
1106         LastCallerInfo *callers;
1107         /* LastCallerInfo nodes for faster allocation */
1108         LastCallerInfo *cstorage;
1109 };
1110
1111 typedef struct {
1112         MonoMethod *method;
1113         guint64 count;
1114         double total;
1115         AllocInfo *alloc_info;
1116         CallerInfo *caller_info;
1117 } MethodProfile;
1118
1119 typedef struct _MethodCallProfile MethodCallProfile;
1120
1121 struct _MethodCallProfile {
1122         MethodCallProfile *next;
1123         MONO_TIMER_TYPE timer;
1124         MonoMethod *method;
1125 };
1126
1127 struct _AllocInfo {
1128         AllocInfo *next;
1129         MonoClass *klass;
1130         guint64 count;
1131         guint64 mem;
1132 };
1133
1134 struct _CallerInfo {
1135         CallerInfo *next;
1136         MonoMethod *caller;
1137         guint count;
1138 };
1139
1140 struct _LastCallerInfo {
1141         LastCallerInfo *next;
1142         MonoMethod *method;
1143         MONO_TIMER_TYPE timer;
1144 };
1145
1146 static MonoProfiler*
1147 create_profiler (void)
1148 {
1149         MonoProfiler *prof = g_new0 (MonoProfiler, 1);
1150
1151         prof->methods = g_hash_table_new (mono_aligned_addr_hash, NULL);
1152         MONO_TIMER_INIT (prof->jit_timer);
1153         prof->mempool = mono_mempool_new ();
1154         return prof;
1155 }
1156 #if 1
1157
1158 #ifdef HAVE_KW_THREAD
1159         static __thread MonoProfiler * tls_profiler;
1160 #       define GET_PROFILER() tls_profiler
1161 #       define SET_PROFILER(x) tls_profiler = (x)
1162 #       define ALLOC_PROFILER() /* nop */
1163 #else
1164         static guint32 profiler_thread_id = -1;
1165 #       define GET_PROFILER() ((MonoProfiler *)TlsGetValue (profiler_thread_id))
1166 #       define SET_PROFILER(x) TlsSetValue (profiler_thread_id, x);
1167 #       define ALLOC_PROFILER() profiler_thread_id = TlsAlloc ()
1168 #endif
1169
1170 #define GET_THREAD_PROF(prof) do {                                                           \
1171                 MonoProfiler *_tprofiler = GET_PROFILER ();                                  \
1172                 if (!_tprofiler) {                                                           \
1173                         _tprofiler = create_profiler ();                                     \
1174                         prof->per_thread = g_slist_prepend (prof->per_thread, _tprofiler);   \
1175                         SET_PROFILER (_tprofiler);                                           \
1176                 }                                                                            \
1177                 prof = _tprofiler;                                                           \
1178         } while (0)
1179 #else
1180 /* thread unsafe but faster variant */
1181 #define GET_THREAD_PROF(prof)
1182 #endif
1183
1184 static gint
1185 compare_profile (MethodProfile *profa, MethodProfile *profb)
1186 {
1187         return (gint)((profb->total - profa->total)*1000);
1188 }
1189
1190 static void
1191 build_profile (MonoMethod *m, MethodProfile *prof, GList **funcs)
1192 {
1193         prof->method = m;
1194         *funcs = g_list_insert_sorted (*funcs, prof, (GCompareFunc)compare_profile);
1195 }
1196
1197 static char*
1198 method_get_name (MonoMethod* method)
1199 {
1200         char *sig, *res;
1201         
1202         sig = mono_signature_get_desc (mono_method_signature (method), FALSE);
1203         res = g_strdup_printf ("%s%s%s::%s(%s)", method->klass->name_space,
1204                         method->klass->name_space ? "." : "", method->klass->name,
1205                 method->name, sig);
1206         g_free (sig);
1207         return res;
1208 }
1209
1210 static void output_callers (MethodProfile *p);
1211
1212 /* This isn't defined on older glib versions and on some platforms */
1213 #ifndef G_GUINT64_FORMAT
1214 #define G_GUINT64_FORMAT "ul"
1215 #endif
1216 #ifndef G_GINT64_FORMAT
1217 #define G_GINT64_FORMAT "lld"
1218 #endif
1219
1220 static void
1221 output_profile (GList *funcs)
1222 {
1223         GList *tmp;
1224         MethodProfile *p;
1225         char *m;
1226         guint64 total_calls = 0;
1227
1228         if (funcs)
1229                 fprintf (poutput, "Time(ms) Count   P/call(ms) Method name\n");
1230         for (tmp = funcs; tmp; tmp = tmp->next) {
1231                 p = tmp->data;
1232                 total_calls += p->count;
1233                 if (!(gint)(p->total*1000))
1234                         continue;
1235                 m = method_get_name (p->method);
1236                 fprintf (poutput, "########################\n");
1237                 fprintf (poutput, "% 8.3f ", (double) (p->total * 1000));
1238                 fprintf (poutput, "%7" G_GUINT64_FORMAT " ", (guint64)p->count);
1239                 fprintf (poutput, "% 8.3f ", (double) (p->total * 1000)/(double)p->count);
1240                 fprintf (poutput, "  %s\n", m);
1241
1242                 g_free (m);
1243                 /* callers */
1244                 output_callers (p);
1245         }
1246         fprintf (poutput, "Total number of calls: %" G_GINT64_FORMAT "\n", (gint64)total_calls);
1247 }
1248
1249 typedef struct {
1250         MethodProfile *mp;
1251         guint64 count;
1252 } NewobjProfile;
1253
1254 static gint
1255 compare_newobj_profile (NewobjProfile *profa, NewobjProfile *profb)
1256 {
1257         if (profb->count == profa->count)
1258                 return 0;
1259         else
1260                 return profb->count > profa->count ? 1 : -1;
1261 }
1262
1263 static void
1264 build_newobj_profile (MonoClass *class, MethodProfile *mprof, GList **funcs)
1265 {
1266         NewobjProfile *prof = g_new (NewobjProfile, 1);
1267         AllocInfo *tmp;
1268         guint64 count = 0;
1269         
1270         prof->mp = mprof;
1271         /* we use the total amount of memory to sort */
1272         for (tmp = mprof->alloc_info; tmp; tmp = tmp->next)
1273                 count += tmp->mem;
1274         prof->count = count;
1275         *funcs = g_list_insert_sorted (*funcs, prof, (GCompareFunc)compare_newobj_profile);
1276 }
1277
1278 static int
1279 compare_caller (CallerInfo *a, CallerInfo *b)
1280 {
1281         return b->count - a->count;
1282 }
1283
1284 static int
1285 compare_alloc (AllocInfo *a, AllocInfo *b)
1286 {
1287         return b->mem - a->mem;
1288 }
1289
1290 static GSList*
1291 sort_alloc_list (AllocInfo *ai)
1292 {
1293         GSList *l = NULL;
1294         AllocInfo *tmp;
1295         for (tmp = ai; tmp; tmp = tmp->next) {
1296                 l = g_slist_insert_sorted (l, tmp, (GCompareFunc)compare_alloc);
1297         }
1298         return l;
1299 }
1300
1301 static GSList*
1302 sort_caller_list (CallerInfo *ai)
1303 {
1304         GSList *l = NULL;
1305         CallerInfo *tmp;
1306         for (tmp = ai; tmp; tmp = tmp->next) {
1307                 l = g_slist_insert_sorted (l, tmp, (GCompareFunc)compare_caller);
1308         }
1309         return l;
1310 }
1311
1312 static void
1313 output_callers (MethodProfile *p) {
1314         guint total_callers, percent;
1315         GSList *sorted, *tmps;
1316         CallerInfo *cinfo;
1317         char *m;
1318         
1319         fprintf (poutput, "  Callers (with count) that contribute at least for 1%%:\n");
1320         total_callers = 0;
1321         for (cinfo = p->caller_info; cinfo; cinfo = cinfo->next) {
1322                 total_callers += cinfo->count;
1323         }
1324         sorted = sort_caller_list (p->caller_info);
1325         for (tmps = sorted; tmps; tmps = tmps->next) {
1326                 cinfo = tmps->data;
1327                 percent = (cinfo->count * 100)/total_callers;
1328                 if (percent < 1)
1329                         continue;
1330                 m = method_get_name (cinfo->caller);
1331                 fprintf (poutput, "    %8d % 3d %% %s\n", cinfo->count, percent, m);
1332                 g_free (m);
1333         }
1334 }
1335
1336 static int moved_objects = 0;
1337 static void
1338 simple_gc_move (MonoProfiler *prof, void **objects, int num)
1339 {
1340         moved_objects += num / 2;
1341 }
1342
1343 static void
1344 output_newobj_profile (GList *proflist)
1345 {
1346         GList *tmp;
1347         NewobjProfile *p;
1348         MethodProfile *mp;
1349         AllocInfo *ainfo;
1350         MonoClass *klass;
1351         const char* isarray;
1352         char buf [256];
1353         char *m;
1354         guint64 total = 0;
1355         GSList *sorted, *tmps;
1356
1357         fprintf (poutput, "\nAllocation profiler\n");
1358
1359         if (proflist)
1360                 fprintf (poutput, "%-9s %s\n", "Total mem", "Method");
1361         for (tmp = proflist; tmp; tmp = tmp->next) {
1362                 p = tmp->data;
1363                 total += p->count;
1364                 if (p->count < 50000)
1365                         continue;
1366                 mp = p->mp;
1367                 m = method_get_name (mp->method);
1368                 fprintf (poutput, "########################\n%8" G_GUINT64_FORMAT " KB %s\n", (p->count / 1024), m);
1369                 g_free (m);
1370                 sorted = sort_alloc_list (mp->alloc_info);
1371                 for (tmps = sorted; tmps; tmps = tmps->next) {
1372                         ainfo = tmps->data;
1373                         if (ainfo->mem < 50000)
1374                                 continue;
1375                         klass = ainfo->klass;
1376                         if (klass->rank) {
1377                                 isarray = "[]";
1378                                 klass = klass->element_class;
1379                         } else {
1380                                 isarray = "";
1381                         }
1382                         g_snprintf (buf, sizeof (buf), "%s%s%s%s",
1383                                 klass->name_space, klass->name_space ? "." : "", klass->name, isarray);
1384                         fprintf (poutput, "    %8" G_GUINT64_FORMAT " KB %8" G_GUINT64_FORMAT " %-48s\n", (ainfo->mem / 1024), ainfo->count, buf);
1385                 }
1386                 /* callers */
1387                 output_callers (mp);
1388         }
1389         fprintf (poutput, "Total memory allocated: %" G_GUINT64_FORMAT " KB\n", total / 1024);
1390         fprintf (poutput, "Objects copied: %d\n", moved_objects);
1391 }
1392
1393 static void
1394 merge_methods (MonoMethod *method, MethodProfile *profile, MonoProfiler *prof)
1395 {
1396         MethodProfile *mprof;
1397         AllocInfo *talloc_info, *alloc_info;
1398         CallerInfo *tcaller_info, *caller_info;
1399
1400         mprof = g_hash_table_lookup (prof->methods, method);
1401         if (!mprof) {
1402                 /* the master thread didn't see this method, just transfer the info as is */
1403                 g_hash_table_insert (prof->methods, method, profile);
1404                 return;
1405         }
1406         /* merge the info from profile into mprof */
1407         mprof->count += profile->count;
1408         mprof->total += profile->total;
1409         /* merge alloc info */
1410         for (talloc_info = profile->alloc_info; talloc_info; talloc_info = talloc_info->next) {
1411                 for (alloc_info = mprof->alloc_info; alloc_info; alloc_info = alloc_info->next) {
1412                         if (alloc_info->klass == talloc_info->klass) {
1413                                 /* mprof already has a record for the klass, merge */
1414                                 alloc_info->count += talloc_info->count;
1415                                 alloc_info->mem += talloc_info->mem;
1416                                 break;
1417                         }
1418                 }
1419                 if (!alloc_info) {
1420                         /* mprof didn't have the info, just copy it over */
1421                         alloc_info = mono_mempool_alloc0 (prof->mempool, sizeof (AllocInfo));
1422                         *alloc_info = *talloc_info;
1423                         alloc_info->next = mprof->alloc_info;
1424                         mprof->alloc_info = alloc_info->next;
1425                 }
1426         }
1427         /* merge callers info */
1428         for (tcaller_info = profile->caller_info; tcaller_info; tcaller_info = tcaller_info->next) {
1429                 for (caller_info = mprof->caller_info; caller_info; caller_info = caller_info->next) {
1430                         if (caller_info->caller == tcaller_info->caller) {
1431                                 /* mprof already has a record for the caller method, merge */
1432                                 caller_info->count += tcaller_info->count;
1433                                 break;
1434                         }
1435                 }
1436                 if (!caller_info) {
1437                         /* mprof didn't have the info, just copy it over */
1438                         caller_info = mono_mempool_alloc0 (prof->mempool, sizeof (CallerInfo));
1439                         *caller_info = *tcaller_info;
1440                         caller_info->next = mprof->caller_info;
1441                         mprof->caller_info = caller_info;
1442                 }
1443         }
1444 }
1445
1446 static void
1447 merge_thread_data (MonoProfiler *master, MonoProfiler *tprof)
1448 {
1449         master->jit_time += tprof->jit_time;
1450         master->methods_jitted += tprof->methods_jitted;
1451         if (master->max_jit_time < tprof->max_jit_time) {
1452                 master->max_jit_time = tprof->max_jit_time;
1453                 master->max_jit_method = tprof->max_jit_method;
1454         }
1455
1456         g_hash_table_foreach (tprof->methods, (GHFunc)merge_methods, master);
1457 }
1458
1459 static void
1460 simple_method_enter (MonoProfiler *prof, MonoMethod *method)
1461 {
1462         MethodProfile *profile_info;
1463         LastCallerInfo *callinfo;
1464         GET_THREAD_PROF (prof);
1465         /*g_print ("enter %p %s::%s in %d (%p)\n", method, method->klass->name, method->name, GetCurrentThreadId (), prof);*/
1466         if (!(profile_info = g_hash_table_lookup (prof->methods, method))) {
1467                 profile_info = mono_mempool_alloc0 (prof->mempool, sizeof (MethodProfile));
1468                 MONO_TIMER_INIT (profile_info->u.timer);
1469                 g_hash_table_insert (prof->methods, method, profile_info);
1470         }
1471         profile_info->count++;
1472         if (prof->callers) {
1473                 CallerInfo *cinfo;
1474                 MonoMethod *caller = prof->callers->method;
1475                 for (cinfo = profile_info->caller_info; cinfo; cinfo = cinfo->next) {
1476                         if (cinfo->caller == caller)
1477                                 break;
1478                 }
1479                 if (!cinfo) {
1480                         cinfo = mono_mempool_alloc0 (prof->mempool, sizeof (CallerInfo));
1481                         cinfo->caller = caller;
1482                         cinfo->next = profile_info->caller_info;
1483                         profile_info->caller_info = cinfo;
1484                 }
1485                 cinfo->count++;
1486         }
1487         if (!(callinfo = prof->cstorage)) {
1488                 callinfo = mono_mempool_alloc (prof->mempool, sizeof (LastCallerInfo));
1489                 MONO_TIMER_INIT (callinfo->timer);
1490         } else {
1491                 prof->cstorage = prof->cstorage->next;
1492         }
1493         callinfo->method = method;
1494         callinfo->next = prof->callers;
1495         prof->callers = callinfo;
1496         MONO_TIMER_START (callinfo->timer);
1497 }
1498
1499 static void
1500 simple_method_leave (MonoProfiler *prof, MonoMethod *method)
1501 {
1502         MethodProfile *profile_info;
1503         LastCallerInfo *callinfo, *newcallinfo = NULL;
1504         
1505         GET_THREAD_PROF (prof);
1506         /*g_print ("leave %p %s::%s in %d (%p)\n", method, method->klass->name, method->name, GetCurrentThreadId (), prof);*/
1507         callinfo = prof->callers;
1508         /* should really not happen, but we don't catch exceptions events, yet ... */
1509         while (callinfo) {
1510                 MONO_TIMER_STOP (callinfo->timer);
1511                 profile_info = g_hash_table_lookup (prof->methods, callinfo->method);
1512                 if (profile_info)
1513                         profile_info->total += MONO_TIMER_ELAPSED (callinfo->timer);
1514                 newcallinfo = callinfo->next;
1515                 callinfo->next = prof->cstorage;
1516                 prof->cstorage = callinfo;
1517                 if (callinfo->method == method)
1518                         break;
1519                 callinfo = newcallinfo;
1520         }
1521         prof->callers = newcallinfo;
1522 }
1523
1524 static void
1525 simple_allocation (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
1526 {
1527         MethodProfile *profile_info;
1528         AllocInfo *tmp;
1529
1530         GET_THREAD_PROF (prof);
1531         if (prof->callers) {
1532                 MonoMethod *caller = prof->callers->method;
1533
1534                 /* Otherwise all allocations are attributed to icall_wrapper_mono_object_new */
1535                 if (caller->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE && prof->callers->next)
1536                         caller = prof->callers->next->method;
1537
1538                 if (!(profile_info = g_hash_table_lookup (prof->methods, caller)))
1539                         g_assert_not_reached ();
1540         } else {
1541                 return; /* fine for now */
1542         }
1543
1544         for (tmp = profile_info->alloc_info; tmp; tmp = tmp->next) {
1545                 if (tmp->klass == klass)
1546                         break;
1547         }
1548         if (!tmp) {
1549                 tmp = mono_mempool_alloc0 (prof->mempool, sizeof (AllocInfo));
1550                 tmp->klass = klass;
1551                 tmp->next = profile_info->alloc_info;
1552                 profile_info->alloc_info = tmp;
1553         }
1554         tmp->count++;
1555         tmp->mem += mono_object_get_size (obj);
1556 }
1557
1558 static void
1559 simple_method_jit (MonoProfiler *prof, MonoMethod *method)
1560 {
1561         GET_THREAD_PROF (prof);
1562         prof->methods_jitted++;
1563         MONO_TIMER_START (prof->jit_timer);
1564 }
1565
1566 static void
1567 simple_method_end_jit (MonoProfiler *prof, MonoMethod *method, int result)
1568 {
1569         double jtime;
1570         GET_THREAD_PROF (prof);
1571         MONO_TIMER_STOP (prof->jit_timer);
1572         jtime = MONO_TIMER_ELAPSED (prof->jit_timer);
1573         prof->jit_time += jtime;
1574         if (jtime > prof->max_jit_time) {
1575                 prof->max_jit_time = jtime;
1576                 prof->max_jit_method = method;
1577         }
1578 }
1579
1580 /* about 10 minutes of samples */
1581 #define MAX_PROF_SAMPLES (1000*60*10)
1582 static int prof_counts = 0;
1583 static int prof_ucounts = 0;
1584 static gpointer* prof_addresses = NULL;
1585 static GHashTable *prof_table = NULL;
1586
1587 static void
1588 simple_stat_hit (MonoProfiler *prof, guchar *ip, void *context)
1589 {
1590         int pos;
1591
1592         if (prof_counts >= MAX_PROF_SAMPLES)
1593                 return;
1594         pos = InterlockedIncrement (&prof_counts);
1595         prof_addresses [pos - 1] = ip;
1596 }
1597
1598 static int
1599 compare_methods_prof (gconstpointer a, gconstpointer b)
1600 {
1601         int ca = GPOINTER_TO_UINT (g_hash_table_lookup (prof_table, a));
1602         int cb = GPOINTER_TO_UINT (g_hash_table_lookup (prof_table, b));
1603         return cb-ca;
1604 }
1605
1606 static void
1607 prof_foreach (char *method, gpointer c, gpointer data)
1608 {
1609         GList **list = data;
1610         *list = g_list_insert_sorted (*list, method, compare_methods_prof);
1611 }
1612
1613 typedef struct Addr2LineData Addr2LineData;
1614
1615 struct Addr2LineData {
1616         Addr2LineData *next;
1617         FILE *pipein;
1618         FILE *pipeout;
1619         char *binary;
1620         int child_pid;
1621 };
1622
1623 static Addr2LineData *addr2line_pipes = NULL;
1624
1625 static char*
1626 try_addr2line (const char* binary, gpointer ip)
1627 {
1628         char buf [1024];
1629         char *res;
1630         Addr2LineData *addr2line;
1631
1632         for (addr2line = addr2line_pipes; addr2line; addr2line = addr2line->next) {
1633                 if (strcmp (binary, addr2line->binary) == 0)
1634                         break;
1635         }
1636         if (!addr2line) {
1637                 const char *addr_argv[] = {"addr2line", "-f", "-e", binary, NULL};
1638                 int child_pid;
1639                 int ch_in, ch_out;
1640 #ifdef __linux__
1641                 char monobin [1024];
1642                 /* non-linux platforms will need different code here */
1643                 if (strcmp (binary, "mono") == 0) {
1644                         int count = readlink ("/proc/self/exe", monobin, sizeof (monobin));
1645                         if (count >= 0 && count < sizeof (monobin)) {
1646                                 monobin [count] = 0;
1647                                 addr_argv [3] = monobin;
1648                         }
1649                 }
1650 #endif
1651                 if (!g_spawn_async_with_pipes (NULL, (char**)addr_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
1652                                 &child_pid, &ch_in, &ch_out, NULL, NULL)) {
1653                         return g_strdup (binary);
1654                 }
1655                 addr2line = g_new0 (Addr2LineData, 1);
1656                 addr2line->child_pid = child_pid;
1657                 addr2line->binary = g_strdup (binary);
1658                 addr2line->pipein = fdopen (ch_in, "w");
1659                 addr2line->pipeout = fdopen (ch_out, "r");
1660                 addr2line->next = addr2line_pipes;
1661                 addr2line_pipes = addr2line;
1662         }
1663         fprintf (addr2line->pipein, "%p\n", ip);
1664         fflush (addr2line->pipein);
1665         /* we first get the func name and then file:lineno in a second line */
1666         if (fgets (buf, sizeof (buf), addr2line->pipeout) && buf [0] != '?') {
1667                 char *end = strchr (buf, '\n');
1668                 if (end)
1669                         *end = 0;
1670                 res = g_strdup_printf ("%s(%s", binary, buf);
1671                 /* discard the filename/line info */
1672                 fgets (buf, sizeof (buf), addr2line->pipeout);
1673         } else {
1674                 res = g_strdup (binary);
1675         }
1676         return res;
1677 }
1678
1679 static void
1680 stat_prof_report (MonoProfiler *prof)
1681 {
1682         MonoJitInfo *ji;
1683         int count = prof_counts;
1684         int i, c;
1685         char *mn;
1686         gpointer ip;
1687         GList *tmp, *sorted = NULL;
1688         GSList *l;
1689         int pcount = ++ prof_counts;
1690
1691         prof_counts = MAX_PROF_SAMPLES;
1692         for (i = 0; i < count; ++i) {
1693                 ip = prof_addresses [i];
1694                 ji = mono_jit_info_table_find (mono_domain_get (), ip);
1695
1696                 if (!ji) {
1697                         for (l = prof->domains; l && !ji; l = l->next)
1698                                 ji = mono_jit_info_table_find (l->data, ip);
1699                 }
1700
1701                 if (ji) {
1702                         mn = mono_method_full_name (ji->method, TRUE);
1703                 } else {
1704 #ifdef HAVE_BACKTRACE_SYMBOLS
1705                         char **names;
1706                         char *send;
1707                         int no_func;
1708                         prof_ucounts++;
1709                         names = backtrace_symbols (&ip, 1);
1710                         send = strchr (names [0], '+');
1711                         if (send) {
1712                                 *send = 0;
1713                                 no_func = 0;
1714                         } else {
1715                                 no_func = 1;
1716                         }
1717                         send = strchr (names [0], '[');
1718                         if (send)
1719                                 *send = 0;
1720                         if (no_func && names [0][0]) {
1721                                 char *endp = strchr (names [0], 0);
1722                                 while (--endp >= names [0] && g_ascii_isspace (*endp))
1723                                         *endp = 0;
1724                                 mn = try_addr2line (names [0], ip);
1725                         } else {
1726                                 mn = g_strdup (names [0]);
1727                         }
1728                         free (names);
1729 #else
1730                         prof_ucounts++;
1731                         mn = g_strdup_printf ("unmanaged [%p]", ip);
1732 #endif
1733                 }
1734                 c = GPOINTER_TO_UINT (g_hash_table_lookup (prof_table, mn));
1735                 c++;
1736                 g_hash_table_insert (prof_table, mn, GUINT_TO_POINTER (c));
1737                 if (c > 1)
1738                         g_free (mn);
1739         }
1740         fprintf (poutput, "prof counts: total/unmanaged: %d/%d\n", pcount, prof_ucounts);
1741         g_hash_table_foreach (prof_table, (GHFunc)prof_foreach, &sorted);
1742         for (tmp = sorted; tmp; tmp = tmp->next) {
1743                 double perc;
1744                 c = GPOINTER_TO_UINT (g_hash_table_lookup (prof_table, tmp->data));
1745                 perc = c*100.0/count;
1746                 fprintf (poutput, "%7d\t%5.2f %% %s\n", c, perc, (char*)tmp->data);
1747         }
1748         g_list_free (sorted);
1749 }
1750
1751 static void
1752 simple_appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result)
1753 {
1754         prof->domains = g_slist_prepend (prof->domains, domain);
1755 }
1756
1757 static void
1758 simple_appdomain_unload (MonoProfiler *prof, MonoDomain *domain)
1759 {
1760         /* FIXME: we should actually record partial data for each domain, 
1761          * but at this point it's must easier using the new logging profiler.
1762          */
1763         mono_profiler_shutdown ();
1764 }
1765
1766 static gint32 simple_shutdown_done = FALSE;
1767
1768 static void
1769 simple_shutdown (MonoProfiler *prof)
1770 {
1771         GList *profile = NULL;
1772         MonoProfiler *tprof;
1773         GSList *tmp;
1774         char *str;
1775         gint32 see_shutdown_done;
1776
1777 #ifndef HOST_WIN32
1778         mono_thread_attach(mono_get_root_domain());
1779 #endif
1780
1781         // Make sure we execute simple_shutdown only once
1782         see_shutdown_done = InterlockedExchange(& simple_shutdown_done, TRUE);
1783         if (see_shutdown_done)
1784                 return;
1785
1786         if (mono_profiler_events & MONO_PROFILE_STATISTICAL) {
1787                 stat_prof_report (prof);
1788         }
1789
1790         // Stop all incoming events
1791         mono_profiler_set_events (0);
1792         
1793         for (tmp = prof->per_thread; tmp; tmp = tmp->next) {
1794                 tprof = tmp->data;
1795                 merge_thread_data (prof, tprof);
1796         }
1797
1798         fprintf (poutput, "Total time spent compiling %d methods (sec): %.4g\n", prof->methods_jitted, prof->jit_time);
1799         if (prof->max_jit_method) {
1800                 str = method_get_name (prof->max_jit_method);
1801                 fprintf (poutput, "Slowest method to compile (sec): %.4g: %s\n", prof->max_jit_time, str);
1802                 g_free (str);
1803         }
1804         g_hash_table_foreach (prof->methods, (GHFunc)build_profile, &profile);
1805         output_profile (profile);
1806         g_list_free (profile);
1807         profile = NULL;
1808                 
1809         g_hash_table_foreach (prof->methods, (GHFunc)build_newobj_profile, &profile);
1810         output_newobj_profile (profile);
1811         g_list_free (profile);
1812
1813         g_free (prof_addresses);
1814         prof_addresses = NULL;
1815         g_hash_table_destroy (prof_table);
1816 }
1817
1818 static void
1819 mono_profiler_install_simple (const char *desc)
1820 {
1821         MonoProfiler *prof;
1822         gchar **args, **ptr;
1823         MonoProfileFlags flags = 0;
1824
1825         MONO_TIMER_STARTUP;
1826         poutput = stdout;
1827
1828         if (!desc)
1829                 desc = "alloc,time,jit";
1830
1831         if (desc) {
1832                 /* Parse options */
1833                 if (strstr (desc, ":"))
1834                         desc = strstr (desc, ":") + 1;
1835                 else
1836                         desc = "alloc,time,jit";
1837                 args = g_strsplit (desc, ",", -1);
1838
1839                 for (ptr = args; ptr && *ptr; ptr++) {
1840                         const char *arg = *ptr;
1841
1842                         // Alwais listen to appdomaon events to shutdown at the first unload
1843                         flags |= MONO_PROFILE_APPDOMAIN_EVENTS;
1844                         if (!strcmp (arg, "time"))
1845                                 flags |= MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_EXCEPTIONS;
1846                         else if (!strcmp (arg, "alloc"))
1847                                 flags |= MONO_PROFILE_ALLOCATIONS;
1848                         else if (!strcmp (arg, "stat"))
1849                                 flags |= MONO_PROFILE_STATISTICAL;
1850                         else if (!strcmp (arg, "jit"))
1851                                 flags |= MONO_PROFILE_JIT_COMPILATION;
1852                         else if (strncmp (arg, "file=", 5) == 0) {
1853                                 poutput = fopen (arg + 5, "wb");
1854                                 if (!poutput) {
1855                                         poutput = stdout;
1856                                         fprintf (stderr, "profiler : cannot open profile output file '%s'.\n", arg + 5);
1857                                 }
1858                         } else {
1859                                 fprintf (stderr, "profiler : Unknown argument '%s'.\n", arg);
1860                                 return;
1861                         }
1862                 }
1863         }
1864         if (flags & MONO_PROFILE_ALLOCATIONS)
1865                 flags |= MONO_PROFILE_GC_MOVES;
1866         if (flags & MONO_PROFILE_ALLOCATIONS)
1867                 flags |= MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_EXCEPTIONS;
1868         if (!flags)
1869                 flags = MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_JIT_COMPILATION | MONO_PROFILE_EXCEPTIONS;
1870
1871         prof = create_profiler ();
1872         ALLOC_PROFILER ();
1873         SET_PROFILER (prof);
1874
1875         /* statistical profiler data */
1876         prof_addresses = g_new0 (gpointer, MAX_PROF_SAMPLES);
1877         prof_table = g_hash_table_new (g_str_hash, g_str_equal);
1878
1879         mono_profiler_install (prof, simple_shutdown);
1880         mono_profiler_install_enter_leave (simple_method_enter, simple_method_leave);
1881         mono_profiler_install_exception (NULL, simple_method_leave, NULL);
1882         mono_profiler_install_jit_compile (simple_method_jit, simple_method_end_jit);
1883         mono_profiler_install_allocation (simple_allocation);
1884         mono_profiler_install_appdomain (NULL, simple_appdomain_load, simple_appdomain_unload, NULL);
1885         mono_profiler_install_statistical (simple_stat_hit);
1886         mono_profiler_install_gc_moves (simple_gc_move);
1887         mono_profiler_set_events (flags);
1888 }
1889
1890 #endif /* DISABLE_PROFILER */
1891
1892 typedef void (*ProfilerInitializer) (const char*);
1893 #define INITIALIZER_NAME "mono_profiler_startup"
1894
1895 /**
1896  * mono_profiler_load:
1897  * @desc: arguments to configure the profiler
1898  *
1899  * Invoke this method to initialize the profiler.   This will drive the
1900  * loading of the internal ("default") or any external profilers.
1901  *
1902  * This routine is invoked by Mono's driver, but must be called manually
1903  * if you embed Mono into your application.
1904  */
1905 void 
1906 mono_profiler_load (const char *desc)
1907 {
1908         mono_gc_base_init ();
1909
1910 #ifndef DISABLE_PROFILER
1911         if (!desc || (strcmp ("default", desc) == 0) || (strncmp (desc, "default:", 8) == 0)) {
1912                 mono_profiler_install_simple (desc);
1913                 return;
1914         }
1915 #else
1916         if (!desc) {
1917                 desc = "default";
1918         }
1919 #endif
1920         {
1921                 MonoDl *pmodule = NULL;
1922                 const char* col = strchr (desc, ':');
1923                 char* libname;
1924                 char* path;
1925                 char *mname;
1926                 char *err;
1927                 void *iter;
1928                 if (col != NULL) {
1929                         mname = g_memdup (desc, col - desc + 1);
1930                         mname [col - desc] = 0;
1931                 } else {
1932                         mname = g_strdup (desc);
1933                 }
1934                 libname = g_strdup_printf ("mono-profiler-%s", mname);
1935                 iter = NULL;
1936                 err = NULL;
1937                 while ((path = mono_dl_build_path (NULL, libname, &iter))) {
1938                         g_free (err);
1939                         pmodule = mono_dl_open (path, MONO_DL_LAZY, &err);
1940                         if (pmodule) {
1941                                 ProfilerInitializer func;
1942                                 if ((err = mono_dl_symbol (pmodule, INITIALIZER_NAME, (gpointer *)&func))) {
1943                                         g_warning ("Cannot find initializer function %s in profiler module: %s (%s)", INITIALIZER_NAME, libname, err);
1944                                         g_free (err);
1945                                         err = NULL;
1946                                 } else {
1947                                         func (desc);
1948                                 }
1949                                 break;
1950                         }
1951                         g_free (path);
1952                 }
1953                 if (!pmodule) {
1954                         g_warning ("Error loading profiler module '%s': %s", libname, err);
1955                         g_free (err);
1956                 }
1957                 g_free (libname);
1958                 g_free (mname);
1959                 g_free (path);
1960         }
1961 }
1962