X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Futils%2Fmono-counters.c;h=2f642bb3ec19473a604993426ee1146202cce6d1;hb=d4014f16df461fff6b60ae927fc52d3c4d528ca9;hp=e55114beee80b12b5e29c90b0a3c28b68d93bfc6;hpb=9b3232e7df30dc895bf1d033196d106ae555c4a0;p=mono.git diff --git a/mono/utils/mono-counters.c b/mono/utils/mono-counters.c index e55114beee8..2f642bb3ec1 100644 --- a/mono/utils/mono-counters.c +++ b/mono/utils/mono-counters.c @@ -5,21 +5,112 @@ #include #include +#include "config.h" #include "mono-counters.h" +#include "mono-proclib.h" -typedef struct _MonoCounter MonoCounter; +#ifdef HAVE_UNISTD_H +#include +#endif struct _MonoCounter { MonoCounter *next; const char *name; void *addr; int type; + size_t size; }; static MonoCounter *counters = NULL; static int valid_mask = 0; static int set_mask = 0; +/** + * mono_counter_get_variance: + * @counter: counter to get the variance + * + * Variance specifies how the counter value is expected to behave between any two samplings. + * + * Returns: the monotonicity of the counter. + */ +int +mono_counter_get_variance (MonoCounter *counter) +{ + return counter->type & MONO_COUNTER_VARIANCE_MASK; +} + +/** + * mono_counter_get_unit: + * @counter: counter to get the unit + * + * The unit gives a high level view of the unit that the counter is measuring. + * + * Returns: the unit of the counter. + */ +int +mono_counter_get_unit (MonoCounter *counter) +{ + return counter->type & MONO_COUNTER_UNIT_MASK; +} + +/** + * mono_counter_get_section: + * @counter: counter to get the section + * + * Sections are the unit of organization between all counters. + * + * Returns: the section of the counter. + */ + +int +mono_counter_get_section (MonoCounter *counter) +{ + return counter->type & MONO_COUNTER_SECTION_MASK; +} + +/** + * mono_counter_get_type: + * @counter: counter to get the type + * + * Returns the type used to strong the value of the counter. + * + * Returns:the type of the counter. + */ +int +mono_counter_get_type (MonoCounter *counter) +{ + return counter->type & MONO_COUNTER_TYPE_MASK; +} + +/** + * mono_counter_get_name: + * @counter: counter to get the name + * + * Returns the counter name. The string should not be freed. + * + * Returns the name of the counter. + */ + +const char* +mono_counter_get_name (MonoCounter *counter) +{ + return counter->name; +} + +/** + * mono_counter_get_size: + * @counter: counter to get the max size of the counter + * + * Use the returned size to create the buffer used with mono_counters_sample + * + * Returns: the max size of the counter data. + */ +size_t +mono_counter_get_size (MonoCounter *counter) +{ + return counter->size; +} + /** * mono_counters_enable: * @section_mask: a mask listing the sections that will be displayed @@ -42,15 +133,76 @@ mono_counters_enable (int section_mask) * Note that @name must be a valid string at all times until * mono_counters_dump () is called. * + * This function should not be used with counter types that require an explicit size such as string + * as the counter size will be set to zero making them effectively useless. + * + * * It may be a function pointer if MONO_COUNTER_CALLBACK is specified: * the function should return the value and take no arguments. */ void mono_counters_register (const char* name, int type, void *addr) +{ + int size; + switch (type & MONO_COUNTER_TYPE_MASK) { + case MONO_COUNTER_INT: + size = sizeof (int); + break; + case MONO_COUNTER_UINT: + size = sizeof (guint); + break; + case MONO_COUNTER_LONG: + case MONO_COUNTER_TIME_INTERVAL: + size = sizeof (gint64); + break; + case MONO_COUNTER_ULONG: + size = sizeof (guint64); + break; + case MONO_COUNTER_WORD: + size = sizeof (gssize); + break; + case MONO_COUNTER_DOUBLE: + size = sizeof (double); + break; + case MONO_COUNTER_STRING: + size = 0; + break; + default: + g_assert_not_reached (); + } + mono_counters_register_with_size (name, type, addr, size); +} + +/** + * mono_counters_register_with_size: + * @name: The name for this counters. + * @type: One of the possible MONO_COUNTER types, or MONO_COUNTER_CALLBACK for a function pointer. + * @addr: The address to register. + * @size: Max size of the counter data. + * + * Register addr as the address of a counter of type @type. + * Note that @name must be a valid string at all times until + * mono_counters_dump () is called. + * + * It may be a function pointer if MONO_COUNTER_CALLBACK is specified: + * the function should return the value and take no arguments. + * + * The value of @size is ignored for types with fixed size such as int and long. + * + * Use @size for types that can have dynamic size such as string. + * + * If @size is negative, it's silently converted to zero. + */ +void +mono_counters_register_with_size (const char *name, int type, void *addr, int size) { MonoCounter *counter; - if (!(type & valid_mask)) - return; + + if ((type & MONO_COUNTER_VARIANCE_MASK) == 0) + type |= MONO_COUNTER_MONOTONIC; + if (size < 0) + size = 0; + counter = malloc (sizeof (MonoCounter)); if (!counter) return; @@ -58,6 +210,7 @@ mono_counters_register (const char* name, int type, void *addr) counter->type = type; counter->addr = addr; counter->next = NULL; + counter->size = size; set_mask |= type; @@ -83,75 +236,232 @@ typedef char* (*StrFunc) (void); #define ENTRY_FMT "%-36s: " static void dump_counter (MonoCounter *counter, FILE *outfile) { - int intval; - guint uintval; - gint64 int64val; - guint64 uint64val; - gssize wordval; - double dval; - const char *str; + void *buffer = g_malloc0 (counter->size); + mono_counters_sample (counter, buffer, counter->size); + switch (counter->type & MONO_COUNTER_TYPE_MASK) { case MONO_COUNTER_INT: - if (counter->type & MONO_COUNTER_CALLBACK) - intval = ((IntFunc)counter->addr) (); - else - intval = *(int*)counter->addr; - fprintf (outfile, ENTRY_FMT "%d\n", counter->name, intval); - break; + fprintf (outfile, ENTRY_FMT "%d\n", counter->name, *(int*)buffer); + break; case MONO_COUNTER_UINT: - if (counter->type & MONO_COUNTER_CALLBACK) - uintval = ((UIntFunc)counter->addr) (); - else - uintval = *(guint*)counter->addr; - fprintf (outfile, ENTRY_FMT "%u\n", counter->name, uintval); - break; + fprintf (outfile, ENTRY_FMT "%u\n", counter->name, *(guint*)buffer); + break; case MONO_COUNTER_LONG: - if (counter->type & MONO_COUNTER_CALLBACK) - int64val = ((LongFunc)counter->addr) (); - else - int64val = *(gint64*)counter->addr; - fprintf (outfile, ENTRY_FMT "%lld\n", counter->name, (long long)int64val); - break; + 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: - if (counter->type & MONO_COUNTER_CALLBACK) - uint64val = ((ULongFunc)counter->addr) (); - else - uint64val = *(guint64*)counter->addr; - fprintf (outfile, ENTRY_FMT "%llu\n", counter->name, (unsigned long long)uint64val); - break; + fprintf (outfile, ENTRY_FMT "%llu\n", counter->name, *(guint64*)buffer); + break; case MONO_COUNTER_WORD: - if (counter->type & MONO_COUNTER_CALLBACK) - wordval = ((PtrFunc)counter->addr) (); - else - wordval = *(gssize*)counter->addr; -#if SIZEOF_VOID_P == 8 - fprintf (outfile, ENTRY_FMT "%lld\n", counter->name, (gint64)wordval); + 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 (void) +{ + return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_USER_TIME); +} + +static gint64 +system_time (void) +{ + return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_SYSTEM_TIME); +} + +static gint64 +total_time (void) +{ + return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_TOTAL_TIME); +} + +static gint64 +working_set (void) +{ + return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_WORKING_SET); +} + +static gint64 +private_bytes (void) +{ + return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_PRIVATE_BYTES); +} + +static gint64 +virtual_bytes (void) +{ + return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_VIRTUAL_BYTES); +} + +static gint64 +page_faults (void) +{ + return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_FAULTS); +} + +static double +cpu_load (int kind) +{ +#if defined(TARGET_WIN32) +#elif defined(TARGET_MACH) + double load [3]; + if (getloadavg (load, 3) > 0) + return load [kind]; #else - fprintf (outfile, ENTRY_FMT "%d\n", counter->name, (gint)wordval); + char buffer[512], *b; + int len, i; + FILE *f = fopen ("/proc/loadavg", "r"); + if (f) { + len = fread (buffer, 1, sizeof (buffer) - 1, f); + if (len > 0) { + buffer [len < 511 ? len : 511] = 0; + b = buffer; + for (i = 0; i < 3; i++) { + if (kind == i) + return strtod (b, NULL); + if (i < 2) { + b = strchr (b, ' '); + if (!b) + return 0; + b += 1; + } + } + } + fclose (f); + } #endif - break; + return 0; +} + +static double +cpu_load_1min (void) +{ + return cpu_load (0); +} + +static double +cpu_load_5min (void) +{ + return cpu_load (1); +} + +static double +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 (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; +} + +/** + * mono_counters_foreach: + * @cb: The callback that will be called for each counter. + * @user_data: Value passed as second argument of the callback. + * + * Iterate over all counters and call @cb for each one of them. Stop iterating if + * the callback returns FALSE. + * + */ +void +mono_counters_foreach (CountersEnumCallback cb, gpointer user_data) +{ + MonoCounter *counter; + + if (!system_counters_initialized) + initialize_system_counters (); + + for (counter = counters; counter; counter = counter->next) { + if (!cb (counter, user_data)) + return; + } +} + +#define COPY_COUNTER(type,functype) do { \ + size = sizeof (type); \ + if (buffer_size < size) \ + return -1; \ + *(type*)buffer = cb ? ((functype)counter->addr) () : *(type*)counter->addr; \ + } while (0); + +int +mono_counters_sample (MonoCounter *counter, void *buffer, int buffer_size) +{ + int cb = counter->type & MONO_COUNTER_CALLBACK; + int size = -1; + + char *strval; + + switch (mono_counter_get_type (counter)) { + case MONO_COUNTER_INT: + COPY_COUNTER (int, IntFunc); + break; + case MONO_COUNTER_UINT: + COPY_COUNTER (guint, UIntFunc); + break; + case MONO_COUNTER_LONG: + case MONO_COUNTER_TIME_INTERVAL: + COPY_COUNTER (gint64, LongFunc); + break; + case MONO_COUNTER_ULONG: + COPY_COUNTER (guint64, ULongFunc); + break; + case MONO_COUNTER_WORD: + COPY_COUNTER (gssize, PtrFunc); + break; case MONO_COUNTER_DOUBLE: - if (counter->type & MONO_COUNTER_CALLBACK) - dval = ((DoubleFunc)counter->addr) (); - else - dval = *(double*)counter->addr; - fprintf (outfile, ENTRY_FMT "%.4f\n", counter->name, dval); - break; + COPY_COUNTER (double, DoubleFunc); + break; case MONO_COUNTER_STRING: - if (counter->type & MONO_COUNTER_CALLBACK) - str = ((StrFunc)counter->addr) (); - else - str = *(char**)counter->addr; - fprintf (outfile, ENTRY_FMT "%s\n", counter->name, str); - break; - case MONO_COUNTER_TIME_INTERVAL: - if (counter->type & MONO_COUNTER_CALLBACK) - int64val = ((LongFunc)counter->addr) (); - else - int64val = *(gint64*)counter->addr; - fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)int64val / 1000.0); - break; + 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'; } + + return size; } static const char @@ -160,15 +470,17 @@ section_names [][10] = { "GC", "Metadata", "Generics", - "Security" + "Security", + "Runtime", + "System", }; static void -mono_counters_dump_section (int section, FILE *outfile) +mono_counters_dump_section (int section, int variance, FILE *outfile) { MonoCounter *counter = counters; while (counter) { - if (counter->type & section) + if ((counter->type & section) && (mono_counter_get_variance (counter) & variance)) dump_counter (counter, outfile); counter = counter->next; } @@ -180,18 +492,29 @@ mono_counters_dump_section (int section, FILE *outfile) * @outfile: a FILE to dump the results to * * Displays the counts of all the enabled counters registered. + * To filter by variance, you can OR one or more variance with the specific section you want. + * Use MONO_COUNTER_SECTION_MASK to dump all categories of a specific variance. */ void mono_counters_dump (int section_mask, FILE *outfile) { int i, j; + int variance; section_mask &= valid_mask; if (!counters) return; + + variance = section_mask & MONO_COUNTER_VARIANCE_MASK; + + /* If no variance mask is supplied, we default to all kinds. */ + if (!variance) + variance = MONO_COUNTER_VARIANCE_MASK; + section_mask &= ~MONO_COUNTER_VARIANCE_MASK; + for (j = 0, i = MONO_COUNTER_JIT; i < MONO_COUNTER_LAST_SECTION; j++, i <<= 1) { if ((section_mask & i) && (set_mask & i)) { fprintf (outfile, "\n%s statistics\n", section_names [j]); - mono_counters_dump_section (i, outfile); + mono_counters_dump_section (i, variance, outfile); } } @@ -207,12 +530,12 @@ void mono_counters_cleanup (void) { MonoCounter *counter = counters; + counters = NULL; while (counter) { - MonoCounter *tmp = counters; + MonoCounter *tmp = counter; counter = counter->next; free (tmp); } - counters = NULL; } static MonoResourceCallback limit_reached = NULL;