Fixed incorrect pointer arithmetic on SGen for MSVC.
[mono.git] / mono / metadata / mono-perfcounters.c
index b380ced4ba8376e5ceed0616482059552d39163f..ec72516a2b394f8fc4db25163ad9a6354000bbf7 100644 (file)
@@ -5,21 +5,40 @@
  *
  * Author: Paolo Molaro (lupus@ximian.com)
  *
- * (C) 2008 Novell, Inc
+ * Copyright 2008-2009 Novell, Inc (http://www.novell.com)
+ * 2011 Xamarin, Inc
  */
 
 #include "config.h"
 #include <time.h>
 #include <string.h>
 #include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if defined (__OpenBSD__)
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
+#if defined (__NetBSD__) || defined (__APPLE__)
+#include <sys/sysctl.h>
+#endif
 #include "metadata/mono-perfcounters.h"
 #include "metadata/appdomain.h"
+#include "metadata/object-internals.h"
 /* for mono_stats */
 #include "metadata/class-internals.h"
 #include "utils/mono-time.h"
+#include "utils/mono-mmap.h"
+#include "utils/mono-proclib.h"
+#include "utils/mono-networkinterfaces.h"
+#include "utils/mono-error-internals.h"
+#include "utils/atomic.h"
 #include <mono/io-layer/io-layer.h>
 
 /* map of CounterSample.cs */
@@ -34,6 +53,7 @@ struct _MonoCounterSample {
        int counterType;
 };
 
+#ifndef DISABLE_PERFCOUNTERS
 /* map of PerformanceCounterType.cs */
 enum {
        NumberOfItemsHEX32=0x00000000,
@@ -66,14 +86,42 @@ enum {
        CounterMultiBase=0x42030500
 };
 
+/* maps a small integer type to the counter types above */
+static const int
+simple_type_to_type [] = {
+       NumberOfItemsHEX32, NumberOfItemsHEX64,
+       NumberOfItems32, NumberOfItems64,
+       CounterDelta32, CounterDelta64,
+       SampleCounter, CountPerTimeInterval32,
+       CountPerTimeInterval64, RateOfCountsPerSecond32,
+       RateOfCountsPerSecond64, RawFraction,
+       CounterTimer, Timer100Ns,
+       SampleFraction, CounterTimerInverse,
+       Timer100NsInverse, CounterMultiTimer,
+       CounterMultiTimer100Ns, CounterMultiTimerInverse,
+       CounterMultiTimer100NsInverse, AverageTimer32,
+       ElapsedTime, AverageCount64,
+       SampleBase, AverageBase,
+       RawBase, CounterMultiBase
+};
+
 enum {
        SingleInstance,
        MultiInstance,
        CatTypeUnknown = -1
 };
 
-#define PERFCTR_CAT(id,name,help,type,first_counter) CATEGORY_ ## id,
-#define PERFCTR_COUNTER(id,name,help,type)
+enum {
+       ProcessInstance,
+       ThreadInstance,
+       CPUInstance,
+       MonoInstance,
+       NetworkInterfaceInstance,
+       CustomInstance
+};
+
+#define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
+#define PERFCTR_COUNTER(id,name,help,type,field)
 enum {
 #include "mono-perfcounters-def.h"
        NUM_CATEGORIES
@@ -81,8 +129,8 @@ enum {
 
 #undef PERFCTR_CAT
 #undef PERFCTR_COUNTER
-#define PERFCTR_CAT(id,name,help,type,first_counter) CATEGORY_START_ ## id = -1,
-#define PERFCTR_COUNTER(id,name,help,type) COUNTER_ ## id,
+#define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1,
+#define PERFCTR_COUNTER(id,name,help,type,field) COUNTER_ ## id,
 /* each counter is assigned an id starting from 0 inside the category */
 enum {
 #include "mono-perfcounters-def.h"
@@ -91,47 +139,142 @@ enum {
 
 #undef PERFCTR_CAT
 #undef PERFCTR_COUNTER
-#define PERFCTR_CAT(id,name,help,type,first_counter)
-#define PERFCTR_COUNTER(id,name,help,type) CCOUNTER_ ## id,
+#define PERFCTR_CAT(id,name,help,type,inst,first_counter)
+#define PERFCTR_COUNTER(id,name,help,type,field) CCOUNTER_ ## id,
 /* this is used just to count the number of counters */
 enum {
 #include "mono-perfcounters-def.h"
        NUM_COUNTERS
 };
 
+static CRITICAL_SECTION perfctr_mutex;
+#define perfctr_lock() EnterCriticalSection (&perfctr_mutex)
+#define perfctr_unlock() LeaveCriticalSection (&perfctr_mutex)
+
+typedef struct {
+       char reserved [16];
+       int size;
+       unsigned short counters_start;
+       unsigned short counters_size;
+       unsigned short data_start;
+       MonoPerfCounters counters;
+       char data [1];
+} MonoSharedArea;
+
+/*
+  binary format of custom counters in shared memory, starting from MonoSharedArea* + data_start;
+  basic stanza:
+  struct stanza_header {
+       byte stanza_type; // FTYPE_*
+       byte other_info;
+       ushort stanza_length; // includeas header
+       ... data ...
+  }
+
+// strings are utf8
+// perfcat and perfinstance are 4-bytes aligned
+struct perfcat {
+       byte typeidx;
+       byte categorytype;
+       ushort length; // includes the counters
+       ushort num_counters;
+       ushort counters_data_size;
+       int num_instances;
+       char name[]; // null terminated
+       char help[]; // null terminated
+       // perfcounters follow
+       {
+               byte countertype;
+               char name[]; // null terminated
+               char help[]; // null terminated
+       }
+       0-byte
+};
+
+struct perfinstance {
+       byte typeidx;
+       byte data_offset; // offset of counters from beginning of struct
+       ushort length;
+       uint category_offset; // offset of category in the shared area
+       char name[]; // null terminated
+       // data follows: this is always 8-byte aligned
+};
+
+*/
+
+enum {
+       FTYPE_CATEGORY = 'C',
+       FTYPE_DELETED = 'D',
+       FTYPE_PREDEF_INSTANCE = 'P', // an instance of a predef counter
+       FTYPE_INSTANCE = 'I',
+       FTYPE_DIRTY = 'd',
+       FTYPE_END = 0
+};
+
+typedef struct {
+       unsigned char ftype;
+       unsigned char extra;
+       unsigned short size;
+} SharedHeader;
+
+typedef struct {
+       SharedHeader header;
+       unsigned short num_counters;
+       unsigned short counters_data_size;
+       int num_instances;
+       /* variable length data follows */
+       char name [1];
+} SharedCategory;
+
+typedef struct {
+       SharedHeader header;
+       unsigned int category_offset;
+       /* variable length data follows */
+       char instance_name [1];
+} SharedInstance;
+
+typedef struct {
+       unsigned char type;
+       guint8 seq_num;
+       /* variable length data follows */
+       char name [1];
+} SharedCounter;
+
 typedef struct {
        const char *name;
        const char *help;
        unsigned char id;
-       signed char type;
+       signed int type : 2;
+       unsigned int instance_type : 6;
        short first_counter;
 } CategoryDesc;
 
 typedef struct {
        const char *name;
        const char *help;
-       int id;
+       short id;
+       unsigned short offset; // offset inside MonoPerfCounters
        int type;
 } CounterDesc;
 
 #undef PERFCTR_CAT
 #undef PERFCTR_COUNTER
-#define PERFCTR_CAT(id,name,help,type,first_counter) {name, help, CATEGORY_ ## id, type, CCOUNTER_ ## first_counter},
-#define PERFCTR_COUNTER(id,name,help,type)
+#define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
+#define PERFCTR_COUNTER(id,name,help,type,field)
 static const CategoryDesc
 predef_categories [] = {
 #include "mono-perfcounters-def.h"
-       {NULL, NULL, NUM_CATEGORIES, -1, NUM_COUNTERS}
+       {NULL, NULL, NUM_CATEGORIES, -1, 0, NUM_COUNTERS}
 };
 
 #undef PERFCTR_CAT
 #undef PERFCTR_COUNTER
-#define PERFCTR_CAT(id,name,help,type,first_counter)
-#define PERFCTR_COUNTER(id,name,help,type) {name, help, COUNTER_ ## id, type},
+#define PERFCTR_CAT(id,name,help,type,inst,first_counter)
+#define PERFCTR_COUNTER(id,name,help,type,field) {name, help, COUNTER_ ## id, G_STRUCT_OFFSET (MonoPerfCounters, field), type},
 static const CounterDesc
 predef_counters [] = {
 #include "mono-perfcounters-def.h"
-       {NULL, NULL, -1, 0}
+       {NULL, NULL, -1, 0, 0}
 };
 
 /*
@@ -149,17 +292,36 @@ typedef struct _ImplVtable ImplVtable;
 
 typedef MonoBoolean (*SampleFunc) (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample* sample);
 typedef gint64 (*UpdateFunc) (ImplVtable *vtable, MonoBoolean do_incr, gint64 value);
+typedef void (*CleanupFunc) (ImplVtable *vtable);
 
 struct _ImplVtable {
        void *arg;
        SampleFunc sample;
        UpdateFunc update;
+       CleanupFunc cleanup;
 };
 
+typedef struct {
+       int id;
+       char *name;
+} NetworkVtableArg;
+
+typedef struct {
+       ImplVtable vtable;
+       MonoPerfCounters *counters;
+       int pid;
+} PredefVtable;
+
+typedef struct {
+       ImplVtable vtable;
+       SharedInstance *instance_desc;
+       SharedCounter *counter_desc;
+} CustomVTable;
+
 static ImplVtable*
 create_vtable (void *arg, SampleFunc sample, UpdateFunc update)
 {
-       ImplVtable *vtable = g_new (ImplVtable, 1);
+       ImplVtable *vtable = g_new0 (ImplVtable, 1);
        vtable->arg = arg;
        vtable->sample = sample;
        vtable->update = update;
@@ -167,17 +329,215 @@ create_vtable (void *arg, SampleFunc sample, UpdateFunc update)
 }
 
 MonoPerfCounters *mono_perfcounters = NULL;
+static MonoSharedArea *shared_area = NULL;
+
+typedef struct {
+       void *sarea;
+       int refcount;
+} ExternalSArea;
+
+/* maps a pid to a ExternalSArea pointer */
+static GHashTable *pid_to_shared_area = NULL;
+
+static MonoSharedArea *
+load_sarea_for_pid (int pid)
+{
+       ExternalSArea *data;
+       MonoSharedArea *area = NULL;
+
+       perfctr_lock ();
+       if (pid_to_shared_area == NULL)
+               pid_to_shared_area = g_hash_table_new (NULL, NULL);
+       data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
+       if (!data) {
+               area = mono_shared_area_for_pid (GINT_TO_POINTER (pid));
+               if (area) {
+                       data = g_new (ExternalSArea, 1);
+                       data->sarea = area;
+                       data->refcount = 1;
+                       g_hash_table_insert (pid_to_shared_area, GINT_TO_POINTER (pid), data);
+               }
+       } else {
+               area = data->sarea;
+               data->refcount ++;
+       }
+       perfctr_unlock ();
+       return area;
+}
+
+static void
+unref_pid_unlocked (int pid)
+{
+       ExternalSArea *data;
+       data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
+       if (data) {
+               data->refcount--;
+               if (!data->refcount) {
+                       g_hash_table_remove (pid_to_shared_area, GINT_TO_POINTER (pid));
+                       mono_shared_area_unload (data->sarea);
+                       g_free (data);
+               }
+       }
+}
+
+static void
+predef_cleanup (ImplVtable *vtable)
+{
+       PredefVtable *vt = (PredefVtable*)vtable;
+       /* ExternalSArea *data; */
+       
+       perfctr_lock ();
+       if (!pid_to_shared_area) {
+               perfctr_unlock ();
+               return;
+       }
+       unref_pid_unlocked (vt->pid);
+       perfctr_unlock ();
+}
+
+static guint64
+mono_determine_physical_ram_size (void)
+{
+#if defined (TARGET_WIN32)
+       MEMORYSTATUSEX memstat;
+
+       memstat.dwLength = sizeof (memstat);
+       GlobalMemoryStatusEx (&memstat);
+       return (guint64)memstat.ullTotalPhys;
+#elif defined (__NetBSD__) || defined (__APPLE__)
+#ifdef __NetBSD__
+       unsigned long value;
+#else
+       guint64 value;
+#endif
+       int mib[2] = {
+               CTL_HW,
+#ifdef __NetBSD__
+               HW_PHYSMEM
+#else
+               HW_MEMSIZE
+#endif
+       };
+       size_t size_sys = sizeof (value);
+
+       sysctl (mib, 2, &value, &size_sys, NULL, 0);
+       if (value == 0)
+               return 134217728;
+
+       return (guint64)value;
+#elif defined (HAVE_SYSCONF)
+       guint64 page_size = 0, num_pages = 0;
+
+       /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
+        * reports invalid values, please add your OS specific code below. */
+#ifdef _SC_PAGESIZE
+       page_size = (guint64)sysconf (_SC_PAGESIZE);
+#endif
+
+#ifdef _SC_PHYS_PAGES
+       num_pages = (guint64)sysconf (_SC_PHYS_PAGES);
+#endif
+
+       if (!page_size || !num_pages) {
+               g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
+               return 134217728;
+       }
+
+       return page_size * num_pages;
+#else
+       return 134217728;
+#endif
+}
 
 void
 mono_perfcounters_init (void)
 {
-       /* later allocate in the shared memory area */
-       mono_perfcounters = g_new0 (MonoPerfCounters, 1);
+       int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
+       d_offset += 7;
+       d_offset &= ~7;
+
+       InitializeCriticalSection (&perfctr_mutex);
+
+       shared_area = mono_shared_area ();
+       shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
+       shared_area->counters_size = sizeof (MonoPerfCounters);
+       shared_area->data_start = d_offset;
+       shared_area->size = 4096;
+       mono_perfcounters = &shared_area->counters;
+}
+
+static int
+perfctr_type_compress (int type)
+{
+       int i;
+       for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
+               if (simple_type_to_type [i] == type)
+                       return i;
+       }
+       /* NumberOfItems32 */
+       return 2;
+}
+
+static unsigned char*
+shared_data_find_room (int size)
+{
+       unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
+       unsigned char *end = (unsigned char *)shared_area + shared_area->size;
+
+       size += 7;
+       size &= ~7;
+       while (p < end) {
+               unsigned short *next;
+               if (*p == FTYPE_END) {
+                       if (size < (end - p))
+                               return p;
+                       return NULL;
+               }
+               if (p + 4 > end)
+                       return NULL;
+               next = (unsigned short*)(p + 2);
+               if (*p == FTYPE_DELETED) {
+                       /* we reuse only if it's the same size */
+                       if (*next == size) {
+                               return p;
+                       }
+               }
+               p += *next;
+       }
+       return NULL;
+}
+
+typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
+
+static void
+foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
+{
+       while (p < end) {
+               unsigned short *next;
+               if (p + 4 > end)
+                       return;
+               next = (unsigned short*)(p + 2);
+               if (!func ((SharedHeader*)p, data))
+                       return;
+               if (*p == FTYPE_END)
+                       return;
+               p += *next;
+       }
+}
+
+static void
+foreach_shared_item (SharedFunc func, void *data)
+{
+       unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
+       unsigned char *end = (unsigned char *)shared_area + shared_area->size;
+
+       foreach_shared_item_in_area (p, end, func, data);
 }
 
 static int
 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
 {
+       /* FIXME: make this case insensitive */
        guint16 *strc = mono_string_chars (str);
        while (*strc == *ascii_str++) {
                if (*strc == 0)
@@ -187,6 +547,147 @@ mono_string_compare_ascii (MonoString *str, const char *ascii_str)
        return *strc - *(const unsigned char *)(ascii_str - 1);
 }
 
+typedef struct {
+       MonoString *name;
+       SharedCategory *cat;
+} CatSearch;
+
+static gboolean
+category_search (SharedHeader *header, void *data)
+{
+       CatSearch *search = data;
+       if (header->ftype == FTYPE_CATEGORY) {
+               SharedCategory *cat = (SharedCategory*)header;
+               if (mono_string_compare_ascii (search->name, cat->name) == 0) {
+                       search->cat = cat;
+                       return FALSE;
+               }
+       }
+       return TRUE;
+}
+
+static SharedCategory*
+find_custom_category (MonoString *name)
+{
+       CatSearch search;
+       search.name = name;
+       search.cat = NULL;
+       foreach_shared_item (category_search, &search);
+       return search.cat;
+}
+
+static gboolean
+category_collect (SharedHeader *header, void *data)
+{
+       GSList **list = data;
+       if (header->ftype == FTYPE_CATEGORY) {
+               *list = g_slist_prepend (*list, header);
+       }
+       return TRUE;
+}
+
+static GSList*
+get_custom_categories (void) {
+       GSList *list = NULL;
+       foreach_shared_item (category_collect, &list);
+       return list;
+}
+
+static char*
+custom_category_counters (SharedCategory* cat)
+{
+       char *p = cat->name + strlen (cat->name) + 1;
+       p += strlen (p) + 1; /* skip category help */
+       return p;
+}
+
+static SharedCounter*
+find_custom_counter (SharedCategory* cat, MonoString *name)
+{
+       int i;
+       char *p = custom_category_counters (cat);
+       for (i = 0; i < cat->num_counters; ++i) {
+               SharedCounter *counter = (SharedCounter*)p;
+               if (mono_string_compare_ascii (name, counter->name) == 0)
+                       return counter;
+               p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
+               p += strlen (p) + 1; /* skip counter help */
+       }
+       return NULL;
+}
+
+typedef struct {
+       unsigned int cat_offset;
+       SharedCategory* cat;
+       MonoString *instance;
+       SharedInstance* result;
+       GSList *list;
+} InstanceSearch;
+
+static gboolean
+instance_search (SharedHeader *header, void *data)
+{
+       InstanceSearch *search = data;
+       if (header->ftype == FTYPE_INSTANCE) {
+               SharedInstance *ins = (SharedInstance*)header;
+               if (search->cat_offset == ins->category_offset) {
+                       if (search->instance) {
+                               if (mono_string_compare_ascii (search->instance, ins->instance_name) == 0) {
+                                       search->result = ins;
+                                       return FALSE;
+                               }
+                       } else {
+                               search->list = g_slist_prepend (search->list, ins);
+                       }
+               }
+       }
+       return TRUE;
+}
+
+static SharedInstance*
+find_custom_instance (SharedCategory* cat, MonoString *instance)
+{
+       InstanceSearch search;
+       search.cat_offset = (char*)cat - (char*)shared_area;
+       search.cat = cat;
+       search.instance = instance;
+       search.list = NULL;
+       search.result = NULL;
+       foreach_shared_item (instance_search, &search);
+       return search.result;
+}
+
+static GSList*
+get_custom_instances_list (SharedCategory* cat)
+{
+       InstanceSearch search;
+       search.cat_offset = (char*)cat - (char*)shared_area;
+       search.cat = cat;
+       search.instance = NULL;
+       search.list = NULL;
+       search.result = NULL;
+       foreach_shared_item (instance_search, &search);
+       return search.list;
+}
+
+static char*
+custom_category_help (SharedCategory* cat)
+{
+       return cat->name + strlen (cat->name) + 1;
+}
+
+static const CounterDesc*
+get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
+{
+       const CounterDesc *cdesc = &predef_counters [desc->first_counter];
+       const CounterDesc *end = &predef_counters [desc [1].first_counter];
+       for (; cdesc < end; ++cdesc) {
+               if (mono_string_compare_ascii (counter, cdesc->name) == 0)
+                       return cdesc;
+       }
+       return NULL;
+}
+
 /* fill the info in sample (except the raw value) */
 static void
 fill_sample (MonoCounterSample *sample)
@@ -201,61 +702,24 @@ fill_sample (MonoCounterSample *sample)
 }
 
 static int
-id_from_string (MonoString *instance)
+id_from_string (MonoString *instance, gboolean is_process)
 {
        int id = -1;
        if (mono_string_length (instance)) {
                char *id_str = mono_string_to_utf8 (instance);
                char *end;
                id = strtol (id_str, &end, 0);
+               if (end == id_str && !is_process)
+                       id = -1;
                g_free (id_str);
-               if (end == id_str)
-                       return -1;
        }
        return id;
 }
 
-static void
-get_cpu_times (int cpu_id, gint64 *user, gint64 *systemt, gint64 *irq, gint64 *sirq, gint64 *idle)
-{
-       char buf [256];
-       char *s;
-       int hz = 100 * 2; // 2 numprocs
-       gint64 user_ticks, nice_ticks, system_ticks, idle_ticks, iowait_ticks, irq_ticks, sirq_ticks;
-       FILE *f = fopen ("/proc/stat", "r");
-       if (!f)
-               return;
-       while ((s = fgets (buf, sizeof (buf), f))) {
-               char *data = NULL;
-               if (cpu_id < 0 && strncmp (s, "cpu", 3) == 0 && g_ascii_isspace (s [3])) {
-                       data = s + 4;
-               } else if (cpu_id >= 0 && strncmp (s, "cpu", 3) == 0 && strtol (s + 3, &data, 10) == cpu_id) {
-                       if (data == s + 3)
-                               continue;
-                       data++;
-               } else {
-                       continue;
-               }
-               sscanf (data, "%Lu %Lu %Lu %Lu %Lu %Lu %Lu", &user_ticks, &nice_ticks, &system_ticks, &idle_ticks, &iowait_ticks, &irq_ticks, &sirq_ticks);
-       }
-       fclose (f);
-
-       if (user)
-               *user = (user_ticks + nice_ticks) * 10000000 / hz;
-       if (systemt)
-               *systemt = (system_ticks) * 10000000 / hz;
-       if (irq)
-               *irq = (irq_ticks) * 10000000 / hz;
-       if (sirq)
-               *sirq = (sirq_ticks) * 10000000 / hz;
-       if (idle)
-               *idle = (idle_ticks) * 10000000 / hz;
-}
-
 static MonoBoolean
 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
 {
-       gint64 value = 0;
+       MonoProcessError error;
        int id = GPOINTER_TO_INT (vtable->arg);
        int pid = id >> 5;
        id &= 0x1f;
@@ -263,27 +727,22 @@ get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *
                fill_sample (sample);
                sample->baseValue = 1;
        }
-       sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
+       sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
        switch (id) {
        case COUNTER_CPU_USER_TIME:
-               get_cpu_times (pid, &value, NULL, NULL, NULL, NULL);
-               sample->rawValue = value;
+               sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
                return TRUE;
        case COUNTER_CPU_PRIV_TIME:
-               get_cpu_times (pid, NULL, &value, NULL, NULL, NULL);
-               sample->rawValue = value;
+               sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
                return TRUE;
        case COUNTER_CPU_INTR_TIME:
-               get_cpu_times (pid, NULL, NULL, &value, NULL, NULL);
-               sample->rawValue = value;
+               sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
                return TRUE;
        case COUNTER_CPU_DCP_TIME:
-               get_cpu_times (pid, NULL, NULL, NULL, &value, NULL);
-               sample->rawValue = value;
+               sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
                return TRUE;
        case COUNTER_CPU_PROC_TIME:
-               get_cpu_times (pid, NULL, NULL, NULL, NULL, &value);
-               sample->rawValue = value;
+               sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
                return TRUE;
        }
        return FALSE;
@@ -292,103 +751,84 @@ get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *
 static void*
 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
 {
-       int id = id_from_string (instance) << 5;
-       int i;
-       const CategoryDesc *desc = &predef_categories [CATEGORY_CPU];
+       int id = id_from_string (instance, FALSE) << 5;
+       const CounterDesc *cdesc;
        *custom = FALSE;
        /* increase the shift above and the mask also in the implementation functions */
-       g_assert (32 > desc [1].first_counter - desc->first_counter);
-       for (i = desc->first_counter; i < desc [1].first_counter; ++i) {
-               const CounterDesc *cdesc = &predef_counters [i];
-               if (mono_string_compare_ascii (counter, cdesc->name) == 0) {
-                       *type = cdesc->type;
-                       return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
-               }
+       //g_assert (32 > desc [1].first_counter - desc->first_counter);
+       if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
+               *type = cdesc->type;
+               return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
        }
        return NULL;
 }
 
-/*
- * /proc/pid/stat format:
- * pid (cmdname) S 
- *     [0] ppid pgid sid tty_nr tty_pgrp flags min_flt cmin_flt maj_flt cmaj_flt
- *     [10] utime stime cutime cstime prio nice threads start_time vsize rss
- *     [20] rsslim start_code end_code start_stack esp eip pending blocked sigign sigcatch
- *     [30] wchan 0 0 exit_signal cpu rt_prio policy
- */
-
-static gint64
-get_process_time (int pid, int pos, int sum)
-{
-       char buf [512];
-       char *s, *end;
-       FILE *f;
-       int len, i;
-       gint64 value;
-
-       g_snprintf (buf, sizeof (buf), "/proc/%d/stat", pid);
-       f = fopen (buf, "r");
-       if (!f)
-               return 0;
-       len = fread (buf, 1, sizeof (buf), f);
-       fclose (f);
-       if (len <= 0)
-               return 0;
-       s = strchr (buf, ')');
-       if (!s)
-               return 0;
-       s++;
-       while (g_ascii_isspace (*s)) s++;
-       if (!*s)
-               return 0;
-       /* skip the status char */
-       while (*s && !g_ascii_isspace (*s)) s++;
-       if (!*s)
-               return 0;
-       for (i = 0; i < pos; ++i) {
-               while (g_ascii_isspace (*s)) s++;
-               if (!*s)
-                       return 0;
-               while (*s && !g_ascii_isspace (*s)) s++;
-               if (!*s)
-                       return 0;
+static MonoBoolean
+get_network_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
+{
+       MonoNetworkError error = MONO_NETWORK_ERROR_OTHER;
+       NetworkVtableArg *narg = (NetworkVtableArg*) vtable->arg;
+       if (!only_value) {
+               fill_sample (sample);
        }
-       /* we are finally at the needed item */
-       value = strtoul (s, &end, 0);
-       /* add also the following value */
-       if (sum) {
-               while (g_ascii_isspace (*s)) s++;
-               if (!*s)
-                       return 0;
-               value += strtoul (s, &end, 0);
+
+       sample->counterType = predef_counters [predef_categories [CATEGORY_NETWORK].first_counter + narg->id].type;
+       switch (narg->id) {
+       case COUNTER_NETWORK_BYTESRECSEC:
+               sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESREC, &error);
+               break;
+       case COUNTER_NETWORK_BYTESSENTSEC:
+               sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESSENT, &error);
+               break;
+       case COUNTER_NETWORK_BYTESTOTALSEC:
+               sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESTOTAL, &error);
+               break;
        }
-       return value;
+
+       if (error == MONO_NETWORK_ERROR_NONE)
+               return TRUE;
+       else
+               return FALSE;
 }
 
-static gint64
-get_pid_stat_item (int pid, const char *item)
-{
-       char buf [256];
-       char *s;
-       FILE *f;
-       int len = strlen (item);
-
-       g_snprintf (buf, sizeof (buf), "/proc/%d/status", pid);
-       f = fopen (buf, "r");
-       if (!f)
-               return 0;
-       while ((s = fgets (buf, sizeof (buf), f))) {
-               if (*item != *buf)
-                       continue;
-               if (strncmp (buf, item, len))
-                       continue;
-               if (buf [len] != ':')
-                       continue;
-               fclose (f);
-               return atoi (buf + len + 1);
-       }
-       fclose (f);
-       return 0;
+static void
+network_cleanup (ImplVtable *vtable)
+{
+       NetworkVtableArg *narg;
+
+       if (vtable == NULL)
+               return;
+
+       narg = vtable->arg;
+       if (narg == NULL)
+               return;
+
+       g_free (narg->name);
+       narg->name = NULL;
+       g_free (narg);
+       vtable->arg = NULL;
+}
+
+static void*
+network_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
+{
+       const CounterDesc *cdesc;
+       NetworkVtableArg *narg;
+       ImplVtable *vtable;
+       char *instance_name;
+
+       *custom = FALSE;
+       if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_NETWORK], counter))) {
+               instance_name = mono_string_to_utf8 (instance);
+               narg = g_new0 (NetworkVtableArg, 1);
+               narg->id = cdesc->id;
+               narg->name = instance_name;
+               *type = cdesc->type;
+               vtable = create_vtable (narg, get_network_counter, NULL);
+               vtable->cleanup = network_cleanup;
+               return vtable;
+       }
+       return NULL;
 }
 
 static MonoBoolean
@@ -406,25 +846,25 @@ get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSamp
        sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
        switch (id) {
        case COUNTER_PROC_USER_TIME:
-               sample->rawValue = get_process_time (pid, 12, FALSE);
+               sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
                return TRUE;
        case COUNTER_PROC_PRIV_TIME:
-               sample->rawValue = get_process_time (pid, 13, FALSE);
+               sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
                return TRUE;
        case COUNTER_PROC_PROC_TIME:
-               sample->rawValue = get_process_time (pid, 12, TRUE);
+               sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
                return TRUE;
        case COUNTER_PROC_THREADS:
-               sample->rawValue = get_pid_stat_item (pid, "Threads");
+               sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
                return TRUE;
        case COUNTER_PROC_VBYTES:
-               sample->rawValue = get_pid_stat_item (pid, "VmSize") * 1024;
+               sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
                return TRUE;
        case COUNTER_PROC_WSET:
-               sample->rawValue = get_pid_stat_item (pid, "VmRSS") * 1024;
+               sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
                return TRUE;
        case COUNTER_PROC_PBYTES:
-               sample->rawValue = get_pid_stat_item (pid, "VmData") * 1024;
+               sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
                return TRUE;
        }
        return FALSE;
@@ -433,18 +873,14 @@ get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSamp
 static void*
 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
 {
-       int id = id_from_string (instance) << 5;
-       int i;
-       const CategoryDesc *desc = &predef_categories [CATEGORY_PROC];
+       int id = id_from_string (instance, TRUE) << 5;
+       const CounterDesc *cdesc;
        *custom = FALSE;
        /* increase the shift above and the mask also in the implementation functions */
-       g_assert (32 > desc [1].first_counter - desc->first_counter);
-       for (i = desc->first_counter; i < desc [1].first_counter; ++i) {
-               const CounterDesc *cdesc = &predef_counters [i];
-               if (mono_string_compare_ascii (counter, cdesc->name) == 0) {
-                       *type = cdesc->type;
-                       return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
-               }
+       //g_assert (32 > desc [1].first_counter - desc->first_counter);
+       if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
+               *type = cdesc->type;
+               return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
        }
        return NULL;
 }
@@ -462,6 +898,9 @@ mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample
        case COUNTER_MEM_NUM_OBJECTS:
                sample->rawValue = mono_stats.new_object_count;
                return TRUE;
+       case COUNTER_MEM_PHYS_TOTAL:
+               sample->rawValue = mono_determine_physical_ram_size ();;
+               return TRUE;
        }
        return FALSE;
 }
@@ -469,19 +908,59 @@ mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample
 static void*
 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
 {
-       int i;
-       const CategoryDesc *desc = &predef_categories [CATEGORY_MONO_MEM];
+       const CounterDesc *cdesc;
        *custom = FALSE;
-       for (i = desc->first_counter; i < desc [1].first_counter; ++i) {
-               const CounterDesc *cdesc = &predef_counters [i];
-               if (mono_string_compare_ascii (counter, cdesc->name) == 0) {
-                       *type = cdesc->type;
-                       return create_vtable (GINT_TO_POINTER (cdesc->id), mono_mem_counter, NULL);
-               }
+       if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
+               *type = cdesc->type;
+               return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
        }
        return NULL;
 }
 
+static MonoBoolean
+predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
+{
+       PredefVtable *vt = (PredefVtable *)vtable;
+       const CounterDesc *desc;
+       int cat_id = GPOINTER_TO_INT (vtable->arg);
+       int id = cat_id >> 16;
+       cat_id &= 0xffff;
+       if (!only_value) {
+               fill_sample (sample);
+               sample->baseValue = 1;
+       }
+       desc = &predef_counters [predef_categories [cat_id].first_counter + id];
+       sample->counterType = desc->type;
+       /* FIXME: check that the offset fits inside imported counters */
+       /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
+       sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
+       return TRUE;
+}
+
+static ImplVtable*
+predef_vtable (void *arg, MonoString *instance)
+{
+       MonoSharedArea *area;
+       PredefVtable *vtable;
+       char *pids = mono_string_to_utf8 (instance);
+       int pid;
+
+       pid = atoi (pids);
+       g_free (pids);
+       area = load_sarea_for_pid (pid);
+       if (!area)
+               return NULL;
+
+       vtable = g_new (PredefVtable, 1);
+       vtable->vtable.arg = arg;
+       vtable->vtable.sample = predef_readonly_counter;
+       vtable->vtable.cleanup = predef_cleanup;
+       vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
+       vtable->pid = pid;
+
+       return (ImplVtable*)vtable;
+}
+
 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
  * this needs some way to set sample->counterType as well, though.
  */
@@ -496,10 +975,59 @@ predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounter
                sample->baseValue = 1;
        }
        sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
-       switch (id) {
-       case COUNTER_ASPNET_REQ_Q:
-               sample->rawValue = mono_perfcounters->aspnet_requests_queued;
-               return TRUE;
+       switch (cat_id) {
+       case CATEGORY_EXC:
+               switch (id) {
+               case COUNTER_EXC_THROWN:
+                       sample->rawValue = mono_perfcounters->exceptions_thrown;
+                       return TRUE;
+               }
+               break;
+       case CATEGORY_ASPNET:
+               switch (id) {
+               case COUNTER_ASPNET_REQ_Q:
+                       sample->rawValue = mono_perfcounters->aspnet_requests_queued;
+                       return TRUE;
+               case COUNTER_ASPNET_REQ_TOTAL:
+                       sample->rawValue = mono_perfcounters->aspnet_requests;
+                       return TRUE;
+               }
+               break;
+       case CATEGORY_THREADPOOL:
+               switch (id) {
+               case COUNTER_THREADPOOL_WORKITEMS:
+                       sample->rawValue = mono_perfcounters->threadpool_workitems;
+                       return TRUE;
+               case COUNTER_THREADPOOL_IOWORKITEMS:
+                       sample->rawValue = mono_perfcounters->threadpool_ioworkitems;
+                       return TRUE;
+               case COUNTER_THREADPOOL_THREADS:
+                       sample->rawValue = mono_perfcounters->threadpool_threads;
+                       return TRUE;
+               case COUNTER_THREADPOOL_IOTHREADS:
+                       sample->rawValue = mono_perfcounters->threadpool_iothreads;
+                       return TRUE;
+               }
+               break;
+       case CATEGORY_JIT:
+               switch (id) {
+               case COUNTER_JIT_BYTES:
+                       sample->rawValue = mono_perfcounters->jit_bytes;
+                       return TRUE;
+               case COUNTER_JIT_METHODS:
+                       sample->rawValue = mono_perfcounters->jit_methods;
+                       return TRUE;
+               case COUNTER_JIT_TIME:
+                       sample->rawValue = mono_perfcounters->jit_time;
+                       return TRUE;
+               case COUNTER_JIT_BYTES_PSEC:
+                       sample->rawValue = mono_perfcounters->jit_bytes;
+                       return TRUE;
+               case COUNTER_JIT_FAILURES:
+                       sample->rawValue = mono_perfcounters->jit_failures;
+                       return TRUE;
+               }
+               break;
        }
        return FALSE;
 }
@@ -507,26 +1035,57 @@ predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounter
 static gint64
 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
 {
-       glong *ptr = NULL;
+       guint32 *volatile ptr = NULL;
+       gint64 *volatile ptr64 = NULL;
        int cat_id = GPOINTER_TO_INT (vtable->arg);
        int id = cat_id >> 16;
        cat_id &= 0xffff;
        switch (cat_id) {
        case CATEGORY_ASPNET:
                switch (id) {
-               case COUNTER_ASPNET_REQ_Q: ptr = (glong*)&mono_perfcounters->aspnet_requests_queued; break;
+               case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
+               case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
+               }
+               break;
+       case CATEGORY_THREADPOOL:
+               switch (id) {
+               case COUNTER_THREADPOOL_WORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_workitems; break;
+               case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_ioworkitems; break;
+               case COUNTER_THREADPOOL_THREADS: ptr = &mono_perfcounters->threadpool_threads; break;
+               case COUNTER_THREADPOOL_IOTHREADS: ptr = &mono_perfcounters->threadpool_iothreads; break;
                }
                break;
        }
        if (ptr) {
                if (do_incr) {
-                       /* FIXME: we need to do this atomically */
+                       if (value == 1)
+                               return InterlockedIncrement ((gint32 *) ptr); /* FIXME: sign */
+                       if (value == -1)
+                               return InterlockedDecrement ((gint32 *) ptr); /* FIXME: sign */
+
                        *ptr += value;
                        return *ptr;
                }
                /* this can be non-atomic */
                *ptr = value;
                return value;
+       } else if (ptr64) {
+               if (do_incr) {
+                       /* FIXME: we need to do this atomically */
+                       /* No InterlockedIncrement64() yet */
+                       /*
+                       if (value == 1)
+                               return InterlockedIncrement64 (ptr);
+                       if (value == -1)
+                               return InterlockedDecrement64 (ptr);
+                       */
+
+                       *ptr64 += value;
+                       return *ptr64;
+               }
+               /* this can be non-atomic */
+               *ptr64 = value;
+               return value;
        }
        return 0;
 }
@@ -534,19 +1093,126 @@ predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
 static void*
 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
 {
-       int i;
-       const CategoryDesc *desc = &predef_categories [cat];
+       const CounterDesc *cdesc;
        *custom = TRUE;
-       for (i = desc->first_counter; i < desc [1].first_counter; ++i) {
-               const CounterDesc *cdesc = &predef_counters [i];
-               if (mono_string_compare_ascii (counter, cdesc->name) == 0) {
-                       *type = cdesc->type;
+       if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
+               *type = cdesc->type;
+               if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
                        return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
-               }
+               else
+                       return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
        }
        return NULL;
 }
 
+static MonoBoolean
+custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
+{
+       CustomVTable *counter_data = (CustomVTable *)vtable;
+       if (!only_value) {
+               fill_sample (sample);
+               sample->baseValue = 1;
+       }
+       sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
+       if (!vtable->arg)
+               sample->rawValue = 0;
+       else
+               sample->rawValue = *(guint64*)vtable->arg;
+       return TRUE;
+}
+
+static gint64
+custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
+{
+       /* FIXME: check writability */
+       guint64 *ptr = vtable->arg;
+       if (ptr) {
+               if (do_incr) {
+                       /* FIXME: we need to do this atomically */
+                       *ptr += value;
+                       return *ptr;
+               }
+               /* this can be non-atomic */
+               *ptr = value;
+               return value;
+       }
+       return 0;
+}
+
+static SharedInstance*
+custom_get_instance (SharedCategory *cat, SharedCounter *scounter, MonoString* instance)
+{
+       SharedInstance* inst;
+       unsigned char *ptr;
+       char *p;
+       int size, data_offset;
+       char *name;
+       inst = find_custom_instance (cat, instance);
+       if (inst)
+               return inst;
+       name = mono_string_to_utf8 (instance);
+       size = sizeof (SharedInstance) + strlen (name);
+       size += 7;
+       size &= ~7;
+       data_offset = size;
+       size += (sizeof (guint64) * cat->num_counters);
+       perfctr_lock ();
+       ptr = shared_data_find_room (size);
+       if (!ptr) {
+               perfctr_unlock ();
+               g_free (name);
+               return NULL;
+       }
+       inst = (SharedInstance*)ptr;
+       inst->header.extra = 0; /* data_offset could overflow here, so we leave this field unused */
+       inst->header.size = size;
+       inst->category_offset = (char*)cat - (char*)shared_area;
+       cat->num_instances++;
+       /* now copy the variable data */
+       p = inst->instance_name;
+       strcpy (p, name);
+       p += strlen (name) + 1;
+       inst->header.ftype = FTYPE_INSTANCE;
+       perfctr_unlock ();
+       g_free (name);
+
+       return inst;
+}
+
+static ImplVtable*
+custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
+{
+       CustomVTable* vtable;
+       vtable = g_new0 (CustomVTable, 1);
+       vtable->vtable.arg = data;
+       vtable->vtable.sample = custom_writable_counter;
+       vtable->vtable.update = custom_writable_update;
+       vtable->instance_desc = inst;
+       vtable->counter_desc = scounter;
+
+       return (ImplVtable*)vtable;
+}
+
+static void*
+custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
+{
+       SharedCounter *scounter;
+       SharedInstance* inst;
+       int size;
+
+       scounter = find_custom_counter (cat, counter);
+       if (!scounter)
+               return NULL;
+       *type = simple_type_to_type [scounter->type];
+       inst = custom_get_instance (cat, scounter, instance);
+       if (!inst)
+               return NULL;
+       size = sizeof (SharedInstance) + strlen (inst->instance_name);
+       size += 7;
+       size &= ~7;
+       return custom_vtable (scounter, inst, (char*)inst + size + scounter->seq_num * sizeof (guint64));
+}
+
 static const CategoryDesc*
 find_category (MonoString *category)
 {
@@ -567,8 +1233,13 @@ mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString
        if (mono_string_compare_ascii (machine, "."))
                return NULL;
        cdesc = find_category (category);
-       if (!cdesc)
-               return NULL;
+       if (!cdesc) {
+               SharedCategory *scat = find_custom_category (category);
+               if (!scat)
+                       return NULL;
+               *custom = TRUE;
+               return custom_get_impl (scat, counter, instance, type);
+       }
        switch (cdesc->id) {
        case CATEGORY_CPU:
                return cpu_get_impl (counter, instance, type, custom);
@@ -576,7 +1247,18 @@ mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString
                return process_get_impl (counter, instance, type, custom);
        case CATEGORY_MONO_MEM:
                return mono_mem_get_impl (counter, instance, type, custom);
+       case CATEGORY_NETWORK:
+               return network_get_impl (counter, instance, type, custom);
+       case CATEGORY_JIT:
+       case CATEGORY_EXC:
+       case CATEGORY_GC:
+       case CATEGORY_REMOTING:
+       case CATEGORY_LOADING:
+       case CATEGORY_THREAD:
+       case CATEGORY_INTEROP:
+       case CATEGORY_SECURITY:
        case CATEGORY_ASPNET:
+       case CATEGORY_THREADPOOL:
                return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
        }
        return NULL;
@@ -603,6 +1285,9 @@ mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
 void
 mono_perfcounter_free_data (void *impl)
 {
+       ImplVtable *vtable = impl;
+       if (vtable && vtable->cleanup)
+               vtable->cleanup (vtable);
        g_free (impl);
 }
 
@@ -610,7 +1295,22 @@ mono_perfcounter_free_data (void *impl)
 MonoBoolean
 mono_perfcounter_category_del (MonoString *name)
 {
-       return FALSE;
+       const CategoryDesc *cdesc;
+       SharedCategory *cat;
+       cdesc = find_category (name);
+       /* can't delete a predefined category */
+       if (cdesc)
+               return FALSE;
+       perfctr_lock ();
+       cat = find_custom_category (name);
+       /* FIXME: check the semantics, if deleting a category means also deleting the instances */
+       if (!cat || cat->num_instances) {
+               perfctr_unlock ();
+               return FALSE;
+       }
+       cat->header.ftype = FTYPE_DELETED;
+       perfctr_unlock ();
+       return TRUE;
 }
 
 MonoString*
@@ -621,33 +1321,53 @@ mono_perfcounter_category_help (MonoString *category, MonoString *machine)
        if (mono_string_compare_ascii (machine, "."))
                return NULL;
        cdesc = find_category (category);
-       if (!cdesc)
-               return NULL;
+       if (!cdesc) {
+               SharedCategory *scat = find_custom_category (category);
+               if (!scat)
+                       return NULL;
+               return mono_string_new (mono_domain_get (), custom_category_help (scat));
+       }
        return mono_string_new (mono_domain_get (), cdesc->help);
 }
 
+/*
+ * Check if the category named @category exists on @machine. If @counter is not NULL, return
+ * TRUE only if a counter with that name exists in the category.
+ */
 MonoBoolean
 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
 {
-       int i;
        const CategoryDesc *cdesc;
        /* no support for counters on other machines */
        if (mono_string_compare_ascii (machine, "."))
                return FALSE;
        cdesc = find_category (category);
-       if (!cdesc)
-               return FALSE;
+       if (!cdesc) {
+               SharedCategory *scat = find_custom_category (category);
+               if (!scat)
+                       return FALSE;
+               /* counter is allowed to be null */
+               if (!counter)
+                       return TRUE;
+               /* search through the custom category */
+               return find_custom_counter (scat, counter) != NULL;
+       }
        /* counter is allowed to be null */
        if (!counter)
                return TRUE;
-       for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
-               const CounterDesc *desc = &predef_counters [i];
-               if (mono_string_compare_ascii (counter, desc->name) == 0)
-                       return TRUE;
-       }
+       if (get_counter_in_category (cdesc, counter))
+               return TRUE;
        return FALSE;
 }
 
+/* C map of the type with the same name */
+typedef struct {
+       MonoObject object;
+       MonoString *help;
+       MonoString *name;
+       int type;
+} CounterCreationData;
+
 /*
  * Since we'll keep a copy of the category per-process, we should also make sure
  * categories with the same name are compatible.
@@ -655,7 +1375,91 @@ mono_perfcounter_category_exists (MonoString *counter, MonoString *category, Mon
 MonoBoolean
 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
 {
-       return FALSE;
+       MonoError error;
+       int result = FALSE;
+       int i, size;
+       int num_counters = mono_array_length (items);
+       int counters_data_size;
+       char *name = NULL;
+       char *chelp = NULL;
+       char **counter_info = NULL;
+       unsigned char *ptr;
+       char *p;
+       SharedCategory *cat;
+
+       /* FIXME: ensure there isn't a category created already */
+       mono_error_init (&error);
+       name = mono_string_to_utf8_checked (category, &error);
+       if (!mono_error_ok (&error))
+               goto failure;
+       chelp = mono_string_to_utf8_checked (help, &error);
+       if (!mono_error_ok (&error))
+               goto failure;
+       counter_info = g_new0 (char*, num_counters * 2);
+       /* calculate the size we need structure size + name/help + 2 0 string terminators */
+       size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
+       for (i = 0; i < num_counters; ++i) {
+               CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
+               counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error);
+               if (!mono_error_ok (&error))
+                       goto failure;
+               counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error);
+               if (!mono_error_ok (&error))
+                       goto failure;
+               size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
+       }
+       for (i = 0; i < num_counters * 2; ++i) {
+               if (!counter_info [i])
+                       goto failure;
+               size += strlen (counter_info [i]) + 1;
+       }
+       size += 7;
+       size &= ~7;
+       counters_data_size = num_counters * 8; /* optimize for size later */
+       if (size > 65535)
+               goto failure;
+       perfctr_lock ();
+       ptr = shared_data_find_room (size);
+       if (!ptr) {
+               perfctr_unlock ();
+               goto failure;
+       }
+       cat = (SharedCategory*)ptr;
+       cat->header.extra = type;
+       cat->header.size = size;
+       cat->num_counters = num_counters;
+       cat->counters_data_size = counters_data_size;
+       /* now copy the vaiable data */
+       p = cat->name;
+       strcpy (p, name);
+       p += strlen (name) + 1;
+       strcpy (p, chelp);
+       p += strlen (chelp) + 1;
+       for (i = 0; i < num_counters; ++i) {
+               CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
+               /* emit the SharedCounter structures */
+               *p++ = perfctr_type_compress (data->type);
+               *p++ = i;
+               strcpy (p, counter_info [i * 2]);
+               p += strlen (counter_info [i * 2]) + 1;
+               strcpy (p, counter_info [i * 2 + 1]);
+               p += strlen (counter_info [i * 2 + 1]) + 1;
+       }
+       cat->header.ftype = FTYPE_CATEGORY;
+
+       perfctr_unlock ();
+       result = TRUE;
+failure:
+       if (counter_info) {
+               for (i = 0; i < num_counters * 2; ++i) {
+                       g_free (counter_info [i]);
+               }
+               g_free (counter_info);
+       }
+       g_free (name);
+       g_free (chelp);
+       mono_error_cleanup (&error);
+       return result;
 }
 
 int
@@ -663,11 +1467,20 @@ mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, Mo
 {
        const CategoryDesc *cdesc;
        /* no support for counters on other machines */
+       /*FIXME: machine appears to be wrong
        if (mono_string_compare_ascii (machine, "."))
-               return FALSE;
+               return FALSE;*/
        cdesc = find_category (category);
-       if (!cdesc)
-               return FALSE;
+       if (!cdesc) {
+               SharedCategory *scat;
+               scat = find_custom_category (category);
+               if (!scat)
+                       return FALSE;
+               if (find_custom_instance (scat, instance))
+                       return TRUE;
+       } else {
+               /* FIXME: search instance */
+       }
        return FALSE;
 }
 
@@ -677,14 +1490,24 @@ mono_perfcounter_category_names (MonoString *machine)
        int i;
        MonoArray *res;
        MonoDomain *domain = mono_domain_get ();
+       GSList *custom_categories, *tmp;
        /* no support for counters on other machines */
        if (mono_string_compare_ascii (machine, "."))
                return mono_array_new (domain, mono_get_string_class (), 0);
-       res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES);
+       perfctr_lock ();
+       custom_categories = get_custom_categories ();
+       res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
        for (i = 0; i < NUM_CATEGORIES; ++i) {
                const CategoryDesc *cdesc = &predef_categories [i];
                mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
        }
+       for (tmp = custom_categories; tmp; tmp = tmp->next) {
+               SharedCategory *scat = tmp->data;
+               mono_array_setref (res, i, mono_string_new (domain, scat->name));
+               i++;
+       }
+       perfctr_unlock ();
+       g_slist_free (custom_categories);
        return res;
 }
 
@@ -692,23 +1515,253 @@ MonoArray*
 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
 {
        int i;
+       SharedCategory *scat;
        const CategoryDesc *cdesc;
        MonoArray *res;
        MonoDomain *domain = mono_domain_get ();
        /* no support for counters on other machines */
-       if (mono_string_compare_ascii (machine, ".") || !(cdesc = find_category (category)))
+       if (mono_string_compare_ascii (machine, "."))
                return mono_array_new (domain, mono_get_string_class (), 0);
-       res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
-       for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
-               const CounterDesc *desc = &predef_counters [i];
-               mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
+       cdesc = find_category (category);
+       if (cdesc) {
+               res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
+               for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
+                       const CounterDesc *desc = &predef_counters [i];
+                       mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
+               }
+               return res;
+       }
+       perfctr_lock ();
+       scat = find_custom_category (category);
+       if (scat) {
+               char *p = custom_category_counters (scat);
+               int i;
+               res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
+               for (i = 0; i < scat->num_counters; ++i) {
+                       mono_array_setref (res, i, mono_string_new (domain, p + 1));
+                       p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
+                       p += strlen (p) + 1; /* skip counter help */
+               }
+               perfctr_unlock ();
+               return res;
+       }
+       perfctr_unlock ();
+       return mono_array_new (domain, mono_get_string_class (), 0);
+}
+
+static MonoArray*
+get_string_array (void **array, int count, gboolean is_process)
+{
+       int i;
+       MonoDomain *domain = mono_domain_get ();
+       MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
+       for (i = 0; i < count; ++i) {
+               char buf [128];
+               char *p;
+               if (is_process) {
+                       char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
+                       p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
+               } else {
+                       sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
+                       p = buf;
+               }
+               mono_array_setref (res, i, mono_string_new (domain, p));
+               if (p != buf)
+                       g_free (p);
+       }
+       return res;
+}
+
+static MonoArray*
+get_string_array_of_strings (void **array, int count)
+{
+       int i;
+       MonoDomain *domain = mono_domain_get ();
+       MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
+       for (i = 0; i < count; ++i) {
+               char* p = array[i];
+               mono_array_setref (res, i, mono_string_new (domain, p));
        }
+
        return res;
 }
 
+static MonoArray*
+get_mono_instances (void)
+{
+       int count = 64;
+       int res;
+       void **buf = NULL;
+       MonoArray *array;
+       do {
+               count *= 2;
+               g_free (buf);
+               buf = g_new (void*, count);
+               res = mono_shared_area_instances (buf, count);
+       } while (res == count);
+       array = get_string_array (buf, res, TRUE);
+       g_free (buf);
+       return array;
+}
+
+static MonoArray*
+get_cpu_instances (void)
+{
+       void **buf = NULL;
+       int i, count;
+       MonoArray *array;
+
+       count = mono_cpu_count () + 1; /* +1 for "_Total" */
+       buf = g_new (void*, count);
+       for (i = 0; i < count; ++i)
+               buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
+       array = get_string_array (buf, count, FALSE);
+       g_free (buf);
+       mono_array_setref (array, 0, mono_string_new (mono_domain_get (), "_Total"));
+       return array;
+}
+
+static MonoArray*
+get_processes_instances (void)
+{
+       MonoArray *array;
+       int count = 0;
+       void **buf = mono_process_list (&count);
+       if (!buf)
+               return get_string_array (NULL, 0, FALSE);
+       array = get_string_array (buf, count, TRUE);
+       g_free (buf);
+       return array;
+}
+
+static MonoArray*
+get_networkinterface_instances (void)
+{
+       MonoArray *array;
+       int count = 0;
+       void **buf = mono_networkinterface_list (&count);
+       if (!buf)
+               return get_string_array_of_strings (NULL, 0);
+       array = get_string_array_of_strings (buf, count);
+       g_strfreev ((char **) buf);
+       return array;
+}
+
+static MonoArray*
+get_custom_instances (MonoString *category)
+{
+       SharedCategory *scat;
+       scat = find_custom_category (category);
+       if (scat) {
+               GSList *list = get_custom_instances_list (scat);
+               GSList *tmp;
+               int i = 0;
+               MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
+               for (tmp = list; tmp; tmp = tmp->next) {
+                       SharedInstance *inst = tmp->data;
+                       mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
+                       i++;
+               }
+               g_slist_free (list);
+               return array;
+       }
+       return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
+}
+
 MonoArray*
 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
 {
-       return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
+       const CategoryDesc* cat;
+       if (mono_string_compare_ascii (machine, "."))
+               return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
+       cat = find_category (category);
+       if (!cat)
+               return get_custom_instances (category);
+       switch (cat->instance_type) {
+       case MonoInstance:
+               return get_mono_instances ();
+       case CPUInstance:
+               return get_cpu_instances ();
+       case ProcessInstance:
+               return get_processes_instances ();
+       case NetworkInterfaceInstance:
+               return get_networkinterface_instances ();
+       case ThreadInstance:
+       default:
+               return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
+       }
+}
+#else
+void*
+mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance, MonoString* machine, int *type, MonoBoolean *custom)
+{
+       g_assert_not_reached ();
 }
 
+MonoBoolean
+mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
+{
+       g_assert_not_reached ();
+}
+
+gint64
+mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
+{
+       g_assert_not_reached ();
+}
+
+void
+mono_perfcounter_free_data (void *impl)
+{
+       g_assert_not_reached ();
+}
+
+/* Category icalls */
+MonoBoolean
+mono_perfcounter_category_del (MonoString *name)
+{
+       g_assert_not_reached ();
+}
+
+MonoString*
+mono_perfcounter_category_help (MonoString *category, MonoString *machine)
+{
+       g_assert_not_reached ();
+}
+
+MonoBoolean
+mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
+{
+       g_assert_not_reached ();
+}
+
+MonoBoolean
+mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
+{
+       g_assert_not_reached ();
+}
+
+int
+mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
+{
+       g_assert_not_reached ();
+}
+
+MonoArray*
+mono_perfcounter_category_names (MonoString *machine)
+{
+       g_assert_not_reached ();
+}
+
+MonoArray*
+mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
+{
+       g_assert_not_reached ();
+}
+
+MonoArray*
+mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
+{
+       g_assert_not_reached ();
+}
+#endif