Merge branch 'master' of github.com:mono/mono
[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         mono_profiler_set_events (0);
710 }
711
712 void
713 mono_profiler_gc_heap_resize (gint64 new_size)
714 {
715         ProfilerDesc *prof;
716         for (prof = prof_list; prof; prof = prof->next) {
717                 if ((prof->events & MONO_PROFILE_GC) && prof->gc_heap_resize)
718                         prof->gc_heap_resize (prof->profiler, new_size);
719         }
720 }
721
722 void
723 mono_profiler_gc_event (MonoGCEvent event, int generation)
724 {
725         ProfilerDesc *prof;
726         for (prof = prof_list; prof; prof = prof->next) {
727                 if ((prof->events & MONO_PROFILE_GC) && prof->gc_event)
728                         prof->gc_event (prof->profiler, event, generation);
729         }
730 }
731
732 void
733 mono_profiler_gc_moves (void **objects, int num)
734 {
735         ProfilerDesc *prof;
736         for (prof = prof_list; prof; prof = prof->next) {
737                 if ((prof->events & MONO_PROFILE_GC_MOVES) && prof->gc_moves)
738                         prof->gc_moves (prof->profiler, objects, num);
739         }
740 }
741
742 void
743 mono_profiler_install_gc (MonoProfileGCFunc callback, MonoProfileGCResizeFunc heap_resize_callback)
744 {
745         mono_gc_enable_events ();
746         if (!prof_list)
747                 return;
748         prof_list->gc_event = callback;
749         prof_list->gc_heap_resize = heap_resize_callback;
750 }
751
752 /**
753  * mono_profiler_install_gc_moves:
754  * @callback: callback function
755  *
756  * Install the @callback function that the GC will call when moving objects.
757  * The callback receives an array of pointers and the number of elements
758  * in the array. Every even element in the array is the original object location
759  * and the following odd element is the new location of the object in memory.
760  * So the number of elements argument will always be a multiple of 2.
761  * Since this callback happens during the GC, it is a restricted environment:
762  * no locks can be taken and the object pointers can be inspected only once
763  * the GC is finished (of course the original location pointers will not
764  * point to valid objects anymore).
765  */
766 void
767 mono_profiler_install_gc_moves (MonoProfileGCMoveFunc callback)
768 {
769         if (!prof_list)
770                 return;
771         prof_list->gc_moves = callback;
772 }
773
774 void
775 mono_profiler_install_runtime_initialized (MonoProfileFunc runtime_initialized_callback)
776 {
777         if (!prof_list)
778                 return;
779         prof_list->runtime_initialized_event = runtime_initialized_callback;
780 }
781
782 void
783 mono_profiler_runtime_initialized (void) {
784         ProfilerDesc *prof;
785         for (prof = prof_list; prof; prof = prof->next) {
786                 if (prof->runtime_initialized_event)
787                         prof->runtime_initialized_event (prof->profiler);
788         }
789 }
790
791 void
792 mono_profiler_install_code_chunk_new (MonoProfilerCodeChunkNew callback) {
793         if (!prof_list)
794                 return;
795         prof_list->code_chunk_new = callback;
796 }
797 void
798 mono_profiler_code_chunk_new (gpointer chunk, int size) {
799         ProfilerDesc *prof;
800         for (prof = prof_list; prof; prof = prof->next) {
801                 if (prof->code_chunk_new)
802                         prof->code_chunk_new (prof->profiler, chunk, size);
803         }
804 }
805
806 void
807 mono_profiler_install_code_chunk_destroy (MonoProfilerCodeChunkDestroy callback) {
808         if (!prof_list)
809                 return;
810         prof_list->code_chunk_destroy = callback;
811 }
812 void
813 mono_profiler_code_chunk_destroy (gpointer chunk) {
814         ProfilerDesc *prof;
815         for (prof = prof_list; prof; prof = prof->next) {
816                 if (prof->code_chunk_destroy)
817                         prof->code_chunk_destroy (prof->profiler, chunk);
818         }
819 }
820
821 void
822 mono_profiler_install_code_buffer_new (MonoProfilerCodeBufferNew callback) {
823         if (!prof_list)
824                 return;
825         prof_list->code_buffer_new = callback;
826 }
827
828 void
829 mono_profiler_install_iomap (MonoProfileIomapFunc callback)
830 {
831         if (!prof_list)
832                 return;
833         prof_list->iomap_cb = callback;
834 }
835
836 void
837 mono_profiler_code_buffer_new (gpointer buffer, int size, MonoProfilerCodeBufferType type, void *data) {
838         ProfilerDesc *prof;
839         for (prof = prof_list; prof; prof = prof->next) {
840                 if (prof->code_buffer_new)
841                         prof->code_buffer_new (prof->profiler, buffer, size, type, data);
842         }
843 }
844
845 static GHashTable *coverage_hash = NULL;
846
847 MonoProfileCoverageInfo* 
848 mono_profiler_coverage_alloc (MonoMethod *method, int entries)
849 {
850         MonoProfileCoverageInfo *res;
851         int instrument = FALSE;
852         ProfilerDesc *prof;
853
854         for (prof = prof_list; prof; prof = prof->next) {
855                 /* note that we call the filter on all the profilers even if just
856                  * a single one would be enough to instrument a method
857                  */
858                 if (prof->coverage_filter_cb)
859                         if (prof->coverage_filter_cb (prof->profiler, method))
860                                 instrument = TRUE;
861         }
862         if (!instrument)
863                 return NULL;
864
865         mono_profiler_coverage_lock ();
866         if (!coverage_hash)
867                 coverage_hash = g_hash_table_new (NULL, NULL);
868
869         res = g_malloc0 (sizeof (MonoProfileCoverageInfo) + sizeof (void*) * 2 * entries);
870
871         res->entries = entries;
872
873         g_hash_table_insert (coverage_hash, method, res);
874         mono_profiler_coverage_unlock ();
875
876         return res;
877 }
878
879 /* safe only when the method antive code has been unloaded */
880 void
881 mono_profiler_coverage_free (MonoMethod *method)
882 {
883         MonoProfileCoverageInfo* info;
884
885         mono_profiler_coverage_lock ();
886         if (!coverage_hash) {
887                 mono_profiler_coverage_unlock ();
888                 return;
889         }
890
891         info = g_hash_table_lookup (coverage_hash, method);
892         if (info) {
893                 g_free (info);
894                 g_hash_table_remove (coverage_hash, method);
895         }
896         mono_profiler_coverage_unlock ();
897 }
898
899 /**
900  * mono_profiler_coverage_get:
901  * @prof: The profiler handle, installed with mono_profiler_install
902  * @method: the method to gather information from.
903  * @func: A routine that will be called back with the results
904  *
905  * If the MONO_PROFILER_INS_COVERAGE flag was active during JIT compilation
906  * it is posisble to obtain coverage information about a give method.
907  *
908  * The function @func will be invoked repeatedly with instances of the
909  * MonoProfileCoverageEntry structure.
910  */
911 void 
912 mono_profiler_coverage_get (MonoProfiler *prof, MonoMethod *method, MonoProfileCoverageFunc func)
913 {
914         MonoProfileCoverageInfo* info;
915         int i, offset;
916         guint32 code_size;
917         const unsigned char *start, *end, *cil_code;
918         MonoMethodHeader *header;
919         MonoProfileCoverageEntry entry;
920         MonoDebugMethodInfo *debug_minfo;
921
922         mono_profiler_coverage_lock ();
923         info = g_hash_table_lookup (coverage_hash, method);
924         mono_profiler_coverage_unlock ();
925
926         if (!info)
927                 return;
928
929         header = mono_method_get_header (method);
930         start = mono_method_header_get_code (header, &code_size, NULL);
931         debug_minfo = mono_debug_lookup_method (method);
932
933         end = start + code_size;
934         for (i = 0; i < info->entries; ++i) {
935                 cil_code = info->data [i].cil_code;
936                 if (cil_code && cil_code >= start && cil_code < end) {
937                         char *fname = NULL;
938                         offset = cil_code - start;
939                         entry.iloffset = offset;
940                         entry.method = method;
941                         entry.counter = info->data [i].count;
942                         entry.line = entry.col = 1;
943                         entry.filename = NULL;
944                         if (debug_minfo) {
945                                 MonoDebugSourceLocation *location;
946
947                                 location = mono_debug_symfile_lookup_location (debug_minfo, offset);
948                                 if (location) {
949                                         entry.line = location->row;
950                                         entry.col = location->column;
951                                         entry.filename = fname = g_strdup (location->source_file);
952                                         mono_debug_free_source_location (location);
953                                 }
954                         }
955
956                         func (prof, &entry);
957                         g_free (fname);
958                 }
959         }
960         mono_metadata_free_mh (header);
961 }
962
963 #ifndef DISABLE_PROFILER
964 /*
965  * Small profiler extracted from mint: we should move it in a loadable module
966  * and improve it to do graphs and more accurate timestamping with rdtsc.
967  */
968
969 static FILE* poutput = NULL;
970
971 #define USE_X86TSC 0
972 #define USE_WIN32COUNTER 0
973 #if USE_X86TSC
974
975 typedef struct {
976         unsigned int lows, highs, lowe, highe;
977 } MonoRdtscTimer;
978
979 #define rdtsc(low,high) \
980         __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
981
982 static int freq;
983
984 static double
985 rdtsc_elapsed (MonoRdtscTimer *t)
986 {
987         unsigned long long diff;
988         unsigned int highe = t->highe;
989         if (t->lowe < t->lows)
990                 highe--;
991         diff = (((unsigned long long) highe - t->highs) << 32) + (t->lowe - t->lows);
992         return ((double)diff / freq) / 1000000; /* have to return the result in seconds */
993 }
994
995 static int 
996 have_rdtsc (void) {
997         char buf[256];
998         int have_freq = 0;
999         int have_flag = 0;
1000         float val;
1001         FILE *cpuinfo;
1002
1003         if (!(cpuinfo = fopen ("/proc/cpuinfo", "r")))
1004                 return 0;
1005         while (fgets (buf, sizeof(buf), cpuinfo)) {
1006                 if (sscanf (buf, "cpu MHz : %f", &val) == 1) {
1007                         /*printf ("got mh: %f\n", val);*/
1008                         have_freq = val;
1009                 }
1010                 if (strncmp (buf, "flags", 5) == 0) {
1011                         if (strstr (buf, "tsc")) {
1012                                 have_flag = 1;
1013                                 /*printf ("have tsc\n");*/
1014                         }
1015                 }
1016         }
1017         fclose (cpuinfo);
1018         return have_flag? have_freq: 0;
1019 }
1020
1021 #define MONO_TIMER_STARTUP      \
1022         if (!(freq = have_rdtsc ())) g_error ("Compiled with TSC support, but none found");
1023 #define MONO_TIMER_TYPE  MonoRdtscTimer
1024 #define MONO_TIMER_INIT(t)
1025 #define MONO_TIMER_DESTROY(t)
1026 #define MONO_TIMER_START(t) rdtsc ((t).lows, (t).highs);
1027 #define MONO_TIMER_STOP(t) rdtsc ((t).lowe, (t).highe);
1028 #define MONO_TIMER_ELAPSED(t) rdtsc_elapsed (&(t))
1029
1030 #elif USE_WIN32COUNTER
1031 #include <windows.h>
1032
1033 typedef struct {
1034         LARGE_INTEGER start, stop;
1035 } MonoWin32Timer;
1036
1037 static int freq;
1038
1039 static double
1040 win32_elapsed (MonoWin32Timer *t)
1041 {
1042         LONGLONG diff = t->stop.QuadPart - t->start.QuadPart;
1043         return ((double)diff / freq) / 1000000; /* have to return the result in seconds */
1044 }
1045
1046 static int 
1047 have_win32counter (void) {
1048         LARGE_INTEGER f;
1049
1050         if (!QueryPerformanceFrequency (&f))
1051                 return 0;
1052         return f.LowPart;
1053 }
1054
1055 #define MONO_TIMER_STARTUP      \
1056         if (!(freq = have_win32counter ())) g_error ("Compiled with Win32 counter support, but none found");
1057 #define MONO_TIMER_TYPE  MonoWin32Timer
1058 #define MONO_TIMER_INIT(t)
1059 #define MONO_TIMER_DESTROY(t)
1060 #define MONO_TIMER_START(t) QueryPerformanceCounter (&(t).start)
1061 #define MONO_TIMER_STOP(t) QueryPerformanceCounter (&(t).stop)
1062 #define MONO_TIMER_ELAPSED(t) win32_elapsed (&(t))
1063
1064 #else
1065
1066 typedef struct {
1067         GTimeVal start, stop;
1068 } MonoGLibTimer;
1069
1070 static double
1071 timeval_elapsed (MonoGLibTimer *t)
1072 {
1073         if (t->start.tv_usec > t->stop.tv_usec) {
1074                 t->stop.tv_usec += G_USEC_PER_SEC;
1075                 t->stop.tv_sec--;
1076         }
1077         return (t->stop.tv_sec - t->start.tv_sec) 
1078                 + ((double)(t->stop.tv_usec - t->start.tv_usec))/ G_USEC_PER_SEC;
1079 }
1080
1081 #define MONO_TIMER_STARTUP
1082 #define MONO_TIMER_TYPE MonoGLibTimer
1083 #define MONO_TIMER_INIT(t)
1084 #define MONO_TIMER_DESTROY(t)
1085 #define MONO_TIMER_START(t) g_get_current_time (&(t).start)
1086 #define MONO_TIMER_STOP(t) g_get_current_time (&(t).stop)
1087 #define MONO_TIMER_ELAPSED(t) timeval_elapsed (&(t))
1088 #endif
1089
1090 typedef struct _AllocInfo AllocInfo;
1091 typedef struct _CallerInfo CallerInfo;
1092 typedef struct _LastCallerInfo LastCallerInfo;
1093
1094 struct _MonoProfiler {
1095         GHashTable *methods;
1096         MonoMemPool *mempool;
1097         GSList *domains;
1098         /* info about JIT time */
1099         MONO_TIMER_TYPE jit_timer;
1100         double      jit_time;
1101         double      max_jit_time;
1102         MonoMethod *max_jit_method;
1103         int         methods_jitted;
1104         
1105         GSList     *per_thread;
1106         
1107         /* chain of callers for the current thread */
1108         LastCallerInfo *callers;
1109         /* LastCallerInfo nodes for faster allocation */
1110         LastCallerInfo *cstorage;
1111 };
1112
1113 typedef struct {
1114         MonoMethod *method;
1115         guint64 count;
1116         double total;
1117         AllocInfo *alloc_info;
1118         CallerInfo *caller_info;
1119 } MethodProfile;
1120
1121 typedef struct _MethodCallProfile MethodCallProfile;
1122
1123 struct _MethodCallProfile {
1124         MethodCallProfile *next;
1125         MONO_TIMER_TYPE timer;
1126         MonoMethod *method;
1127 };
1128
1129 struct _AllocInfo {
1130         AllocInfo *next;
1131         MonoClass *klass;
1132         guint64 count;
1133         guint64 mem;
1134 };
1135
1136 struct _CallerInfo {
1137         CallerInfo *next;
1138         MonoMethod *caller;
1139         guint count;
1140 };
1141
1142 struct _LastCallerInfo {
1143         LastCallerInfo *next;
1144         MonoMethod *method;
1145         MONO_TIMER_TYPE timer;
1146 };
1147
1148 static MonoProfiler*
1149 create_profiler (void)
1150 {
1151         MonoProfiler *prof = g_new0 (MonoProfiler, 1);
1152
1153         prof->methods = g_hash_table_new (mono_aligned_addr_hash, NULL);
1154         MONO_TIMER_INIT (prof->jit_timer);
1155         prof->mempool = mono_mempool_new ();
1156         return prof;
1157 }
1158 #if 1
1159
1160 #ifdef HAVE_KW_THREAD
1161         static __thread MonoProfiler * tls_profiler;
1162 #       define GET_PROFILER() tls_profiler
1163 #       define SET_PROFILER(x) tls_profiler = (x)
1164 #       define ALLOC_PROFILER() /* nop */
1165 #else
1166         static guint32 profiler_thread_id = -1;
1167 #       define GET_PROFILER() ((MonoProfiler *)TlsGetValue (profiler_thread_id))
1168 #       define SET_PROFILER(x) TlsSetValue (profiler_thread_id, x);
1169 #       define ALLOC_PROFILER() profiler_thread_id = TlsAlloc ()
1170 #endif
1171
1172 #define GET_THREAD_PROF(prof) do {                                                           \
1173                 MonoProfiler *_tprofiler = GET_PROFILER ();                                  \
1174                 if (!_tprofiler) {                                                           \
1175                         _tprofiler = create_profiler ();                                     \
1176                         prof->per_thread = g_slist_prepend (prof->per_thread, _tprofiler);   \
1177                         SET_PROFILER (_tprofiler);                                           \
1178                 }                                                                            \
1179                 prof = _tprofiler;                                                           \
1180         } while (0)
1181 #else
1182 /* thread unsafe but faster variant */
1183 #define GET_THREAD_PROF(prof)
1184 #endif
1185
1186 static gint
1187 compare_profile (MethodProfile *profa, MethodProfile *profb)
1188 {
1189         return (gint)((profb->total - profa->total)*1000);
1190 }
1191
1192 static void
1193 build_profile (MonoMethod *m, MethodProfile *prof, GList **funcs)
1194 {
1195         prof->method = m;
1196         *funcs = g_list_insert_sorted (*funcs, prof, (GCompareFunc)compare_profile);
1197 }
1198
1199 static char*
1200 method_get_name (MonoMethod* method)
1201 {
1202         char *sig, *res;
1203         
1204         sig = mono_signature_get_desc (mono_method_signature (method), FALSE);
1205         res = g_strdup_printf ("%s%s%s::%s(%s)", method->klass->name_space,
1206                         method->klass->name_space ? "." : "", method->klass->name,
1207                 method->name, sig);
1208         g_free (sig);
1209         return res;
1210 }
1211
1212 static void output_callers (MethodProfile *p);
1213
1214 /* This isn't defined on older glib versions and on some platforms */
1215 #ifndef G_GUINT64_FORMAT
1216 #define G_GUINT64_FORMAT "ul"
1217 #endif
1218 #ifndef G_GINT64_FORMAT
1219 #define G_GINT64_FORMAT "lld"
1220 #endif
1221
1222 static void
1223 output_profile (GList *funcs)
1224 {
1225         GList *tmp;
1226         MethodProfile *p;
1227         char *m;
1228         guint64 total_calls = 0;
1229
1230         if (funcs)
1231                 fprintf (poutput, "Time(ms) Count   P/call(ms) Method name\n");
1232         for (tmp = funcs; tmp; tmp = tmp->next) {
1233                 p = tmp->data;
1234                 total_calls += p->count;
1235                 if (!(gint)(p->total*1000))
1236                         continue;
1237                 m = method_get_name (p->method);
1238                 fprintf (poutput, "########################\n");
1239                 fprintf (poutput, "% 8.3f ", (double) (p->total * 1000));
1240                 fprintf (poutput, "%7" G_GUINT64_FORMAT " ", (guint64)p->count);
1241                 fprintf (poutput, "% 8.3f ", (double) (p->total * 1000)/(double)p->count);
1242                 fprintf (poutput, "  %s\n", m);
1243
1244                 g_free (m);
1245                 /* callers */
1246                 output_callers (p);
1247         }
1248         fprintf (poutput, "Total number of calls: %" G_GINT64_FORMAT "\n", (gint64)total_calls);
1249 }
1250
1251 typedef struct {
1252         MethodProfile *mp;
1253         guint64 count;
1254 } NewobjProfile;
1255
1256 static gint
1257 compare_newobj_profile (NewobjProfile *profa, NewobjProfile *profb)
1258 {
1259         if (profb->count == profa->count)
1260                 return 0;
1261         else
1262                 return profb->count > profa->count ? 1 : -1;
1263 }
1264
1265 static void
1266 build_newobj_profile (MonoClass *class, MethodProfile *mprof, GList **funcs)
1267 {
1268         NewobjProfile *prof = g_new (NewobjProfile, 1);
1269         AllocInfo *tmp;
1270         guint64 count = 0;
1271         
1272         prof->mp = mprof;
1273         /* we use the total amount of memory to sort */
1274         for (tmp = mprof->alloc_info; tmp; tmp = tmp->next)
1275                 count += tmp->mem;
1276         prof->count = count;
1277         *funcs = g_list_insert_sorted (*funcs, prof, (GCompareFunc)compare_newobj_profile);
1278 }
1279
1280 static int
1281 compare_caller (CallerInfo *a, CallerInfo *b)
1282 {
1283         return b->count - a->count;
1284 }
1285
1286 static int
1287 compare_alloc (AllocInfo *a, AllocInfo *b)
1288 {
1289         return b->mem - a->mem;
1290 }
1291
1292 static GSList*
1293 sort_alloc_list (AllocInfo *ai)
1294 {
1295         GSList *l = NULL;
1296         AllocInfo *tmp;
1297         for (tmp = ai; tmp; tmp = tmp->next) {
1298                 l = g_slist_insert_sorted (l, tmp, (GCompareFunc)compare_alloc);
1299         }
1300         return l;
1301 }
1302
1303 static GSList*
1304 sort_caller_list (CallerInfo *ai)
1305 {
1306         GSList *l = NULL;
1307         CallerInfo *tmp;
1308         for (tmp = ai; tmp; tmp = tmp->next) {
1309                 l = g_slist_insert_sorted (l, tmp, (GCompareFunc)compare_caller);
1310         }
1311         return l;
1312 }
1313
1314 static void
1315 output_callers (MethodProfile *p) {
1316         guint total_callers, percent;
1317         GSList *sorted, *tmps;
1318         CallerInfo *cinfo;
1319         char *m;
1320         
1321         fprintf (poutput, "  Callers (with count) that contribute at least for 1%%:\n");
1322         total_callers = 0;
1323         for (cinfo = p->caller_info; cinfo; cinfo = cinfo->next) {
1324                 total_callers += cinfo->count;
1325         }
1326         sorted = sort_caller_list (p->caller_info);
1327         for (tmps = sorted; tmps; tmps = tmps->next) {
1328                 cinfo = tmps->data;
1329                 percent = (cinfo->count * 100)/total_callers;
1330                 if (percent < 1)
1331                         continue;
1332                 m = method_get_name (cinfo->caller);
1333                 fprintf (poutput, "    %8d % 3d %% %s\n", cinfo->count, percent, m);
1334                 g_free (m);
1335         }
1336 }
1337
1338 static int moved_objects = 0;
1339 static void
1340 simple_gc_move (MonoProfiler *prof, void **objects, int num)
1341 {
1342         moved_objects += num / 2;
1343 }
1344
1345 static void
1346 output_newobj_profile (GList *proflist)
1347 {
1348         GList *tmp;
1349         NewobjProfile *p;
1350         MethodProfile *mp;
1351         AllocInfo *ainfo;
1352         MonoClass *klass;
1353         const char* isarray;
1354         char buf [256];
1355         char *m;
1356         guint64 total = 0;
1357         GSList *sorted, *tmps;
1358
1359         fprintf (poutput, "\nAllocation profiler\n");
1360
1361         if (proflist)
1362                 fprintf (poutput, "%-9s %s\n", "Total mem", "Method");
1363         for (tmp = proflist; tmp; tmp = tmp->next) {
1364                 p = tmp->data;
1365                 total += p->count;
1366                 if (p->count < 50000)
1367                         continue;
1368                 mp = p->mp;
1369                 m = method_get_name (mp->method);
1370                 fprintf (poutput, "########################\n%8" G_GUINT64_FORMAT " KB %s\n", (p->count / 1024), m);
1371                 g_free (m);
1372                 sorted = sort_alloc_list (mp->alloc_info);
1373                 for (tmps = sorted; tmps; tmps = tmps->next) {
1374                         ainfo = tmps->data;
1375                         if (ainfo->mem < 50000)
1376                                 continue;
1377                         klass = ainfo->klass;
1378                         if (klass->rank) {
1379                                 isarray = "[]";
1380                                 klass = klass->element_class;
1381                         } else {
1382                                 isarray = "";
1383                         }
1384                         g_snprintf (buf, sizeof (buf), "%s%s%s%s",
1385                                 klass->name_space, klass->name_space ? "." : "", klass->name, isarray);
1386                         fprintf (poutput, "    %8" G_GUINT64_FORMAT " KB %8" G_GUINT64_FORMAT " %-48s\n", (ainfo->mem / 1024), ainfo->count, buf);
1387                 }
1388                 /* callers */
1389                 output_callers (mp);
1390         }
1391         fprintf (poutput, "Total memory allocated: %" G_GUINT64_FORMAT " KB\n", total / 1024);
1392         fprintf (poutput, "Objects copied: %d\n", moved_objects);
1393 }
1394
1395 static void
1396 merge_methods (MonoMethod *method, MethodProfile *profile, MonoProfiler *prof)
1397 {
1398         MethodProfile *mprof;
1399         AllocInfo *talloc_info, *alloc_info;
1400         CallerInfo *tcaller_info, *caller_info;
1401
1402         mprof = g_hash_table_lookup (prof->methods, method);
1403         if (!mprof) {
1404                 /* the master thread didn't see this method, just transfer the info as is */
1405                 g_hash_table_insert (prof->methods, method, profile);
1406                 return;
1407         }
1408         /* merge the info from profile into mprof */
1409         mprof->count += profile->count;
1410         mprof->total += profile->total;
1411         /* merge alloc info */
1412         for (talloc_info = profile->alloc_info; talloc_info; talloc_info = talloc_info->next) {
1413                 for (alloc_info = mprof->alloc_info; alloc_info; alloc_info = alloc_info->next) {
1414                         if (alloc_info->klass == talloc_info->klass) {
1415                                 /* mprof already has a record for the klass, merge */
1416                                 alloc_info->count += talloc_info->count;
1417                                 alloc_info->mem += talloc_info->mem;
1418                                 break;
1419                         }
1420                 }
1421                 if (!alloc_info) {
1422                         /* mprof didn't have the info, just copy it over */
1423                         alloc_info = mono_mempool_alloc0 (prof->mempool, sizeof (AllocInfo));
1424                         *alloc_info = *talloc_info;
1425                         alloc_info->next = mprof->alloc_info;
1426                         mprof->alloc_info = alloc_info->next;
1427                 }
1428         }
1429         /* merge callers info */
1430         for (tcaller_info = profile->caller_info; tcaller_info; tcaller_info = tcaller_info->next) {
1431                 for (caller_info = mprof->caller_info; caller_info; caller_info = caller_info->next) {
1432                         if (caller_info->caller == tcaller_info->caller) {
1433                                 /* mprof already has a record for the caller method, merge */
1434                                 caller_info->count += tcaller_info->count;
1435                                 break;
1436                         }
1437                 }
1438                 if (!caller_info) {
1439                         /* mprof didn't have the info, just copy it over */
1440                         caller_info = mono_mempool_alloc0 (prof->mempool, sizeof (CallerInfo));
1441                         *caller_info = *tcaller_info;
1442                         caller_info->next = mprof->caller_info;
1443                         mprof->caller_info = caller_info;
1444                 }
1445         }
1446 }
1447
1448 static void
1449 merge_thread_data (MonoProfiler *master, MonoProfiler *tprof)
1450 {
1451         master->jit_time += tprof->jit_time;
1452         master->methods_jitted += tprof->methods_jitted;
1453         if (master->max_jit_time < tprof->max_jit_time) {
1454                 master->max_jit_time = tprof->max_jit_time;
1455                 master->max_jit_method = tprof->max_jit_method;
1456         }
1457
1458         g_hash_table_foreach (tprof->methods, (GHFunc)merge_methods, master);
1459 }
1460
1461 static void
1462 simple_method_enter (MonoProfiler *prof, MonoMethod *method)
1463 {
1464         MethodProfile *profile_info;
1465         LastCallerInfo *callinfo;
1466         GET_THREAD_PROF (prof);
1467         /*g_print ("enter %p %s::%s in %d (%p)\n", method, method->klass->name, method->name, GetCurrentThreadId (), prof);*/
1468         if (!(profile_info = g_hash_table_lookup (prof->methods, method))) {
1469                 profile_info = mono_mempool_alloc0 (prof->mempool, sizeof (MethodProfile));
1470                 MONO_TIMER_INIT (profile_info->u.timer);
1471                 g_hash_table_insert (prof->methods, method, profile_info);
1472         }
1473         profile_info->count++;
1474         if (prof->callers) {
1475                 CallerInfo *cinfo;
1476                 MonoMethod *caller = prof->callers->method;
1477                 for (cinfo = profile_info->caller_info; cinfo; cinfo = cinfo->next) {
1478                         if (cinfo->caller == caller)
1479                                 break;
1480                 }
1481                 if (!cinfo) {
1482                         cinfo = mono_mempool_alloc0 (prof->mempool, sizeof (CallerInfo));
1483                         cinfo->caller = caller;
1484                         cinfo->next = profile_info->caller_info;
1485                         profile_info->caller_info = cinfo;
1486                 }
1487                 cinfo->count++;
1488         }
1489         if (!(callinfo = prof->cstorage)) {
1490                 callinfo = mono_mempool_alloc (prof->mempool, sizeof (LastCallerInfo));
1491                 MONO_TIMER_INIT (callinfo->timer);
1492         } else {
1493                 prof->cstorage = prof->cstorage->next;
1494         }
1495         callinfo->method = method;
1496         callinfo->next = prof->callers;
1497         prof->callers = callinfo;
1498         MONO_TIMER_START (callinfo->timer);
1499 }
1500
1501 static void
1502 simple_method_leave (MonoProfiler *prof, MonoMethod *method)
1503 {
1504         MethodProfile *profile_info;
1505         LastCallerInfo *callinfo, *newcallinfo = NULL;
1506         
1507         GET_THREAD_PROF (prof);
1508         /*g_print ("leave %p %s::%s in %d (%p)\n", method, method->klass->name, method->name, GetCurrentThreadId (), prof);*/
1509         callinfo = prof->callers;
1510         /* should really not happen, but we don't catch exceptions events, yet ... */
1511         while (callinfo) {
1512                 MONO_TIMER_STOP (callinfo->timer);
1513                 profile_info = g_hash_table_lookup (prof->methods, callinfo->method);
1514                 if (profile_info)
1515                         profile_info->total += MONO_TIMER_ELAPSED (callinfo->timer);
1516                 newcallinfo = callinfo->next;
1517                 callinfo->next = prof->cstorage;
1518                 prof->cstorage = callinfo;
1519                 if (callinfo->method == method)
1520                         break;
1521                 callinfo = newcallinfo;
1522         }
1523         prof->callers = newcallinfo;
1524 }
1525
1526 static void
1527 simple_allocation (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
1528 {
1529         MethodProfile *profile_info;
1530         AllocInfo *tmp;
1531
1532         GET_THREAD_PROF (prof);
1533         if (prof->callers) {
1534                 MonoMethod *caller = prof->callers->method;
1535
1536                 /* Otherwise all allocations are attributed to icall_wrapper_mono_object_new */
1537                 if (caller->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE && prof->callers->next)
1538                         caller = prof->callers->next->method;
1539
1540                 if (!(profile_info = g_hash_table_lookup (prof->methods, caller)))
1541                         g_assert_not_reached ();
1542         } else {
1543                 return; /* fine for now */
1544         }
1545
1546         for (tmp = profile_info->alloc_info; tmp; tmp = tmp->next) {
1547                 if (tmp->klass == klass)
1548                         break;
1549         }
1550         if (!tmp) {
1551                 tmp = mono_mempool_alloc0 (prof->mempool, sizeof (AllocInfo));
1552                 tmp->klass = klass;
1553                 tmp->next = profile_info->alloc_info;
1554                 profile_info->alloc_info = tmp;
1555         }
1556         tmp->count++;
1557         tmp->mem += mono_object_get_size (obj);
1558 }
1559
1560 static void
1561 simple_method_jit (MonoProfiler *prof, MonoMethod *method)
1562 {
1563         GET_THREAD_PROF (prof);
1564         prof->methods_jitted++;
1565         MONO_TIMER_START (prof->jit_timer);
1566 }
1567
1568 static void
1569 simple_method_end_jit (MonoProfiler *prof, MonoMethod *method, int result)
1570 {
1571         double jtime;
1572         GET_THREAD_PROF (prof);
1573         MONO_TIMER_STOP (prof->jit_timer);
1574         jtime = MONO_TIMER_ELAPSED (prof->jit_timer);
1575         prof->jit_time += jtime;
1576         if (jtime > prof->max_jit_time) {
1577                 prof->max_jit_time = jtime;
1578                 prof->max_jit_method = method;
1579         }
1580 }
1581
1582 /* about 10 minutes of samples */
1583 #define MAX_PROF_SAMPLES (1000*60*10)
1584 static int prof_counts = 0;
1585 static int prof_ucounts = 0;
1586 static gpointer* prof_addresses = NULL;
1587 static GHashTable *prof_table = NULL;
1588
1589 static void
1590 simple_stat_hit (MonoProfiler *prof, guchar *ip, void *context)
1591 {
1592         int pos;
1593
1594         if (prof_counts >= MAX_PROF_SAMPLES)
1595                 return;
1596         pos = InterlockedIncrement (&prof_counts);
1597         prof_addresses [pos - 1] = ip;
1598 }
1599
1600 static int
1601 compare_methods_prof (gconstpointer a, gconstpointer b)
1602 {
1603         int ca = GPOINTER_TO_UINT (g_hash_table_lookup (prof_table, a));
1604         int cb = GPOINTER_TO_UINT (g_hash_table_lookup (prof_table, b));
1605         return cb-ca;
1606 }
1607
1608 static void
1609 prof_foreach (char *method, gpointer c, gpointer data)
1610 {
1611         GList **list = data;
1612         *list = g_list_insert_sorted (*list, method, compare_methods_prof);
1613 }
1614
1615 typedef struct Addr2LineData Addr2LineData;
1616
1617 struct Addr2LineData {
1618         Addr2LineData *next;
1619         FILE *pipein;
1620         FILE *pipeout;
1621         char *binary;
1622         int child_pid;
1623 };
1624
1625 static Addr2LineData *addr2line_pipes = NULL;
1626
1627 static char*
1628 try_addr2line (const char* binary, gpointer ip)
1629 {
1630         char buf [1024];
1631         char *res;
1632         Addr2LineData *addr2line;
1633
1634         for (addr2line = addr2line_pipes; addr2line; addr2line = addr2line->next) {
1635                 if (strcmp (binary, addr2line->binary) == 0)
1636                         break;
1637         }
1638         if (!addr2line) {
1639                 const char *addr_argv[] = {"addr2line", "-f", "-e", binary, NULL};
1640                 int child_pid;
1641                 int ch_in, ch_out;
1642 #ifdef __linux__
1643                 char monobin [1024];
1644                 /* non-linux platforms will need different code here */
1645                 if (strcmp (binary, "mono") == 0) {
1646                         int count = readlink ("/proc/self/exe", monobin, sizeof (monobin));
1647                         if (count >= 0 && count < sizeof (monobin)) {
1648                                 monobin [count] = 0;
1649                                 addr_argv [3] = monobin;
1650                         }
1651                 }
1652 #endif
1653                 if (!g_spawn_async_with_pipes (NULL, (char**)addr_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
1654                                 &child_pid, &ch_in, &ch_out, NULL, NULL)) {
1655                         return g_strdup (binary);
1656                 }
1657                 addr2line = g_new0 (Addr2LineData, 1);
1658                 addr2line->child_pid = child_pid;
1659                 addr2line->binary = g_strdup (binary);
1660                 addr2line->pipein = fdopen (ch_in, "w");
1661                 addr2line->pipeout = fdopen (ch_out, "r");
1662                 addr2line->next = addr2line_pipes;
1663                 addr2line_pipes = addr2line;
1664         }
1665         fprintf (addr2line->pipein, "%p\n", ip);
1666         fflush (addr2line->pipein);
1667         /* we first get the func name and then file:lineno in a second line */
1668         if (fgets (buf, sizeof (buf), addr2line->pipeout) && buf [0] != '?') {
1669                 char *end = strchr (buf, '\n');
1670                 if (end)
1671                         *end = 0;
1672                 res = g_strdup_printf ("%s(%s", binary, buf);
1673                 /* discard the filename/line info */
1674                 fgets (buf, sizeof (buf), addr2line->pipeout);
1675         } else {
1676                 res = g_strdup (binary);
1677         }
1678         return res;
1679 }
1680
1681 static void
1682 stat_prof_report (MonoProfiler *prof)
1683 {
1684         MonoJitInfo *ji;
1685         int count = prof_counts;
1686         int i, c;
1687         char *mn;
1688         gpointer ip;
1689         GList *tmp, *sorted = NULL;
1690         GSList *l;
1691         int pcount = ++ prof_counts;
1692
1693         prof_counts = MAX_PROF_SAMPLES;
1694         for (i = 0; i < count; ++i) {
1695                 ip = prof_addresses [i];
1696                 ji = mono_jit_info_table_find (mono_domain_get (), ip);
1697
1698                 if (!ji) {
1699                         for (l = prof->domains; l && !ji; l = l->next)
1700                                 ji = mono_jit_info_table_find (l->data, ip);
1701                 }
1702
1703                 if (ji) {
1704                         mn = mono_method_full_name (ji->method, TRUE);
1705                 } else {
1706 #ifdef HAVE_BACKTRACE_SYMBOLS
1707                         char **names;
1708                         char *send;
1709                         int no_func;
1710                         prof_ucounts++;
1711                         names = backtrace_symbols (&ip, 1);
1712                         send = strchr (names [0], '+');
1713                         if (send) {
1714                                 *send = 0;
1715                                 no_func = 0;
1716                         } else {
1717                                 no_func = 1;
1718                         }
1719                         send = strchr (names [0], '[');
1720                         if (send)
1721                                 *send = 0;
1722                         if (no_func && names [0][0]) {
1723                                 char *endp = strchr (names [0], 0);
1724                                 while (--endp >= names [0] && g_ascii_isspace (*endp))
1725                                         *endp = 0;
1726                                 mn = try_addr2line (names [0], ip);
1727                         } else {
1728                                 mn = g_strdup (names [0]);
1729                         }
1730                         free (names);
1731 #else
1732                         prof_ucounts++;
1733                         mn = g_strdup_printf ("unmanaged [%p]", ip);
1734 #endif
1735                 }
1736                 c = GPOINTER_TO_UINT (g_hash_table_lookup (prof_table, mn));
1737                 c++;
1738                 g_hash_table_insert (prof_table, mn, GUINT_TO_POINTER (c));
1739                 if (c > 1)
1740                         g_free (mn);
1741         }
1742         fprintf (poutput, "prof counts: total/unmanaged: %d/%d\n", pcount, prof_ucounts);
1743         g_hash_table_foreach (prof_table, (GHFunc)prof_foreach, &sorted);
1744         for (tmp = sorted; tmp; tmp = tmp->next) {
1745                 double perc;
1746                 c = GPOINTER_TO_UINT (g_hash_table_lookup (prof_table, tmp->data));
1747                 perc = c*100.0/count;
1748                 fprintf (poutput, "%7d\t%5.2f %% %s\n", c, perc, (char*)tmp->data);
1749         }
1750         g_list_free (sorted);
1751 }
1752
1753 static void
1754 simple_appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result)
1755 {
1756         prof->domains = g_slist_prepend (prof->domains, domain);
1757 }
1758
1759 static void
1760 simple_appdomain_unload (MonoProfiler *prof, MonoDomain *domain)
1761 {
1762         /* FIXME: we should actually record partial data for each domain, 
1763          * but at this point it's must easier using the new logging profiler.
1764          */
1765         mono_profiler_shutdown ();
1766 }
1767
1768 static gint32 simple_shutdown_done = FALSE;
1769
1770 static void
1771 simple_shutdown (MonoProfiler *prof)
1772 {
1773         GList *profile = NULL;
1774         MonoProfiler *tprof;
1775         GSList *tmp;
1776         char *str;
1777         gint32 see_shutdown_done;
1778
1779 #ifndef HOST_WIN32
1780         mono_thread_attach(mono_get_root_domain());
1781 #endif
1782
1783         // Make sure we execute simple_shutdown only once
1784         see_shutdown_done = InterlockedExchange(& simple_shutdown_done, TRUE);
1785         if (see_shutdown_done)
1786                 return;
1787
1788         if (mono_profiler_events & MONO_PROFILE_STATISTICAL) {
1789                 stat_prof_report (prof);
1790         }
1791
1792         // Stop all incoming events
1793         mono_profiler_set_events (0);
1794         
1795         for (tmp = prof->per_thread; tmp; tmp = tmp->next) {
1796                 tprof = tmp->data;
1797                 merge_thread_data (prof, tprof);
1798         }
1799
1800         fprintf (poutput, "Total time spent compiling %d methods (sec): %.4g\n", prof->methods_jitted, prof->jit_time);
1801         if (prof->max_jit_method) {
1802                 str = method_get_name (prof->max_jit_method);
1803                 fprintf (poutput, "Slowest method to compile (sec): %.4g: %s\n", prof->max_jit_time, str);
1804                 g_free (str);
1805         }
1806         g_hash_table_foreach (prof->methods, (GHFunc)build_profile, &profile);
1807         output_profile (profile);
1808         g_list_free (profile);
1809         profile = NULL;
1810                 
1811         g_hash_table_foreach (prof->methods, (GHFunc)build_newobj_profile, &profile);
1812         output_newobj_profile (profile);
1813         g_list_free (profile);
1814
1815         g_free (prof_addresses);
1816         prof_addresses = NULL;
1817         g_hash_table_destroy (prof_table);
1818 }
1819
1820 static void
1821 mono_profiler_install_simple (const char *desc)
1822 {
1823         MonoProfiler *prof;
1824         gchar **args, **ptr;
1825         MonoProfileFlags flags = 0;
1826
1827         MONO_TIMER_STARTUP;
1828         poutput = stdout;
1829
1830         if (!desc)
1831                 desc = "alloc,time,jit";
1832
1833         if (desc) {
1834                 /* Parse options */
1835                 if (strstr (desc, ":"))
1836                         desc = strstr (desc, ":") + 1;
1837                 else
1838                         desc = "alloc,time,jit";
1839                 args = g_strsplit (desc, ",", -1);
1840
1841                 for (ptr = args; ptr && *ptr; ptr++) {
1842                         const char *arg = *ptr;
1843
1844                         // Alwais listen to appdomaon events to shutdown at the first unload
1845                         flags |= MONO_PROFILE_APPDOMAIN_EVENTS;
1846                         if (!strcmp (arg, "time"))
1847                                 flags |= MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_EXCEPTIONS;
1848                         else if (!strcmp (arg, "alloc"))
1849                                 flags |= MONO_PROFILE_ALLOCATIONS;
1850                         else if (!strcmp (arg, "stat"))
1851                                 flags |= MONO_PROFILE_STATISTICAL;
1852                         else if (!strcmp (arg, "jit"))
1853                                 flags |= MONO_PROFILE_JIT_COMPILATION;
1854                         else if (strncmp (arg, "file=", 5) == 0) {
1855                                 poutput = fopen (arg + 5, "wb");
1856                                 if (!poutput) {
1857                                         poutput = stdout;
1858                                         fprintf (stderr, "profiler : cannot open profile output file '%s'.\n", arg + 5);
1859                                 }
1860                         } else {
1861                                 fprintf (stderr, "profiler : Unknown argument '%s'.\n", arg);
1862                                 return;
1863                         }
1864                 }
1865         }
1866         if (flags & MONO_PROFILE_ALLOCATIONS)
1867                 flags |= MONO_PROFILE_GC_MOVES;
1868         if (flags & MONO_PROFILE_ALLOCATIONS)
1869                 flags |= MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_EXCEPTIONS;
1870         if (!flags)
1871                 flags = MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_JIT_COMPILATION | MONO_PROFILE_EXCEPTIONS;
1872
1873         prof = create_profiler ();
1874         ALLOC_PROFILER ();
1875         SET_PROFILER (prof);
1876
1877         /* statistical profiler data */
1878         prof_addresses = g_new0 (gpointer, MAX_PROF_SAMPLES);
1879         prof_table = g_hash_table_new (g_str_hash, g_str_equal);
1880
1881         mono_profiler_install (prof, simple_shutdown);
1882         mono_profiler_install_enter_leave (simple_method_enter, simple_method_leave);
1883         mono_profiler_install_exception (NULL, simple_method_leave, NULL);
1884         mono_profiler_install_jit_compile (simple_method_jit, simple_method_end_jit);
1885         mono_profiler_install_allocation (simple_allocation);
1886         mono_profiler_install_appdomain (NULL, simple_appdomain_load, simple_appdomain_unload, NULL);
1887         mono_profiler_install_statistical (simple_stat_hit);
1888         mono_profiler_install_gc_moves (simple_gc_move);
1889         mono_profiler_set_events (flags);
1890 }
1891
1892 #endif /* DISABLE_PROFILER */
1893
1894 typedef void (*ProfilerInitializer) (const char*);
1895 #define INITIALIZER_NAME "mono_profiler_startup"
1896
1897 /**
1898  * mono_profiler_load:
1899  * @desc: arguments to configure the profiler
1900  *
1901  * Invoke this method to initialize the profiler.   This will drive the
1902  * loading of the internal ("default") or any external profilers.
1903  *
1904  * This routine is invoked by Mono's driver, but must be called manually
1905  * if you embed Mono into your application.
1906  */
1907 void 
1908 mono_profiler_load (const char *desc)
1909 {
1910         mono_gc_base_init ();
1911
1912 #ifndef DISABLE_PROFILER
1913         if (!desc || (strcmp ("default", desc) == 0) || (strncmp (desc, "default:", 8) == 0)) {
1914                 mono_profiler_install_simple (desc);
1915                 return;
1916         }
1917 #else
1918         if (!desc) {
1919                 desc = "default";
1920         }
1921 #endif
1922         {
1923                 MonoDl *pmodule = NULL;
1924                 const char* col = strchr (desc, ':');
1925                 char* libname;
1926                 char* path;
1927                 char *mname;
1928                 char *err;
1929                 void *iter;
1930                 if (col != NULL) {
1931                         mname = g_memdup (desc, col - desc + 1);
1932                         mname [col - desc] = 0;
1933                 } else {
1934                         mname = g_strdup (desc);
1935                 }
1936                 libname = g_strdup_printf ("mono-profiler-%s", mname);
1937                 iter = NULL;
1938                 err = NULL;
1939                 while ((path = mono_dl_build_path (NULL, libname, &iter))) {
1940                         g_free (err);
1941                         pmodule = mono_dl_open (path, MONO_DL_LAZY, &err);
1942                         if (pmodule) {
1943                                 ProfilerInitializer func;
1944                                 if ((err = mono_dl_symbol (pmodule, INITIALIZER_NAME, (gpointer *)&func))) {
1945                                         g_warning ("Cannot find initializer function %s in profiler module: %s (%s)", INITIALIZER_NAME, libname, err);
1946                                         g_free (err);
1947                                         err = NULL;
1948                                 } else {
1949                                         func (desc);
1950                                 }
1951                                 break;
1952                         }
1953                         g_free (path);
1954                 }
1955                 if (!pmodule) {
1956                         g_warning ("Error loading profiler module '%s': %s", libname, err);
1957                         g_free (err);
1958                 }
1959                 g_free (libname);
1960                 g_free (mname);
1961                 g_free (path);
1962         }
1963 }
1964