Merge pull request #1020 from ludovic-henry/pr7-system-counters
authorLudovic Henry <ludovic.henry@xamarin.com>
Mon, 12 May 2014 21:19:34 +0000 (17:19 -0400)
committerLudovic Henry <ludovic.henry@xamarin.com>
Mon, 12 May 2014 21:19:34 +0000 (17:19 -0400)
[counters] Add system counters

1  2 
mono/utils/mono-counters.c

index 07c71fe60a22c06dfdab5e0830f5489b0ce3f11f,a73b1fb579b541b12b646b5fb77b928d70bfc27e..9d72ba2dcd30e7cc5ed82c5330f7a0be3ce5b336
  
  #include <stdlib.h>
  #include <glib.h>
+ #include "config.h"
  #include "mono-counters.h"
+ #include "mono-proclib.h"
+ #ifdef HAVE_UNISTD_H
+ #include <unistd.h>
+ #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)
  {
 -      switch (mono_counter_get_type (counter)) {
 -      case MONO_COUNTER_INT:
 -              return sizeof (int);
 -      case MONO_COUNTER_UINT:
 -              return sizeof (guint);
 -      case MONO_COUNTER_LONG:
 -      case MONO_COUNTER_TIME_INTERVAL:
 -              return sizeof (gint64);
 -      case MONO_COUNTER_ULONG:
 -              return sizeof (guint64);
 -      case MONO_COUNTER_WORD:
 -              return sizeof (gssize);
 -      case MONO_COUNTER_DOUBLE:
 -              return sizeof (double);
 -      case MONO_COUNTER_STRING:
 -              return -1; // FIXME
 -      default:
 -              g_assert_not_reached ();
 -      }
 +      return counter->size;
  }
  
  /**
@@@ -127,75 -100,16 +133,75 @@@ 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 & MONO_COUNTER_VARIANCE_MASK) == 0)
                type |= MONO_COUNTER_MONOTONIC;
 +      if (size < 0)
 +              size = 0;
  
        counter = malloc (sizeof (MonoCounter));
        if (!counter)
        counter->type = type;
        counter->addr = addr;
        counter->next = NULL;
 +      counter->size = size;
  
        set_mask |= type;
  
@@@ -230,42 -143,193 +236,159 @@@ 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;
 -            if (mono_counter_get_unit (counter) == MONO_COUNTER_TIME)
 -                    fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)int64val / 10000.0);
 -            else
 -                    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;
 -            fprintf (outfile, ENTRY_FMT "%zd\n", counter->name, (gint64)wordval);
 -            break;
 +              fprintf (outfile, ENTRY_FMT "%zd\n", counter->name, *(gssize*)buffer);
 +              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;
 +              fprintf (outfile, ENTRY_FMT "%.4f\n", counter->name, *(double*)buffer);
 +              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;
 +              fprintf (outfile, ENTRY_FMT "%s\n", counter->name, (counter->size == 0) ? "(null)" : (char*)buffer);
 +              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);
 +              fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)(*(gint64*)buffer) / 1000.0);
                break;
        }
 +
 +      g_free (buffer);
  }
  
+ static gint64
+ user_time ()
+ {
+       return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_USER_TIME);
+ }
+ static gint64
+ system_time ()
+ {
+       return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_SYSTEM_TIME);
+ }
+ static gint64
+ total_time ()
+ {
+       return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_TOTAL_TIME);
+ }
+ static gint64
+ working_set ()
+ {
+       return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_WORKING_SET);
+ }
+ static gint64
+ private_bytes ()
+ {
+       return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_PRIVATE_BYTES);
+ }
+ static gint64
+ virtual_bytes ()
+ {
+       return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_VIRTUAL_BYTES);
+ }
+ static gint64
+ page_faults ()
+ {
+       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
+       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
+       return 0;
+ }
+ static double
+ cpu_load_1min ()
+ {
+       return cpu_load (0);
+ }
+ static double
+ cpu_load_5min ()
+ {
+       return cpu_load (1);
+ }
+ static double
+ cpu_load_15min ()
+ {
+       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 ()
+ {
+       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:
@@@ -281,6 -345,9 +404,9 @@@ mono_counters_foreach (CountersEnumCall
  {
        MonoCounter *counter;
  
+       if (!system_counters_initialized)
+               initialize_system_counters ();
        for (counter = counters; counter; counter = counter->next) {
                if (!cb (counter, user_data))
                        return;
@@@ -301,8 -368,6 +427,8 @@@ mono_counters_sample (MonoCounter *coun
        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);
                COPY_COUNTER (double, DoubleFunc);
                break;
        case MONO_COUNTER_STRING:
 -              // FIXME : add support for string sampling
 -              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;
@@@ -351,11 -408,11 +477,11 @@@ section_names [][10] = 
  };
  
  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) && (mono_counter_get_variance (counter) & section))
 +              if ((counter->type & section) && (mono_counter_get_variance (counter) & variance))
                        dump_counter (counter, outfile);
                counter = counter->next;
        }
@@@ -374,22 -431,18 +500,22 @@@ voi
  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 (!(section_mask & MONO_COUNTER_VARIANCE_MASK))
 -              section_mask |= MONO_COUNTER_VARIANCE_MASK;
 +      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 | (section_mask & MONO_COUNTER_VARIANCE_MASK), outfile);
 +                      mono_counters_dump_section (i, variance, outfile);
                }
        }