-#ifndef DISABLE_PROFILER
-/*
- * Small profiler extracted from mint: we should move it in a loadable module
- * and improve it to do graphs and more accurate timestamping with rdtsc.
- */
-
-static FILE* poutput = NULL;
-
-#define USE_X86TSC 0
-#define USE_WIN32COUNTER 0
-#if USE_X86TSC
-
-typedef struct {
- unsigned int lows, highs, lowe, highe;
-} MonoRdtscTimer;
-
-#define rdtsc(low,high) \
- __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
-
-static int freq;
-
-static double
-rdtsc_elapsed (MonoRdtscTimer *t)
-{
- unsigned long long diff;
- unsigned int highe = t->highe;
- if (t->lowe < t->lows)
- highe--;
- diff = (((unsigned long long) highe - t->highs) << 32) + (t->lowe - t->lows);
- return ((double)diff / freq) / 1000000; /* have to return the result in seconds */
-}
-
-static int
-have_rdtsc (void) {
- char buf[256];
- int have_freq = 0;
- int have_flag = 0;
- float val;
- FILE *cpuinfo;
-
- if (!(cpuinfo = fopen ("/proc/cpuinfo", "r")))
- return 0;
- while (fgets (buf, sizeof(buf), cpuinfo)) {
- if (sscanf (buf, "cpu MHz : %f", &val) == 1) {
- /*printf ("got mh: %f\n", val);*/
- have_freq = val;
- }
- if (strncmp (buf, "flags", 5) == 0) {
- if (strstr (buf, "tsc")) {
- have_flag = 1;
- /*printf ("have tsc\n");*/
- }
- }
- }
- fclose (cpuinfo);
- return have_flag? have_freq: 0;
-}
-
-#define MONO_TIMER_STARTUP \
- if (!(freq = have_rdtsc ())) g_error ("Compiled with TSC support, but none found");
-#define MONO_TIMER_TYPE MonoRdtscTimer
-#define MONO_TIMER_INIT(t)
-#define MONO_TIMER_DESTROY(t)
-#define MONO_TIMER_START(t) rdtsc ((t).lows, (t).highs);
-#define MONO_TIMER_STOP(t) rdtsc ((t).lowe, (t).highe);
-#define MONO_TIMER_ELAPSED(t) rdtsc_elapsed (&(t))
-
-#elif USE_WIN32COUNTER
-#include <windows.h>
-
-typedef struct {
- LARGE_INTEGER start, stop;
-} MonoWin32Timer;
-
-static int freq;
-
-static double
-win32_elapsed (MonoWin32Timer *t)
-{
- LONGLONG diff = t->stop.QuadPart - t->start.QuadPart;
- return ((double)diff / freq) / 1000000; /* have to return the result in seconds */
-}
-
-static int
-have_win32counter (void) {
- LARGE_INTEGER f;
-
- if (!QueryPerformanceFrequency (&f))
- return 0;
- return f.LowPart;
-}
-
-#define MONO_TIMER_STARTUP \
- if (!(freq = have_win32counter ())) g_error ("Compiled with Win32 counter support, but none found");
-#define MONO_TIMER_TYPE MonoWin32Timer
-#define MONO_TIMER_INIT(t)
-#define MONO_TIMER_DESTROY(t)
-#define MONO_TIMER_START(t) QueryPerformanceCounter (&(t).start)
-#define MONO_TIMER_STOP(t) QueryPerformanceCounter (&(t).stop)
-#define MONO_TIMER_ELAPSED(t) win32_elapsed (&(t))
-
-#else
-
-typedef struct {
- GTimeVal start, stop;
-} MonoGLibTimer;
-
-static double
-timeval_elapsed (MonoGLibTimer *t)
-{
- if (t->start.tv_usec > t->stop.tv_usec) {
- t->stop.tv_usec += G_USEC_PER_SEC;
- t->stop.tv_sec--;
- }
- return (t->stop.tv_sec - t->start.tv_sec)
- + ((double)(t->stop.tv_usec - t->start.tv_usec))/ G_USEC_PER_SEC;
-}
-
-#define MONO_TIMER_STARTUP
-#define MONO_TIMER_TYPE MonoGLibTimer
-#define MONO_TIMER_INIT(t)
-#define MONO_TIMER_DESTROY(t)
-#define MONO_TIMER_START(t) g_get_current_time (&(t).start)
-#define MONO_TIMER_STOP(t) g_get_current_time (&(t).stop)
-#define MONO_TIMER_ELAPSED(t) timeval_elapsed (&(t))
-#endif
-
-typedef struct _AllocInfo AllocInfo;
-typedef struct _CallerInfo CallerInfo;
-typedef struct _LastCallerInfo LastCallerInfo;
-
-struct _MonoProfiler {
- GHashTable *methods;
- MonoMemPool *mempool;
- /* info about JIT time */
- MONO_TIMER_TYPE jit_timer;
- double jit_time;
- double max_jit_time;
- MonoMethod *max_jit_method;
- int methods_jitted;
-
- GSList *per_thread;
-
- /* chain of callers for the current thread */
- LastCallerInfo *callers;
- /* LastCallerInfo nodes for faster allocation */
- LastCallerInfo *cstorage;
-};
-
-typedef struct {
- MonoMethod *method;
- guint64 count;
- double total;
- AllocInfo *alloc_info;
- CallerInfo *caller_info;
-} MethodProfile;
-
-typedef struct _MethodCallProfile MethodCallProfile;
-
-struct _MethodCallProfile {
- MethodCallProfile *next;
- MONO_TIMER_TYPE timer;
- MonoMethod *method;
-};
-
-struct _AllocInfo {
- AllocInfo *next;
- MonoClass *klass;
- guint64 count;
- guint64 mem;
-};
-
-struct _CallerInfo {
- CallerInfo *next;
- MonoMethod *caller;
- guint count;
-};
-
-struct _LastCallerInfo {
- LastCallerInfo *next;
- MonoMethod *method;
- MONO_TIMER_TYPE timer;
-};
-
-static MonoProfiler*
-create_profiler (void)
-{
- MonoProfiler *prof = g_new0 (MonoProfiler, 1);
-
- prof->methods = g_hash_table_new (mono_aligned_addr_hash, NULL);
- MONO_TIMER_INIT (prof->jit_timer);
- prof->mempool = mono_mempool_new ();
- return prof;
-}
-#if 1
-
-#ifdef HAVE_KW_THREAD
- static __thread MonoProfiler * tls_profiler;
-# define GET_PROFILER() tls_profiler
-# define SET_PROFILER(x) tls_profiler = (x)
-# define ALLOC_PROFILER() /* nop */
-#else
- static guint32 profiler_thread_id = -1;
-# define GET_PROFILER() ((MonoProfiler *)TlsGetValue (profiler_thread_id))
-# define SET_PROFILER(x) TlsSetValue (profiler_thread_id, x);
-# define ALLOC_PROFILER() profiler_thread_id = TlsAlloc ()
-#endif
-
-#define GET_THREAD_PROF(prof) do { \
- MonoProfiler *_tprofiler = GET_PROFILER (); \
- if (!_tprofiler) { \
- _tprofiler = create_profiler (); \
- prof->per_thread = g_slist_prepend (prof->per_thread, _tprofiler); \
- SET_PROFILER (_tprofiler); \
- } \
- prof = _tprofiler; \
- } while (0)
-#else
-/* thread unsafe but faster variant */
-#define GET_THREAD_PROF(prof)
-#endif
-
-static gint
-compare_profile (MethodProfile *profa, MethodProfile *profb)
-{
- return (gint)((profb->total - profa->total)*1000);
-}
-
-static void
-build_profile (MonoMethod *m, MethodProfile *prof, GList **funcs)
-{
- prof->method = m;
- *funcs = g_list_insert_sorted (*funcs, prof, (GCompareFunc)compare_profile);
-}
-
-static char*
-method_get_name (MonoMethod* method)
-{
- char *sig, *res;
-
- sig = mono_signature_get_desc (mono_method_signature (method), FALSE);
- res = g_strdup_printf ("%s%s%s::%s(%s)", method->klass->name_space,
- method->klass->name_space ? "." : "", method->klass->name,
- method->name, sig);
- g_free (sig);
- return res;
-}
-
-static void output_callers (MethodProfile *p);
-
-static void
-output_profile (GList *funcs)
-{
- GList *tmp;
- MethodProfile *p;
- char *m;
- guint64 total_calls = 0;
-
- if (funcs)
- fprintf (poutput, "Time(ms) Count P/call(ms) Method name\n");
- for (tmp = funcs; tmp; tmp = tmp->next) {
- p = tmp->data;
- total_calls += p->count;
- if (!(gint)(p->total*1000))
- continue;
- m = method_get_name (p->method);
- fprintf (poutput, "########################\n");
- fprintf (poutput, "% 8.3f ", (double) (p->total * 1000));
- fprintf (poutput, "%7llu ", (unsigned long long)p->count);
- fprintf (poutput, "% 8.3f ", (double) (p->total * 1000)/(double)p->count);
- fprintf (poutput, " %s\n", m);
-
- g_free (m);
- /* callers */
- output_callers (p);
- }
- fprintf (poutput, "Total number of calls: %lld\n", (long long)total_calls);
-}
-
-typedef struct {
- MethodProfile *mp;
- guint64 count;
-} NewobjProfile;
-
-static gint
-compare_newobj_profile (NewobjProfile *profa, NewobjProfile *profb)
-{
- if (profb->count == profa->count)
- return 0;
- else
- return profb->count > profa->count ? 1 : -1;
-}
-
-static void
-build_newobj_profile (MonoClass *class, MethodProfile *mprof, GList **funcs)
-{
- NewobjProfile *prof = g_new (NewobjProfile, 1);
- AllocInfo *tmp;
- guint64 count = 0;
-
- prof->mp = mprof;
- /* we use the total amount of memory to sort */
- for (tmp = mprof->alloc_info; tmp; tmp = tmp->next)
- count += tmp->mem;
- prof->count = count;
- *funcs = g_list_insert_sorted (*funcs, prof, (GCompareFunc)compare_newobj_profile);
-}
-
-static int
-compare_caller (CallerInfo *a, CallerInfo *b)
-{
- return b->count - a->count;
-}
-
-static int
-compare_alloc (AllocInfo *a, AllocInfo *b)
-{
- return b->mem - a->mem;
-}
-
-static GSList*
-sort_alloc_list (AllocInfo *ai)
-{
- GSList *l = NULL;
- AllocInfo *tmp;
- for (tmp = ai; tmp; tmp = tmp->next) {
- l = g_slist_insert_sorted (l, tmp, (GCompareFunc)compare_alloc);
- }
- return l;
-}
-
-static GSList*
-sort_caller_list (CallerInfo *ai)
-{
- GSList *l = NULL;
- CallerInfo *tmp;
- for (tmp = ai; tmp; tmp = tmp->next) {
- l = g_slist_insert_sorted (l, tmp, (GCompareFunc)compare_caller);
- }
- return l;
-}
-
-static void
-output_callers (MethodProfile *p) {
- guint total_callers, percent;
- GSList *sorted, *tmps;
- CallerInfo *cinfo;
- char *m;
-
- fprintf (poutput, " Callers (with count) that contribute at least for 1%%:\n");
- total_callers = 0;
- for (cinfo = p->caller_info; cinfo; cinfo = cinfo->next) {
- total_callers += cinfo->count;
- }
- sorted = sort_caller_list (p->caller_info);
- for (tmps = sorted; tmps; tmps = tmps->next) {
- cinfo = tmps->data;
- percent = (cinfo->count * 100)/total_callers;
- if (percent < 1)
- continue;
- m = method_get_name (cinfo->caller);
- fprintf (poutput, " %8d % 3d %% %s\n", cinfo->count, percent, m);
- g_free (m);
- }
-}
-
-/* This isn't defined on older glib versions and on some platforms */
-#ifndef G_GUINT64_FORMAT
-#define G_GUINT64_FORMAT "ul"
-#endif
-
-static void
-output_newobj_profile (GList *proflist)
-{
- GList *tmp;
- NewobjProfile *p;
- MethodProfile *mp;
- AllocInfo *ainfo;
- MonoClass *klass;
- const char* isarray;
- char buf [256];
- char *m;
- guint64 total = 0;
- GSList *sorted, *tmps;
-
- fprintf (poutput, "\nAllocation profiler\n");
-
- if (proflist)
- fprintf (poutput, "%-9s %s\n", "Total mem", "Method");
- for (tmp = proflist; tmp; tmp = tmp->next) {
- p = tmp->data;
- total += p->count;
- if (p->count < 50000)
- continue;
- mp = p->mp;
- m = method_get_name (mp->method);
- fprintf (poutput, "########################\n%8" G_GUINT64_FORMAT " KB %s\n", (p->count / 1024), m);
- g_free (m);
- sorted = sort_alloc_list (mp->alloc_info);
- for (tmps = sorted; tmps; tmps = tmps->next) {
- ainfo = tmps->data;
- if (ainfo->mem < 50000)
- continue;
- klass = ainfo->klass;
- if (klass->rank) {
- isarray = "[]";
- klass = klass->element_class;
- } else {
- isarray = "";
- }
- g_snprintf (buf, sizeof (buf), "%s%s%s%s",
- klass->name_space, klass->name_space ? "." : "", klass->name, isarray);
- fprintf (poutput, " %8" G_GUINT64_FORMAT " KB %8" G_GUINT64_FORMAT " %-48s\n", (ainfo->mem / 1024), ainfo->count, buf);
- }
- /* callers */
- output_callers (mp);
- }
- fprintf (poutput, "Total memory allocated: %" G_GUINT64_FORMAT " KB\n", total / 1024);
-}
-
-static void
-merge_methods (MonoMethod *method, MethodProfile *profile, MonoProfiler *prof)
-{
- MethodProfile *mprof;
- AllocInfo *talloc_info, *alloc_info;
- CallerInfo *tcaller_info, *caller_info;
-
- mprof = g_hash_table_lookup (prof->methods, method);
- if (!mprof) {
- /* the master thread didn't see this method, just transfer the info as is */
- g_hash_table_insert (prof->methods, method, profile);
- return;
- }
- /* merge the info from profile into mprof */
- mprof->count += profile->count;
- mprof->total += profile->total;
- /* merge alloc info */
- for (talloc_info = profile->alloc_info; talloc_info; talloc_info = talloc_info->next) {
- for (alloc_info = mprof->alloc_info; alloc_info; alloc_info = alloc_info->next) {
- if (alloc_info->klass == talloc_info->klass) {
- /* mprof already has a record for the klass, merge */
- alloc_info->count += talloc_info->count;
- alloc_info->mem += talloc_info->mem;
- break;
- }
- }
- if (!alloc_info) {
- /* mprof didn't have the info, just copy it over */
- alloc_info = mono_mempool_alloc0 (prof->mempool, sizeof (AllocInfo));
- *alloc_info = *talloc_info;
- alloc_info->next = mprof->alloc_info;
- mprof->alloc_info = alloc_info->next;
- }
- }
- /* merge callers info */
- for (tcaller_info = profile->caller_info; tcaller_info; tcaller_info = tcaller_info->next) {
- for (caller_info = mprof->caller_info; caller_info; caller_info = caller_info->next) {
- if (caller_info->caller == tcaller_info->caller) {
- /* mprof already has a record for the caller method, merge */
- caller_info->count += tcaller_info->count;
- break;
- }
- }
- if (!caller_info) {
- /* mprof didn't have the info, just copy it over */
- caller_info = mono_mempool_alloc0 (prof->mempool, sizeof (CallerInfo));
- *caller_info = *tcaller_info;
- caller_info->next = mprof->caller_info;
- mprof->caller_info = caller_info;
- }
- }
-}
-
-static void
-merge_thread_data (MonoProfiler *master, MonoProfiler *tprof)
-{
- master->jit_time += tprof->jit_time;
- master->methods_jitted += tprof->methods_jitted;
- if (master->max_jit_time < tprof->max_jit_time) {
- master->max_jit_time = tprof->max_jit_time;
- master->max_jit_method = tprof->max_jit_method;
- }
-
- g_hash_table_foreach (tprof->methods, (GHFunc)merge_methods, master);
-}