/*
* Copyright 2006-2010 Novell
* Copyright 2011 Xamarin Inc
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
#include <stdlib.h>
#include "config.h"
#include "mono-counters.h"
#include "mono-proclib.h"
+#include "mono-os-mutex.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
};
static MonoCounter *counters = NULL;
+static mono_mutex_t counters_mutex;
+
+static volatile gboolean initialized = FALSE;
+
static int valid_mask = 0;
static int set_mask = 0;
+static GSList *register_callbacks = NULL;
+
+static void initialize_system_counters (void);
+
/**
* mono_counter_get_variance:
* @counter: counter to get the variance
valid_mask = section_mask & MONO_COUNTER_SECTION_MASK;
}
+void
+mono_counters_init (void)
+{
+ if (initialized)
+ return;
+
+ mono_os_mutex_init (&counters_mutex);
+
+ initialize_system_counters ();
+
+ initialized = TRUE;
+}
+
+static void
+register_internal (const char *name, int type, void *addr, int size)
+{
+ MonoCounter *counter;
+ GSList *register_callback;
+
+ g_assert (size >= 0);
+ if ((type & MONO_COUNTER_VARIANCE_MASK) == 0)
+ type |= MONO_COUNTER_MONOTONIC;
+
+ mono_os_mutex_lock (&counters_mutex);
+
+ for (counter = counters; counter; counter = counter->next) {
+ if (counter->addr == addr) {
+ g_warning ("you are registering twice the same counter address");
+ mono_os_mutex_unlock (&counters_mutex);
+ return;
+ }
+ }
+
+ counter = (MonoCounter *) malloc (sizeof (MonoCounter));
+ if (!counter) {
+ mono_os_mutex_unlock (&counters_mutex);
+ return;
+ }
+ counter->name = g_strdup (name);
+ counter->type = type;
+ counter->addr = addr;
+ counter->next = NULL;
+ counter->size = size;
+
+ set_mask |= type;
+
+ /* Append */
+ if (counters) {
+ MonoCounter *item = counters;
+ while (item->next)
+ item = item->next;
+ item->next = counter;
+ } else {
+ counters = counter;
+ }
+
+ for (register_callback = register_callbacks; register_callback; register_callback = register_callback->next)
+ ((MonoCounterRegisterCallback)register_callback->data) (counter);
+
+ mono_os_mutex_unlock (&counters_mutex);
+}
+
/**
* mono_counters_register:
* @name: The name for this counters.
default:
g_assert_not_reached ();
}
- mono_counters_register_with_size (name, type, addr, size);
+
+ if (!initialized)
+ g_debug ("counters not enabled");
+ else
+ register_internal (name, type, addr, size);
}
/**
void
mono_counters_register_with_size (const char *name, int type, void *addr, int size)
{
- MonoCounter *counter;
-
- if ((type & MONO_COUNTER_VARIANCE_MASK) == 0)
- type |= MONO_COUNTER_MONOTONIC;
- if (size < 0)
- size = 0;
+ if (!initialized)
+ g_debug ("counters not enabled");
+ else
+ register_internal (name, type, addr, size);
+}
- counter = malloc (sizeof (MonoCounter));
- if (!counter)
+/**
+ * mono_counters_on_register
+ * @callback : function to callback when a counter is registered
+ *
+ * Add a callback that is going to be called when a counter is registered
+ */
+void
+mono_counters_on_register (MonoCounterRegisterCallback callback)
+{
+ if (!initialized) {
+ g_debug ("counters not enabled");
return;
- counter->name = name;
- counter->type = type;
- counter->addr = addr;
- counter->next = NULL;
- counter->size = size;
-
- set_mask |= type;
-
- /* Append */
- if (counters) {
- MonoCounter *item = counters;
- while (item->next)
- item = item->next;
- item->next = counter;
- } else {
- counters = counter;
}
+
+ mono_os_mutex_lock (&counters_mutex);
+ register_callbacks = g_slist_append (register_callbacks, (gpointer) callback);
+ mono_os_mutex_unlock (&counters_mutex);
}
typedef int (*IntFunc) (void);
typedef double (*DoubleFunc) (void);
typedef char* (*StrFunc) (void);
-#define ENTRY_FMT "%-36s: "
-static void
-dump_counter (MonoCounter *counter, FILE *outfile) {
- void *buffer = g_malloc0 (counter->size);
- mono_counters_sample (counter, buffer, counter->size);
-
- switch (counter->type & MONO_COUNTER_TYPE_MASK) {
- case MONO_COUNTER_INT:
- fprintf (outfile, ENTRY_FMT "%d\n", counter->name, *(int*)buffer);
- break;
- case MONO_COUNTER_UINT:
- fprintf (outfile, ENTRY_FMT "%u\n", counter->name, *(guint*)buffer);
- break;
- case MONO_COUNTER_LONG:
- if ((counter->type & MONO_COUNTER_UNIT_MASK) == MONO_COUNTER_TIME)
- fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)(*(gint64*)buffer) / 10000.0);
- else
- fprintf (outfile, ENTRY_FMT "%lld\n", counter->name, *(gint64*)buffer);
- break;
- case MONO_COUNTER_ULONG:
- fprintf (outfile, ENTRY_FMT "%llu\n", counter->name, *(guint64*)buffer);
- break;
- case MONO_COUNTER_WORD:
- fprintf (outfile, ENTRY_FMT "%zd\n", counter->name, *(gssize*)buffer);
- break;
- case MONO_COUNTER_DOUBLE:
- fprintf (outfile, ENTRY_FMT "%.4f\n", counter->name, *(double*)buffer);
- break;
- case MONO_COUNTER_STRING:
- fprintf (outfile, ENTRY_FMT "%s\n", counter->name, (counter->size == 0) ? "(null)" : (char*)buffer);
- break;
- case MONO_COUNTER_TIME_INTERVAL:
- fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)(*(gint64*)buffer) / 1000.0);
- break;
- }
-
- g_free (buffer);
-}
-
static gint64
-user_time ()
+user_time (void)
{
return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_USER_TIME);
}
static gint64
-system_time ()
+system_time (void)
{
return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_SYSTEM_TIME);
}
static gint64
-total_time ()
+total_time (void)
{
return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_TOTAL_TIME);
}
static gint64
-working_set ()
+working_set (void)
{
return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_WORKING_SET);
}
static gint64
-private_bytes ()
+private_bytes (void)
{
return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_PRIVATE_BYTES);
}
static gint64
-virtual_bytes ()
+virtual_bytes (void)
{
return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_VIRTUAL_BYTES);
}
static gint64
-page_faults ()
+page_faults (void)
{
return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_FAULTS);
}
FILE *f = fopen ("/proc/loadavg", "r");
if (f) {
len = fread (buffer, 1, sizeof (buffer) - 1, f);
+ fclose (f);
if (len > 0) {
buffer [len < 511 ? len : 511] = 0;
b = buffer;
}
}
}
- fclose (f);
}
#endif
return 0;
}
static double
-cpu_load_1min ()
+cpu_load_1min (void)
{
return cpu_load (0);
}
static double
-cpu_load_5min ()
+cpu_load_5min (void)
{
return cpu_load (1);
}
static double
-cpu_load_15min ()
+cpu_load_15min (void)
{
return cpu_load (2);
}
-static gboolean system_counters_initialized = FALSE;
-
#define SYSCOUNTER_TIME (MONO_COUNTER_SYSTEM | MONO_COUNTER_LONG | MONO_COUNTER_TIME | MONO_COUNTER_MONOTONIC | MONO_COUNTER_CALLBACK)
#define SYSCOUNTER_BYTES (MONO_COUNTER_SYSTEM | MONO_COUNTER_LONG | MONO_COUNTER_BYTES | MONO_COUNTER_VARIABLE | MONO_COUNTER_CALLBACK)
#define SYSCOUNTER_COUNT (MONO_COUNTER_SYSTEM | MONO_COUNTER_LONG | MONO_COUNTER_COUNT | MONO_COUNTER_MONOTONIC | MONO_COUNTER_CALLBACK)
#define SYSCOUNTER_LOAD (MONO_COUNTER_SYSTEM | MONO_COUNTER_DOUBLE | MONO_COUNTER_PERCENTAGE | MONO_COUNTER_VARIABLE | MONO_COUNTER_CALLBACK)
static void
-initialize_system_counters ()
+initialize_system_counters (void)
{
- mono_counters_register ("User Time", SYSCOUNTER_TIME, &user_time);
- mono_counters_register ("System Time", SYSCOUNTER_TIME, &system_time);
- mono_counters_register ("Total Time", SYSCOUNTER_TIME, &total_time);
- mono_counters_register ("Working Set", SYSCOUNTER_BYTES, &working_set);
- mono_counters_register ("Private Bytes", SYSCOUNTER_BYTES, &private_bytes);
- mono_counters_register ("Virtual Bytes", SYSCOUNTER_BYTES, &virtual_bytes);
- mono_counters_register ("Page Faults", SYSCOUNTER_COUNT, &page_faults);
- mono_counters_register ("CPU Load Average - 1min", SYSCOUNTER_LOAD, &cpu_load_1min);
- mono_counters_register ("CPU Load Average - 5min", SYSCOUNTER_LOAD, &cpu_load_5min);
- mono_counters_register ("CPU Load Average - 15min", SYSCOUNTER_LOAD, &cpu_load_15min);
-
- system_counters_initialized = TRUE;
+ register_internal ("User Time", SYSCOUNTER_TIME, (gpointer) &user_time, sizeof (gint64));
+ register_internal ("System Time", SYSCOUNTER_TIME, (gpointer) &system_time, sizeof (gint64));
+ register_internal ("Total Time", SYSCOUNTER_TIME, (gpointer) &total_time, sizeof (gint64));
+ register_internal ("Working Set", SYSCOUNTER_BYTES, (gpointer) &working_set, sizeof (gint64));
+ register_internal ("Private Bytes", SYSCOUNTER_BYTES, (gpointer) &private_bytes, sizeof (gint64));
+ register_internal ("Virtual Bytes", SYSCOUNTER_BYTES, (gpointer) &virtual_bytes, sizeof (gint64));
+ register_internal ("Page Faults", SYSCOUNTER_COUNT, (gpointer) &page_faults, sizeof (gint64));
+ register_internal ("CPU Load Average - 1min", SYSCOUNTER_LOAD, (gpointer) &cpu_load_1min, sizeof (double));
+ register_internal ("CPU Load Average - 5min", SYSCOUNTER_LOAD, (gpointer) &cpu_load_5min, sizeof (double));
+ register_internal ("CPU Load Average - 15min", SYSCOUNTER_LOAD, (gpointer) &cpu_load_15min, sizeof (double));
}
/**
{
MonoCounter *counter;
- if (!system_counters_initialized)
- initialize_system_counters ();
+ if (!initialized) {
+ g_debug ("counters not enabled");
+ return;
+ }
+
+ mono_os_mutex_lock (&counters_mutex);
for (counter = counters; counter; counter = counter->next) {
- if (!cb (counter, user_data))
+ if (!cb (counter, user_data)) {
+ mono_os_mutex_unlock (&counters_mutex);
return;
+ }
}
+
+ mono_os_mutex_unlock (&counters_mutex);
}
#define COPY_COUNTER(type,functype) do { \
size = sizeof (type); \
if (buffer_size < size) \
- return -1; \
- type __var = cb ? ((functype)counter->addr) () : *(type*)counter->addr; \
- memcpy (buffer, &__var, size); \
+ size = -1; \
+ else \
+ *(type*)buffer = cb ? ((functype)counter->addr) () : *(type*)counter->addr; \
} while (0);
-int
-mono_counters_sample (MonoCounter *counter, void *buffer, int buffer_size)
+/* lockless */
+static int
+sample_internal (MonoCounter *counter, void *buffer, int buffer_size)
{
int cb = counter->type & MONO_COUNTER_CALLBACK;
int size = -1;
COPY_COUNTER (double, DoubleFunc);
break;
case MONO_COUNTER_STRING:
- if (buffer_size < counter->size)
- return -1;
- if (counter->size == 0)
- return 0;
- strval = cb ? ((StrFunc)counter->addr) () : (char*)counter->addr;
- if (!strval)
- return 0;
- size = counter->size;
- strncpy (buffer, strval, size - 1);
- ((char*)buffer)[size - 1] = '\0';
+ if (buffer_size < counter->size) {
+ size = -1;
+ } else if (counter->size == 0) {
+ size = 0;
+ } else {
+ strval = cb ? ((StrFunc)counter->addr) () : (char*)counter->addr;
+ if (!strval) {
+ size = 0;
+ } else {
+ size = counter->size;
+ strncpy ((char *) buffer, strval, size - 1);
+ ((char*)buffer)[size - 1] = '\0';
+ }
+ }
}
return size;
}
+int
+mono_counters_sample (MonoCounter *counter, void *buffer, int buffer_size)
+{
+ if (!initialized) {
+ g_debug ("counters not enabled");
+ return -1;
+ }
+
+ return sample_internal (counter, buffer, buffer_size);
+}
+
+#define ENTRY_FMT "%-36s: "
+static void
+dump_counter (MonoCounter *counter, FILE *outfile) {
+ void *buffer = g_malloc0 (counter->size);
+ int size = sample_internal (counter, buffer, counter->size);
+
+ switch (counter->type & MONO_COUNTER_TYPE_MASK) {
+ case MONO_COUNTER_INT:
+ fprintf (outfile, ENTRY_FMT "%d\n", counter->name, *(int*)buffer);
+ break;
+ case MONO_COUNTER_UINT:
+ fprintf (outfile, ENTRY_FMT "%u\n", counter->name, *(guint*)buffer);
+ break;
+ case MONO_COUNTER_LONG:
+ if ((counter->type & MONO_COUNTER_UNIT_MASK) == MONO_COUNTER_TIME)
+ fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)(*(gint64*)buffer) / 10000.0);
+ else
+ fprintf (outfile, ENTRY_FMT "%lld\n", counter->name, *(long long *)buffer);
+ break;
+ case MONO_COUNTER_ULONG:
+ if ((counter->type & MONO_COUNTER_UNIT_MASK) == MONO_COUNTER_TIME)
+ fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)(*(guint64*)buffer) / 10000.0);
+ else
+ fprintf (outfile, ENTRY_FMT "%llu\n", counter->name, *(unsigned long long *)buffer);
+ break;
+ case MONO_COUNTER_WORD:
+ fprintf (outfile, ENTRY_FMT "%zd\n", counter->name, *(gssize*)buffer);
+ break;
+ case MONO_COUNTER_DOUBLE:
+ fprintf (outfile, ENTRY_FMT "%.4f\n", counter->name, *(double*)buffer);
+ break;
+ case MONO_COUNTER_STRING:
+ fprintf (outfile, ENTRY_FMT "%s\n", counter->name, (size == 0) ? "(null)" : (char*)buffer);
+ break;
+ case MONO_COUNTER_TIME_INTERVAL:
+ fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)(*(gint64*)buffer) / 1000.0);
+ break;
+ }
+
+ g_free (buffer);
+}
+
static const char
-section_names [][10] = {
+section_names [][12] = {
"JIT",
"GC",
"Metadata",
"Security",
"Runtime",
"System",
+ "", // MONO_COUNTER_PERFCOUNTERS - not used.
+ "Profiler",
};
static void
int i, j;
int variance;
section_mask &= valid_mask;
- if (!counters)
+
+ if (!initialized)
return;
+ mono_os_mutex_lock (&counters_mutex);
+
+ if (!counters) {
+ mono_os_mutex_unlock (&counters_mutex);
+ return;
+ }
+
variance = section_mask & MONO_COUNTER_VARIANCE_MASK;
/* If no variance mask is supplied, we default to all kinds. */
}
fflush (outfile);
+ mono_os_mutex_unlock (&counters_mutex);
}
/**
void
mono_counters_cleanup (void)
{
- MonoCounter *counter = counters;
+ MonoCounter *counter;
+
+ if (!initialized)
+ return;
+
+ mono_os_mutex_lock (&counters_mutex);
+
+ counter = counters;
counters = NULL;
while (counter) {
MonoCounter *tmp = counter;
counter = counter->next;
+ free ((void*)tmp->name);
free (tmp);
}
+
+ mono_os_mutex_unlock (&counters_mutex);
}
static MonoResourceCallback limit_reached = NULL;