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