e30c2047cc2a724236549b659b0ecc913733e434
[mono.git] / mono / metadata / mono-perfcounters.c
1 /*
2  * mono-perfcounters.c
3  *
4  * Performance counters support.
5  *
6  * Author: Paolo Molaro (lupus@ximian.com)
7  *
8  * Copyright 2008-2009 Novell, Inc (http://www.novell.com)
9  */
10
11 #include "config.h"
12 #include <time.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18 #if defined (__OpenBSD__)
19 #include <sys/param.h>
20 #endif
21 #ifdef HAVE_SYS_TYPES_H
22 #include <sys/types.h>
23 #endif
24 #ifdef HAVE_SYS_TIME_H
25 #include <sys/time.h>
26 #endif
27 #if defined (__NetBSD__) || defined (__APPLE__)
28 #include <sys/sysctl.h>
29 #endif
30 #include "metadata/mono-perfcounters.h"
31 #include "metadata/appdomain.h"
32 #include "metadata/object-internals.h"
33 /* for mono_stats */
34 #include "metadata/class-internals.h"
35 #include "utils/mono-time.h"
36 #include "utils/mono-mmap.h"
37 #include "utils/mono-proclib.h"
38 #include "utils/mono-networkinterfaces.h"
39 #include "utils/mono-error-internals.h"
40 #include <mono/io-layer/io-layer.h>
41
42 /* map of CounterSample.cs */
43 struct _MonoCounterSample {
44         gint64 rawValue;
45         gint64 baseValue;
46         gint64 counterFrequency;
47         gint64 systemFrequency;
48         gint64 timeStamp;
49         gint64 timeStamp100nSec;
50         gint64 counterTimeStamp;
51         int counterType;
52 };
53
54 /* map of PerformanceCounterType.cs */
55 enum {
56         NumberOfItemsHEX32=0x00000000,
57         NumberOfItemsHEX64=0x00000100,
58         NumberOfItems32=0x00010000,
59         NumberOfItems64=0x00010100,
60         CounterDelta32=0x00400400,
61         CounterDelta64=0x00400500,
62         SampleCounter=0x00410400,
63         CountPerTimeInterval32=0x00450400,
64         CountPerTimeInterval64=0x00450500,
65         RateOfCountsPerSecond32=0x10410400,
66         RateOfCountsPerSecond64=0x10410500,
67         RawFraction=0x20020400,
68         CounterTimer=0x20410500,
69         Timer100Ns=0x20510500,
70         SampleFraction=0x20C20400,
71         CounterTimerInverse=0x21410500,
72         Timer100NsInverse=0x21510500,
73         CounterMultiTimer=0x22410500,
74         CounterMultiTimer100Ns=0x22510500,
75         CounterMultiTimerInverse=0x23410500,
76         CounterMultiTimer100NsInverse=0x23510500,
77         AverageTimer32=0x30020400,
78         ElapsedTime=0x30240500,
79         AverageCount64=0x40020500,
80         SampleBase=0x40030401,
81         AverageBase=0x40030402,
82         RawBase=0x40030403,
83         CounterMultiBase=0x42030500
84 };
85
86 /* maps a small integer type to the counter types above */
87 static const int
88 simple_type_to_type [] = {
89         NumberOfItemsHEX32, NumberOfItemsHEX64,
90         NumberOfItems32, NumberOfItems64,
91         CounterDelta32, CounterDelta64,
92         SampleCounter, CountPerTimeInterval32,
93         CountPerTimeInterval64, RateOfCountsPerSecond32,
94         RateOfCountsPerSecond64, RawFraction,
95         CounterTimer, Timer100Ns,
96         SampleFraction, CounterTimerInverse,
97         Timer100NsInverse, CounterMultiTimer,
98         CounterMultiTimer100Ns, CounterMultiTimerInverse,
99         CounterMultiTimer100NsInverse, AverageTimer32,
100         ElapsedTime, AverageCount64,
101         SampleBase, AverageBase,
102         RawBase, CounterMultiBase
103 };
104
105 enum {
106         SingleInstance,
107         MultiInstance,
108         CatTypeUnknown = -1
109 };
110
111 enum {
112         ProcessInstance,
113         ThreadInstance,
114         CPUInstance,
115         MonoInstance,
116         NetworkInterfaceInstance,
117         CustomInstance
118 };
119
120 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
121 #define PERFCTR_COUNTER(id,name,help,type,field)
122 enum {
123 #include "mono-perfcounters-def.h"
124         NUM_CATEGORIES
125 };
126
127 #undef PERFCTR_CAT
128 #undef PERFCTR_COUNTER
129 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1,
130 #define PERFCTR_COUNTER(id,name,help,type,field) COUNTER_ ## id,
131 /* each counter is assigned an id starting from 0 inside the category */
132 enum {
133 #include "mono-perfcounters-def.h"
134         END_COUNTERS
135 };
136
137 #undef PERFCTR_CAT
138 #undef PERFCTR_COUNTER
139 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
140 #define PERFCTR_COUNTER(id,name,help,type,field) CCOUNTER_ ## id,
141 /* this is used just to count the number of counters */
142 enum {
143 #include "mono-perfcounters-def.h"
144         NUM_COUNTERS
145 };
146
147 static CRITICAL_SECTION perfctr_mutex;
148 #define perfctr_lock() EnterCriticalSection (&perfctr_mutex)
149 #define perfctr_unlock() LeaveCriticalSection (&perfctr_mutex)
150
151 typedef struct {
152         char reserved [16];
153         int size;
154         unsigned short counters_start;
155         unsigned short counters_size;
156         unsigned short data_start;
157         MonoPerfCounters counters;
158         char data [1];
159 } MonoSharedArea;
160
161 /*
162   binary format of custom counters in shared memory, starting from MonoSharedArea* + data_start;
163   basic stanza:
164   struct stanza_header {
165         byte stanza_type; // FTYPE_*
166         byte other_info;
167         ushort stanza_length; // includeas header
168         ... data ...
169   }
170
171 // strings are utf8
172 // perfcat and perfinstance are 4-bytes aligned
173 struct perfcat {
174         byte typeidx;
175         byte categorytype;
176         ushort length; // includes the counters
177         ushort num_counters;
178         ushort counters_data_size;
179         int num_instances;
180         char name[]; // null terminated
181         char help[]; // null terminated
182         // perfcounters follow
183         {
184                 byte countertype;
185                 char name[]; // null terminated
186                 char help[]; // null terminated
187         }
188         0-byte
189 };
190
191 struct perfinstance {
192         byte typeidx;
193         byte data_offset; // offset of counters from beginning of struct
194         ushort length;
195         uint category_offset; // offset of category in the shared area
196         char name[]; // null terminated
197         // data follows: this is always 8-byte aligned
198 };
199
200 */
201
202 enum {
203         FTYPE_CATEGORY = 'C',
204         FTYPE_DELETED = 'D',
205         FTYPE_PREDEF_INSTANCE = 'P', // an instance of a predef counter
206         FTYPE_INSTANCE = 'I',
207         FTYPE_DIRTY = 'd',
208         FTYPE_END = 0
209 };
210
211 typedef struct {
212         unsigned char ftype;
213         unsigned char extra;
214         unsigned short size;
215 } SharedHeader;
216
217 typedef struct {
218         SharedHeader header;
219         unsigned short num_counters;
220         unsigned short counters_data_size;
221         int num_instances;
222         /* variable length data follows */
223         char name [1];
224 } SharedCategory;
225
226 typedef struct {
227         SharedHeader header;
228         unsigned int category_offset;
229         /* variable length data follows */
230         char instance_name [1];
231 } SharedInstance;
232
233 typedef struct {
234         unsigned char type;
235         guint8 seq_num;
236         /* variable length data follows */
237         char name [1];
238 } SharedCounter;
239
240 typedef struct {
241         const char *name;
242         const char *help;
243         unsigned char id;
244         signed int type : 2;
245         unsigned int instance_type : 6;
246         short first_counter;
247 } CategoryDesc;
248
249 typedef struct {
250         const char *name;
251         const char *help;
252         short id;
253         unsigned short offset; // offset inside MonoPerfCounters
254         int type;
255 } CounterDesc;
256
257 #undef PERFCTR_CAT
258 #undef PERFCTR_COUNTER
259 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
260 #define PERFCTR_COUNTER(id,name,help,type,field)
261 static const CategoryDesc
262 predef_categories [] = {
263 #include "mono-perfcounters-def.h"
264         {NULL, NULL, NUM_CATEGORIES, -1, 0, NUM_COUNTERS}
265 };
266
267 #undef PERFCTR_CAT
268 #undef PERFCTR_COUNTER
269 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
270 #define PERFCTR_COUNTER(id,name,help,type,field) {name, help, COUNTER_ ## id, G_STRUCT_OFFSET (MonoPerfCounters, field), type},
271 static const CounterDesc
272 predef_counters [] = {
273 #include "mono-perfcounters-def.h"
274         {NULL, NULL, -1, 0, 0}
275 };
276
277 /*
278  * We have several different classes of counters:
279  * *) system counters
280  * *) runtime counters
281  * *) remote counters
282  * *) user-defined counters
283  * *) windows counters (the implementation on windows will use this)
284  *
285  * To easily handle the differences we create a vtable for each class that contains the
286  * function pointers with the actual implementation to access the counters.
287  */
288 typedef struct _ImplVtable ImplVtable;
289
290 typedef MonoBoolean (*SampleFunc) (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample* sample);
291 typedef gint64 (*UpdateFunc) (ImplVtable *vtable, MonoBoolean do_incr, gint64 value);
292 typedef void (*CleanupFunc) (ImplVtable *vtable);
293
294 struct _ImplVtable {
295         void *arg;
296         SampleFunc sample;
297         UpdateFunc update;
298         CleanupFunc cleanup;
299 };
300
301 typedef struct {
302         int id;
303         char *name;
304 } NetworkVtableArg;
305
306 typedef struct {
307         ImplVtable vtable;
308         MonoPerfCounters *counters;
309         int pid;
310 } PredefVtable;
311
312 typedef struct {
313         ImplVtable vtable;
314         SharedInstance *instance_desc;
315         SharedCounter *counter_desc;
316 } CustomVTable;
317
318 static ImplVtable*
319 create_vtable (void *arg, SampleFunc sample, UpdateFunc update)
320 {
321         ImplVtable *vtable = g_new0 (ImplVtable, 1);
322         vtable->arg = arg;
323         vtable->sample = sample;
324         vtable->update = update;
325         return vtable;
326 }
327
328 MonoPerfCounters *mono_perfcounters = NULL;
329 static MonoSharedArea *shared_area = NULL;
330
331 typedef struct {
332         void *sarea;
333         int refcount;
334 } ExternalSArea;
335
336 /* maps a pid to a ExternalSArea pointer */
337 static GHashTable *pid_to_shared_area = NULL;
338
339 static MonoSharedArea *
340 load_sarea_for_pid (int pid)
341 {
342         ExternalSArea *data;
343         MonoSharedArea *area = NULL;
344
345         perfctr_lock ();
346         if (pid_to_shared_area == NULL)
347                 pid_to_shared_area = g_hash_table_new (NULL, NULL);
348         data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
349         if (!data) {
350                 area = mono_shared_area_for_pid (GINT_TO_POINTER (pid));
351                 if (area) {
352                         data = g_new (ExternalSArea, 1);
353                         data->sarea = area;
354                         data->refcount = 1;
355                         g_hash_table_insert (pid_to_shared_area, GINT_TO_POINTER (pid), data);
356                 }
357         } else {
358                 area = data->sarea;
359                 data->refcount ++;
360         }
361         perfctr_unlock ();
362         return area;
363 }
364
365 static void
366 unref_pid_unlocked (int pid)
367 {
368         ExternalSArea *data;
369         data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
370         if (data) {
371                 data->refcount--;
372                 if (!data->refcount) {
373                         g_hash_table_remove (pid_to_shared_area, GINT_TO_POINTER (pid));
374                         mono_shared_area_unload (data->sarea);
375                         g_free (data);
376                 }
377         }
378 }
379
380 static void
381 predef_cleanup (ImplVtable *vtable)
382 {
383         PredefVtable *vt = (PredefVtable*)vtable;
384         /* ExternalSArea *data; */
385         
386         perfctr_lock ();
387         if (!pid_to_shared_area) {
388                 perfctr_unlock ();
389                 return;
390         }
391         unref_pid_unlocked (vt->pid);
392         perfctr_unlock ();
393 }
394
395 static guint64
396 mono_determine_physical_ram_size (void)
397 {
398 #if defined (TARGET_WIN32)
399         MEMORYSTATUSEX memstat;
400
401         memstat.dwLength = sizeof (memstat);
402         GlobalMemoryStatusEx (&memstat);
403         return (guint64)memstat.ullTotalPhys;
404 #elif defined (__NetBSD__) || defined (__APPLE__)
405 #ifdef __NetBSD__
406         unsigned long value;
407 #else
408         guint64 value;
409 #endif
410         int mib[2] = {
411                 CTL_HW,
412 #ifdef __NetBSD__
413                 HW_PHYSMEM
414 #else
415                 HW_MEMSIZE
416 #endif
417         };
418         size_t size_sys = sizeof (value);
419
420         sysctl (mib, 2, &value, &size_sys, NULL, 0);
421         if (value == 0)
422                 return 134217728;
423
424         return (guint64)value;
425 #elif defined (HAVE_SYSCONF)
426         guint64 page_size = 0, num_pages = 0;
427
428         /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
429          * reports invalid values, please add your OS specific code below. */
430 #ifdef _SC_PAGESIZE
431         page_size = (guint64)sysconf (_SC_PAGESIZE);
432 #endif
433
434 #ifdef _SC_PHYS_PAGES
435         num_pages = (guint64)sysconf (_SC_PHYS_PAGES);
436 #endif
437
438         if (!page_size || !num_pages) {
439                 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
440                 return 134217728;
441         }
442
443         return page_size * num_pages;
444 #else
445         return 134217728;
446 #endif
447 }
448
449 void
450 mono_perfcounters_init (void)
451 {
452         int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
453         d_offset += 7;
454         d_offset &= ~7;
455
456         InitializeCriticalSection (&perfctr_mutex);
457
458         shared_area = mono_shared_area ();
459         shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
460         shared_area->counters_size = sizeof (MonoPerfCounters);
461         shared_area->data_start = d_offset;
462         shared_area->size = 4096;
463         mono_perfcounters = &shared_area->counters;
464 }
465
466 static int
467 perfctr_type_compress (int type)
468 {
469         int i;
470         for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
471                 if (simple_type_to_type [i] == type)
472                         return i;
473         }
474         /* NumberOfItems32 */
475         return 2;
476 }
477
478 static unsigned char*
479 shared_data_find_room (int size)
480 {
481         unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
482         unsigned char *end = (unsigned char *)shared_area + shared_area->size;
483
484         size += 7;
485         size &= ~7;
486         while (p < end) {
487                 unsigned short *next;
488                 if (*p == FTYPE_END) {
489                         if (size < (end - p))
490                                 return p;
491                         return NULL;
492                 }
493                 if (p + 4 > end)
494                         return NULL;
495                 next = (unsigned short*)(p + 2);
496                 if (*p == FTYPE_DELETED) {
497                         /* we reuse only if it's the same size */
498                         if (*next == size) {
499                                 return p;
500                         }
501                 }
502                 p += *next;
503         }
504         return NULL;
505 }
506
507 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
508
509 static void
510 foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
511 {
512         while (p < end) {
513                 unsigned short *next;
514                 if (p + 4 > end)
515                         return;
516                 next = (unsigned short*)(p + 2);
517                 if (!func ((SharedHeader*)p, data))
518                         return;
519                 if (*p == FTYPE_END)
520                         return;
521                 p += *next;
522         }
523 }
524
525 static void
526 foreach_shared_item (SharedFunc func, void *data)
527 {
528         unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
529         unsigned char *end = (unsigned char *)shared_area + shared_area->size;
530
531         foreach_shared_item_in_area (p, end, func, data);
532 }
533
534 static int
535 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
536 {
537         /* FIXME: make this case insensitive */
538         guint16 *strc = mono_string_chars (str);
539         while (*strc == *ascii_str++) {
540                 if (*strc == 0)
541                         return 0;
542                 strc++;
543         }
544         return *strc - *(const unsigned char *)(ascii_str - 1);
545 }
546
547 typedef struct {
548         MonoString *name;
549         SharedCategory *cat;
550 } CatSearch;
551
552 static gboolean
553 category_search (SharedHeader *header, void *data)
554 {
555         CatSearch *search = data;
556         if (header->ftype == FTYPE_CATEGORY) {
557                 SharedCategory *cat = (SharedCategory*)header;
558                 if (mono_string_compare_ascii (search->name, cat->name) == 0) {
559                         search->cat = cat;
560                         return FALSE;
561                 }
562         }
563         return TRUE;
564 }
565
566 static SharedCategory*
567 find_custom_category (MonoString *name)
568 {
569         CatSearch search;
570         search.name = name;
571         search.cat = NULL;
572         foreach_shared_item (category_search, &search);
573         return search.cat;
574 }
575
576 static gboolean
577 category_collect (SharedHeader *header, void *data)
578 {
579         GSList **list = data;
580         if (header->ftype == FTYPE_CATEGORY) {
581                 *list = g_slist_prepend (*list, header);
582         }
583         return TRUE;
584 }
585
586 static GSList*
587 get_custom_categories (void) {
588         GSList *list = NULL;
589         foreach_shared_item (category_collect, &list);
590         return list;
591 }
592
593 static char*
594 custom_category_counters (SharedCategory* cat)
595 {
596         char *p = cat->name + strlen (cat->name) + 1;
597         p += strlen (p) + 1; /* skip category help */
598         return p;
599 }
600
601 static SharedCounter*
602 find_custom_counter (SharedCategory* cat, MonoString *name)
603 {
604         int i;
605         char *p = custom_category_counters (cat);
606         for (i = 0; i < cat->num_counters; ++i) {
607                 SharedCounter *counter = (SharedCounter*)p;
608                 if (mono_string_compare_ascii (name, counter->name) == 0)
609                         return counter;
610                 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
611                 p += strlen (p) + 1; /* skip counter help */
612         }
613         return NULL;
614 }
615
616 typedef struct {
617         unsigned int cat_offset;
618         SharedCategory* cat;
619         MonoString *instance;
620         SharedInstance* result;
621         GSList *list;
622 } InstanceSearch;
623
624 static gboolean
625 instance_search (SharedHeader *header, void *data)
626 {
627         InstanceSearch *search = data;
628         if (header->ftype == FTYPE_INSTANCE) {
629                 SharedInstance *ins = (SharedInstance*)header;
630                 if (search->cat_offset == ins->category_offset) {
631                         if (search->instance) {
632                                 if (mono_string_compare_ascii (search->instance, ins->instance_name) == 0) {
633                                         search->result = ins;
634                                         return FALSE;
635                                 }
636                         } else {
637                                 search->list = g_slist_prepend (search->list, ins);
638                         }
639                 }
640         }
641         return TRUE;
642 }
643
644 static SharedInstance*
645 find_custom_instance (SharedCategory* cat, MonoString *instance)
646 {
647         InstanceSearch search;
648         search.cat_offset = (char*)cat - (char*)shared_area;
649         search.cat = cat;
650         search.instance = instance;
651         search.list = NULL;
652         search.result = NULL;
653         foreach_shared_item (instance_search, &search);
654         return search.result;
655 }
656
657 static GSList*
658 get_custom_instances_list (SharedCategory* cat)
659 {
660         InstanceSearch search;
661         search.cat_offset = (char*)cat - (char*)shared_area;
662         search.cat = cat;
663         search.instance = NULL;
664         search.list = NULL;
665         search.result = NULL;
666         foreach_shared_item (instance_search, &search);
667         return search.list;
668 }
669
670 static char*
671 custom_category_help (SharedCategory* cat)
672 {
673         return cat->name + strlen (cat->name) + 1;
674 }
675
676 static const CounterDesc*
677 get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
678 {
679         const CounterDesc *cdesc = &predef_counters [desc->first_counter];
680         const CounterDesc *end = &predef_counters [desc [1].first_counter];
681         for (; cdesc < end; ++cdesc) {
682                 if (mono_string_compare_ascii (counter, cdesc->name) == 0)
683                         return cdesc;
684         }
685         return NULL;
686 }
687
688 /* fill the info in sample (except the raw value) */
689 static void
690 fill_sample (MonoCounterSample *sample)
691 {
692         sample->timeStamp = mono_100ns_ticks ();
693         sample->timeStamp100nSec = sample->timeStamp;
694         sample->counterTimeStamp = sample->timeStamp;
695         sample->counterFrequency = 10000000;
696         sample->systemFrequency = 10000000;
697         // the real basevalue needs to be get from a different counter...
698         sample->baseValue = 0;
699 }
700
701 static int
702 id_from_string (MonoString *instance, gboolean is_process)
703 {
704         int id = -1;
705         if (mono_string_length (instance)) {
706                 char *id_str = mono_string_to_utf8 (instance);
707                 char *end;
708                 id = strtol (id_str, &end, 0);
709                 if (end == id_str && !is_process)
710                         id = -1;
711                 g_free (id_str);
712         }
713         return id;
714 }
715
716 static MonoBoolean
717 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
718 {
719         MonoProcessError error;
720         int id = GPOINTER_TO_INT (vtable->arg);
721         int pid = id >> 5;
722         id &= 0x1f;
723         if (!only_value) {
724                 fill_sample (sample);
725                 sample->baseValue = 1;
726         }
727         sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
728         switch (id) {
729         case COUNTER_CPU_USER_TIME:
730                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
731                 return TRUE;
732         case COUNTER_CPU_PRIV_TIME:
733                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
734                 return TRUE;
735         case COUNTER_CPU_INTR_TIME:
736                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
737                 return TRUE;
738         case COUNTER_CPU_DCP_TIME:
739                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
740                 return TRUE;
741         case COUNTER_CPU_PROC_TIME:
742                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
743                 return TRUE;
744         }
745         return FALSE;
746 }
747
748 static void*
749 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
750 {
751         int id = id_from_string (instance, FALSE) << 5;
752         const CounterDesc *cdesc;
753         *custom = FALSE;
754         /* increase the shift above and the mask also in the implementation functions */
755         //g_assert (32 > desc [1].first_counter - desc->first_counter);
756         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
757                 *type = cdesc->type;
758                 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
759         }
760         return NULL;
761 }
762
763 static MonoBoolean
764 get_network_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
765 {
766         MonoNetworkError error = MONO_NETWORK_ERROR_OTHER;
767         NetworkVtableArg *narg = (NetworkVtableArg*) vtable->arg;
768         if (!only_value) {
769                 fill_sample (sample);
770         }
771
772         sample->counterType = predef_counters [predef_categories [CATEGORY_NETWORK].first_counter + narg->id].type;
773         switch (narg->id) {
774         case COUNTER_NETWORK_BYTESRECSEC:
775                 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESREC, &error);
776                 break;
777         case COUNTER_NETWORK_BYTESSENTSEC:
778                 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESSENT, &error);
779                 break;
780         case COUNTER_NETWORK_BYTESTOTALSEC:
781                 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESTOTAL, &error);
782                 break;
783         }
784
785         if (error == MONO_NETWORK_ERROR_NONE)
786                 return TRUE;
787         else
788                 return FALSE;
789 }
790
791 static void
792 network_cleanup (ImplVtable *vtable)
793 {
794         NetworkVtableArg *narg;
795
796         if (vtable == NULL)
797                 return;
798
799         narg = vtable->arg;
800         if (narg == NULL)
801                 return;
802
803         g_free (narg->name);
804         narg->name = NULL;
805         g_free (narg);
806         vtable->arg = NULL;
807 }
808
809 static void*
810 network_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
811 {
812         const CounterDesc *cdesc;
813         NetworkVtableArg *narg;
814         ImplVtable *vtable;
815         char *instance_name;
816
817         *custom = FALSE;
818         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_NETWORK], counter))) {
819                 instance_name = mono_string_to_utf8 (instance);
820                 narg = g_new0 (NetworkVtableArg, 1);
821                 narg->id = cdesc->id;
822                 narg->name = instance_name;
823                 *type = cdesc->type;
824                 vtable = create_vtable (narg, get_network_counter, NULL);
825                 vtable->cleanup = network_cleanup;
826                 return vtable;
827         }
828         return NULL;
829 }
830
831 static MonoBoolean
832 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
833 {
834         int id = GPOINTER_TO_INT (vtable->arg);
835         int pid = id >> 5;
836         if (pid < 0)
837                 return FALSE;
838         id &= 0x1f;
839         if (!only_value) {
840                 fill_sample (sample);
841                 sample->baseValue = 1;
842         }
843         sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
844         switch (id) {
845         case COUNTER_PROC_USER_TIME:
846                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
847                 return TRUE;
848         case COUNTER_PROC_PRIV_TIME:
849                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
850                 return TRUE;
851         case COUNTER_PROC_PROC_TIME:
852                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
853                 return TRUE;
854         case COUNTER_PROC_THREADS:
855                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
856                 return TRUE;
857         case COUNTER_PROC_VBYTES:
858                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
859                 return TRUE;
860         case COUNTER_PROC_WSET:
861                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
862                 return TRUE;
863         case COUNTER_PROC_PBYTES:
864                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
865                 return TRUE;
866         }
867         return FALSE;
868 }
869
870 static void*
871 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
872 {
873         int id = id_from_string (instance, TRUE) << 5;
874         const CounterDesc *cdesc;
875         *custom = FALSE;
876         /* increase the shift above and the mask also in the implementation functions */
877         //g_assert (32 > desc [1].first_counter - desc->first_counter);
878         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
879                 *type = cdesc->type;
880                 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
881         }
882         return NULL;
883 }
884
885 static MonoBoolean
886 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
887 {
888         int id = GPOINTER_TO_INT (vtable->arg);
889         if (!only_value) {
890                 fill_sample (sample);
891                 sample->baseValue = 1;
892         }
893         sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
894         switch (id) {
895         case COUNTER_MEM_NUM_OBJECTS:
896                 sample->rawValue = mono_stats.new_object_count;
897                 return TRUE;
898         case COUNTER_MEM_PHYS_TOTAL:
899                 sample->rawValue = mono_determine_physical_ram_size ();;
900                 return TRUE;
901         }
902         return FALSE;
903 }
904
905 static void*
906 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
907 {
908         const CounterDesc *cdesc;
909         *custom = FALSE;
910         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
911                 *type = cdesc->type;
912                 return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
913         }
914         return NULL;
915 }
916
917 static MonoBoolean
918 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
919 {
920         PredefVtable *vt = (PredefVtable *)vtable;
921         const CounterDesc *desc;
922         int cat_id = GPOINTER_TO_INT (vtable->arg);
923         int id = cat_id >> 16;
924         cat_id &= 0xffff;
925         if (!only_value) {
926                 fill_sample (sample);
927                 sample->baseValue = 1;
928         }
929         desc = &predef_counters [predef_categories [cat_id].first_counter + id];
930         sample->counterType = desc->type;
931         /* FIXME: check that the offset fits inside imported counters */
932         /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
933         sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
934         return TRUE;
935 }
936
937 static ImplVtable*
938 predef_vtable (void *arg, MonoString *instance)
939 {
940         MonoSharedArea *area;
941         PredefVtable *vtable;
942         char *pids = mono_string_to_utf8 (instance);
943         int pid;
944
945         pid = atoi (pids);
946         g_free (pids);
947         area = load_sarea_for_pid (pid);
948         if (!area)
949                 return NULL;
950
951         vtable = g_new (PredefVtable, 1);
952         vtable->vtable.arg = arg;
953         vtable->vtable.sample = predef_readonly_counter;
954         vtable->vtable.cleanup = predef_cleanup;
955         vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
956         vtable->pid = pid;
957
958         return (ImplVtable*)vtable;
959 }
960
961 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
962  * this needs some way to set sample->counterType as well, though.
963  */
964 static MonoBoolean
965 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
966 {
967         int cat_id = GPOINTER_TO_INT (vtable->arg);
968         int id = cat_id >> 16;
969         cat_id &= 0xffff;
970         if (!only_value) {
971                 fill_sample (sample);
972                 sample->baseValue = 1;
973         }
974         sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
975         switch (cat_id) {
976         case CATEGORY_EXC:
977                 switch (id) {
978                 case COUNTER_EXC_THROWN:
979                         sample->rawValue = mono_perfcounters->exceptions_thrown;
980                         return TRUE;
981                 }
982                 break;
983         case CATEGORY_ASPNET:
984                 switch (id) {
985                 case COUNTER_ASPNET_REQ_Q:
986                         sample->rawValue = mono_perfcounters->aspnet_requests_queued;
987                         return TRUE;
988                 case COUNTER_ASPNET_REQ_TOTAL:
989                         sample->rawValue = mono_perfcounters->aspnet_requests;
990                         return TRUE;
991                 }
992                 break;
993         case CATEGORY_THREADPOOL:
994                 switch (id) {
995                 case COUNTER_THREADPOOL_WORKITEMS:
996                         sample->rawValue = mono_perfcounters->threadpool_workitems;
997                         return TRUE;
998                 case COUNTER_THREADPOOL_IOWORKITEMS:
999                         sample->rawValue = mono_perfcounters->threadpool_ioworkitems;
1000                         return TRUE;
1001                 case COUNTER_THREADPOOL_THREADS:
1002                         sample->rawValue = mono_perfcounters->threadpool_threads;
1003                         return TRUE;
1004                 case COUNTER_THREADPOOL_IOTHREADS:
1005                         sample->rawValue = mono_perfcounters->threadpool_iothreads;
1006                         return TRUE;
1007                 }
1008                 break;
1009         case CATEGORY_JIT:
1010                 switch (id) {
1011                 case COUNTER_JIT_BYTES:
1012                         sample->rawValue = mono_perfcounters->jit_bytes;
1013                         return TRUE;
1014                 case COUNTER_JIT_METHODS:
1015                         sample->rawValue = mono_perfcounters->jit_methods;
1016                         return TRUE;
1017                 case COUNTER_JIT_TIME:
1018                         sample->rawValue = mono_perfcounters->jit_time;
1019                         return TRUE;
1020                 case COUNTER_JIT_BYTES_PSEC:
1021                         sample->rawValue = mono_perfcounters->jit_bytes;
1022                         return TRUE;
1023                 case COUNTER_JIT_FAILURES:
1024                         sample->rawValue = mono_perfcounters->jit_failures;
1025                         return TRUE;
1026                 }
1027                 break;
1028         }
1029         return FALSE;
1030 }
1031
1032 static gint64
1033 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1034 {
1035         guint32 *volatile ptr = NULL;
1036         gint64 *volatile ptr64 = NULL;
1037         int cat_id = GPOINTER_TO_INT (vtable->arg);
1038         int id = cat_id >> 16;
1039         cat_id &= 0xffff;
1040         switch (cat_id) {
1041         case CATEGORY_ASPNET:
1042                 switch (id) {
1043                 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
1044                 case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
1045                 }
1046                 break;
1047         case CATEGORY_THREADPOOL:
1048                 switch (id) {
1049                 case COUNTER_THREADPOOL_WORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_workitems; break;
1050                 case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_ioworkitems; break;
1051                 case COUNTER_THREADPOOL_THREADS: ptr = &mono_perfcounters->threadpool_threads; break;
1052                 case COUNTER_THREADPOOL_IOTHREADS: ptr = &mono_perfcounters->threadpool_iothreads; break;
1053                 }
1054                 break;
1055         }
1056         if (ptr) {
1057                 if (do_incr) {
1058                         if (value == 1)
1059                                 return InterlockedIncrement ((gint32 *) ptr); /* FIXME: sign */
1060                         if (value == -1)
1061                                 return InterlockedDecrement ((gint32 *) ptr); /* FIXME: sign */
1062
1063                         *ptr += value;
1064                         return *ptr;
1065                 }
1066                 /* this can be non-atomic */
1067                 *ptr = value;
1068                 return value;
1069         } else if (ptr64) {
1070                 if (do_incr) {
1071                         /* FIXME: we need to do this atomically */
1072                         /* No InterlockedIncrement64() yet */
1073                         /*
1074                         if (value == 1)
1075                                 return InterlockedIncrement64 (ptr);
1076                         if (value == -1)
1077                                 return InterlockedDecrement64 (ptr);
1078                         */
1079
1080                         *ptr64 += value;
1081                         return *ptr64;
1082                 }
1083                 /* this can be non-atomic */
1084                 *ptr64 = value;
1085                 return value;
1086         }
1087         return 0;
1088 }
1089
1090 static void*
1091 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
1092 {
1093         const CounterDesc *cdesc;
1094         *custom = TRUE;
1095         if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
1096                 *type = cdesc->type;
1097                 if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
1098                         return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
1099                 else
1100                         return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
1101         }
1102         return NULL;
1103 }
1104
1105 static MonoBoolean
1106 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1107 {
1108         CustomVTable *counter_data = (CustomVTable *)vtable;
1109         if (!only_value) {
1110                 fill_sample (sample);
1111                 sample->baseValue = 1;
1112         }
1113         sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
1114         if (!vtable->arg)
1115                 sample->rawValue = 0;
1116         else
1117                 sample->rawValue = *(guint64*)vtable->arg;
1118         return TRUE;
1119 }
1120
1121 static gint64
1122 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1123 {
1124         /* FIXME: check writability */
1125         guint64 *ptr = vtable->arg;
1126         if (ptr) {
1127                 if (do_incr) {
1128                         /* FIXME: we need to do this atomically */
1129                         *ptr += value;
1130                         return *ptr;
1131                 }
1132                 /* this can be non-atomic */
1133                 *ptr = value;
1134                 return value;
1135         }
1136         return 0;
1137 }
1138
1139 static SharedInstance*
1140 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, MonoString* instance)
1141 {
1142         SharedInstance* inst;
1143         unsigned char *ptr;
1144         char *p;
1145         int size, data_offset;
1146         char *name;
1147         inst = find_custom_instance (cat, instance);
1148         if (inst)
1149                 return inst;
1150         name = mono_string_to_utf8 (instance);
1151         size = sizeof (SharedInstance) + strlen (name);
1152         size += 7;
1153         size &= ~7;
1154         data_offset = size;
1155         size += (sizeof (guint64) * cat->num_counters);
1156         perfctr_lock ();
1157         ptr = shared_data_find_room (size);
1158         if (!ptr) {
1159                 perfctr_unlock ();
1160                 g_free (name);
1161                 return NULL;
1162         }
1163         inst = (SharedInstance*)ptr;
1164         inst->header.extra = 0; /* data_offset could overflow here, so we leave this field unused */
1165         inst->header.size = size;
1166         inst->category_offset = (char*)cat - (char*)shared_area;
1167         cat->num_instances++;
1168         /* now copy the variable data */
1169         p = inst->instance_name;
1170         strcpy (p, name);
1171         p += strlen (name) + 1;
1172         inst->header.ftype = FTYPE_INSTANCE;
1173         perfctr_unlock ();
1174         g_free (name);
1175
1176         return inst;
1177 }
1178
1179 static ImplVtable*
1180 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
1181 {
1182         CustomVTable* vtable;
1183         vtable = g_new0 (CustomVTable, 1);
1184         vtable->vtable.arg = data;
1185         vtable->vtable.sample = custom_writable_counter;
1186         vtable->vtable.update = custom_writable_update;
1187         vtable->instance_desc = inst;
1188         vtable->counter_desc = scounter;
1189
1190         return (ImplVtable*)vtable;
1191 }
1192
1193 static void*
1194 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
1195 {
1196         SharedCounter *scounter;
1197         SharedInstance* inst;
1198         int size;
1199
1200         scounter = find_custom_counter (cat, counter);
1201         if (!scounter)
1202                 return NULL;
1203         *type = simple_type_to_type [scounter->type];
1204         inst = custom_get_instance (cat, scounter, instance);
1205         if (!inst)
1206                 return NULL;
1207         size = sizeof (SharedInstance) + strlen (inst->instance_name);
1208         size += 7;
1209         size &= ~7;
1210         return custom_vtable (scounter, inst, (char*)inst + size + scounter->seq_num * sizeof (guint64));
1211 }
1212
1213 static const CategoryDesc*
1214 find_category (MonoString *category)
1215 {
1216         int i;
1217         for (i = 0; i < NUM_CATEGORIES; ++i) {
1218                 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1219                         return &predef_categories [i];
1220         }
1221         return NULL;
1222 }
1223
1224 void*
1225 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1226                 MonoString* machine, int *type, MonoBoolean *custom)
1227 {
1228         const CategoryDesc *cdesc;
1229         /* no support for counters on other machines */
1230         if (mono_string_compare_ascii (machine, "."))
1231                 return NULL;
1232         cdesc = find_category (category);
1233         if (!cdesc) {
1234                 SharedCategory *scat = find_custom_category (category);
1235                 if (!scat)
1236                         return NULL;
1237                 *custom = TRUE;
1238                 return custom_get_impl (scat, counter, instance, type);
1239         }
1240         switch (cdesc->id) {
1241         case CATEGORY_CPU:
1242                 return cpu_get_impl (counter, instance, type, custom);
1243         case CATEGORY_PROC:
1244                 return process_get_impl (counter, instance, type, custom);
1245         case CATEGORY_MONO_MEM:
1246                 return mono_mem_get_impl (counter, instance, type, custom);
1247         case CATEGORY_NETWORK:
1248                 return network_get_impl (counter, instance, type, custom);
1249         case CATEGORY_JIT:
1250         case CATEGORY_EXC:
1251         case CATEGORY_GC:
1252         case CATEGORY_REMOTING:
1253         case CATEGORY_LOADING:
1254         case CATEGORY_THREAD:
1255         case CATEGORY_INTEROP:
1256         case CATEGORY_SECURITY:
1257         case CATEGORY_ASPNET:
1258         case CATEGORY_THREADPOOL:
1259                 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
1260         }
1261         return NULL;
1262 }
1263
1264 MonoBoolean
1265 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1266 {
1267         ImplVtable *vtable = impl;
1268         if (vtable && vtable->sample)
1269                 return vtable->sample (vtable, only_value, sample);
1270         return FALSE;
1271 }
1272
1273 gint64
1274 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1275 {
1276         ImplVtable *vtable = impl;
1277         if (vtable && vtable->update)
1278                 return vtable->update (vtable, do_incr, value);
1279         return 0;
1280 }
1281
1282 void
1283 mono_perfcounter_free_data (void *impl)
1284 {
1285         ImplVtable *vtable = impl;
1286         if (vtable && vtable->cleanup)
1287                 vtable->cleanup (vtable);
1288         g_free (impl);
1289 }
1290
1291 /* Category icalls */
1292 MonoBoolean
1293 mono_perfcounter_category_del (MonoString *name)
1294 {
1295         const CategoryDesc *cdesc;
1296         SharedCategory *cat;
1297         cdesc = find_category (name);
1298         /* can't delete a predefined category */
1299         if (cdesc)
1300                 return FALSE;
1301         perfctr_lock ();
1302         cat = find_custom_category (name);
1303         /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1304         if (!cat || cat->num_instances) {
1305                 perfctr_unlock ();
1306                 return FALSE;
1307         }
1308         cat->header.ftype = FTYPE_DELETED;
1309         perfctr_unlock ();
1310         return TRUE;
1311 }
1312
1313 MonoString*
1314 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1315 {
1316         const CategoryDesc *cdesc;
1317         /* no support for counters on other machines */
1318         if (mono_string_compare_ascii (machine, "."))
1319                 return NULL;
1320         cdesc = find_category (category);
1321         if (!cdesc) {
1322                 SharedCategory *scat = find_custom_category (category);
1323                 if (!scat)
1324                         return NULL;
1325                 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1326         }
1327         return mono_string_new (mono_domain_get (), cdesc->help);
1328 }
1329
1330 /*
1331  * Check if the category named @category exists on @machine. If @counter is not NULL, return
1332  * TRUE only if a counter with that name exists in the category.
1333  */
1334 MonoBoolean
1335 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1336 {
1337         const CategoryDesc *cdesc;
1338         /* no support for counters on other machines */
1339         if (mono_string_compare_ascii (machine, "."))
1340                 return FALSE;
1341         cdesc = find_category (category);
1342         if (!cdesc) {
1343                 SharedCategory *scat = find_custom_category (category);
1344                 if (!scat)
1345                         return FALSE;
1346                 /* counter is allowed to be null */
1347                 if (!counter)
1348                         return TRUE;
1349                 /* search through the custom category */
1350                 return find_custom_counter (scat, counter) != NULL;
1351         }
1352         /* counter is allowed to be null */
1353         if (!counter)
1354                 return TRUE;
1355         if (get_counter_in_category (cdesc, counter))
1356                 return TRUE;
1357         return FALSE;
1358 }
1359
1360 /* C map of the type with the same name */
1361 typedef struct {
1362         MonoObject object;
1363         MonoString *help;
1364         MonoString *name;
1365         int type;
1366 } CounterCreationData;
1367
1368 /*
1369  * Since we'll keep a copy of the category per-process, we should also make sure
1370  * categories with the same name are compatible.
1371  */
1372 MonoBoolean
1373 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1374 {
1375         MonoError error;
1376         int result = FALSE;
1377         int i, size;
1378         int num_counters = mono_array_length (items);
1379         int counters_data_size;
1380         char *name = NULL;
1381         char *chelp = NULL;
1382         char **counter_info = NULL;
1383         unsigned char *ptr;
1384         char *p;
1385         SharedCategory *cat;
1386
1387         /* FIXME: ensure there isn't a category created already */
1388         mono_error_init (&error);
1389         name = mono_string_to_utf8_checked (category, &error);
1390         if (!mono_error_ok (&error))
1391                 goto failure;
1392         chelp = mono_string_to_utf8_checked (help, &error);
1393         if (!mono_error_ok (&error))
1394                 goto failure;
1395         counter_info = g_new0 (char*, num_counters * 2);
1396         /* calculate the size we need structure size + name/help + 2 0 string terminators */
1397         size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1398         for (i = 0; i < num_counters; ++i) {
1399                 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1400                 counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error);
1401                 if (!mono_error_ok (&error))
1402                         goto failure;
1403                 counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error);
1404                 if (!mono_error_ok (&error))
1405                         goto failure;
1406                 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1407         }
1408         for (i = 0; i < num_counters * 2; ++i) {
1409                 if (!counter_info [i])
1410                         goto failure;
1411                 size += strlen (counter_info [i]) + 1;
1412         }
1413         size += 7;
1414         size &= ~7;
1415         counters_data_size = num_counters * 8; /* optimize for size later */
1416         if (size > 65535)
1417                 goto failure;
1418         perfctr_lock ();
1419         ptr = shared_data_find_room (size);
1420         if (!ptr) {
1421                 perfctr_unlock ();
1422                 goto failure;
1423         }
1424         cat = (SharedCategory*)ptr;
1425         cat->header.extra = type;
1426         cat->header.size = size;
1427         cat->num_counters = num_counters;
1428         cat->counters_data_size = counters_data_size;
1429         /* now copy the vaiable data */
1430         p = cat->name;
1431         strcpy (p, name);
1432         p += strlen (name) + 1;
1433         strcpy (p, chelp);
1434         p += strlen (chelp) + 1;
1435         for (i = 0; i < num_counters; ++i) {
1436                 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1437                 /* emit the SharedCounter structures */
1438                 *p++ = perfctr_type_compress (data->type);
1439                 *p++ = i;
1440                 strcpy (p, counter_info [i * 2]);
1441                 p += strlen (counter_info [i * 2]) + 1;
1442                 strcpy (p, counter_info [i * 2 + 1]);
1443                 p += strlen (counter_info [i * 2 + 1]) + 1;
1444         }
1445         cat->header.ftype = FTYPE_CATEGORY;
1446
1447         perfctr_unlock ();
1448         result = TRUE;
1449 failure:
1450         if (counter_info) {
1451                 for (i = 0; i < num_counters * 2; ++i) {
1452                         g_free (counter_info [i]);
1453                 }
1454                 g_free (counter_info);
1455         }
1456         g_free (name);
1457         g_free (chelp);
1458         mono_error_cleanup (&error);
1459         return result;
1460 }
1461
1462 int
1463 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1464 {
1465         const CategoryDesc *cdesc;
1466         /* no support for counters on other machines */
1467         /*FIXME: machine appears to be wrong
1468         if (mono_string_compare_ascii (machine, "."))
1469                 return FALSE;*/
1470         cdesc = find_category (category);
1471         if (!cdesc) {
1472                 SharedCategory *scat;
1473                 scat = find_custom_category (category);
1474                 if (!scat)
1475                         return FALSE;
1476                 if (find_custom_instance (scat, instance))
1477                         return TRUE;
1478         } else {
1479                 /* FIXME: search instance */
1480         }
1481         return FALSE;
1482 }
1483
1484 MonoArray*
1485 mono_perfcounter_category_names (MonoString *machine)
1486 {
1487         int i;
1488         MonoArray *res;
1489         MonoDomain *domain = mono_domain_get ();
1490         GSList *custom_categories, *tmp;
1491         /* no support for counters on other machines */
1492         if (mono_string_compare_ascii (machine, "."))
1493                 return mono_array_new (domain, mono_get_string_class (), 0);
1494         perfctr_lock ();
1495         custom_categories = get_custom_categories ();
1496         res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1497         for (i = 0; i < NUM_CATEGORIES; ++i) {
1498                 const CategoryDesc *cdesc = &predef_categories [i];
1499                 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1500         }
1501         for (tmp = custom_categories; tmp; tmp = tmp->next) {
1502                 SharedCategory *scat = tmp->data;
1503                 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1504                 i++;
1505         }
1506         perfctr_unlock ();
1507         g_slist_free (custom_categories);
1508         return res;
1509 }
1510
1511 MonoArray*
1512 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1513 {
1514         int i;
1515         SharedCategory *scat;
1516         const CategoryDesc *cdesc;
1517         MonoArray *res;
1518         MonoDomain *domain = mono_domain_get ();
1519         /* no support for counters on other machines */
1520         if (mono_string_compare_ascii (machine, "."))
1521                 return mono_array_new (domain, mono_get_string_class (), 0);
1522         cdesc = find_category (category);
1523         if (cdesc) {
1524                 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1525                 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1526                         const CounterDesc *desc = &predef_counters [i];
1527                         mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1528                 }
1529                 return res;
1530         }
1531         perfctr_lock ();
1532         scat = find_custom_category (category);
1533         if (scat) {
1534                 char *p = custom_category_counters (scat);
1535                 int i;
1536                 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1537                 for (i = 0; i < scat->num_counters; ++i) {
1538                         mono_array_setref (res, i, mono_string_new (domain, p + 1));
1539                         p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
1540                         p += strlen (p) + 1; /* skip counter help */
1541                 }
1542                 perfctr_unlock ();
1543                 return res;
1544         }
1545         perfctr_unlock ();
1546         return mono_array_new (domain, mono_get_string_class (), 0);
1547 }
1548
1549 static MonoArray*
1550 get_string_array (void **array, int count, gboolean is_process)
1551 {
1552         int i;
1553         MonoDomain *domain = mono_domain_get ();
1554         MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1555         for (i = 0; i < count; ++i) {
1556                 char buf [128];
1557                 char *p;
1558                 if (is_process) {
1559                         char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1560                         p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1561                 } else {
1562                         sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1563                         p = buf;
1564                 }
1565                 mono_array_setref (res, i, mono_string_new (domain, p));
1566                 if (p != buf)
1567                         g_free (p);
1568         }
1569         return res;
1570 }
1571
1572 static MonoArray*
1573 get_string_array_of_strings (void **array, int count)
1574 {
1575         int i;
1576         MonoDomain *domain = mono_domain_get ();
1577         MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1578         for (i = 0; i < count; ++i) {
1579                 char* p = array[i];
1580                 mono_array_setref (res, i, mono_string_new (domain, p));
1581         }
1582
1583         return res;
1584 }
1585
1586 static MonoArray*
1587 get_mono_instances (void)
1588 {
1589         int count = 64;
1590         int res;
1591         void **buf = NULL;
1592         MonoArray *array;
1593         do {
1594                 count *= 2;
1595                 g_free (buf);
1596                 buf = g_new (void*, count);
1597                 res = mono_shared_area_instances (buf, count);
1598         } while (res == count);
1599         array = get_string_array (buf, res, TRUE);
1600         g_free (buf);
1601         return array;
1602 }
1603
1604 static MonoArray*
1605 get_cpu_instances (void)
1606 {
1607         void **buf = NULL;
1608         int i, count;
1609         MonoArray *array;
1610
1611         count = mono_cpu_count () + 1; /* +1 for "_Total" */
1612         buf = g_new (void*, count);
1613         for (i = 0; i < count; ++i)
1614                 buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
1615         array = get_string_array (buf, count, FALSE);
1616         g_free (buf);
1617         mono_array_setref (array, 0, mono_string_new (mono_domain_get (), "_Total"));
1618         return array;
1619 }
1620
1621 static MonoArray*
1622 get_processes_instances (void)
1623 {
1624         MonoArray *array;
1625         int count = 0;
1626         void **buf = mono_process_list (&count);
1627         if (!buf)
1628                 return get_string_array (NULL, 0, FALSE);
1629         array = get_string_array (buf, count, TRUE);
1630         g_free (buf);
1631         return array;
1632 }
1633
1634 static MonoArray*
1635 get_networkinterface_instances (void)
1636 {
1637         MonoArray *array;
1638         int count = 0;
1639         void **buf = mono_networkinterface_list (&count);
1640         if (!buf)
1641                 return get_string_array_of_strings (NULL, 0);
1642         array = get_string_array_of_strings (buf, count);
1643         g_strfreev ((char **) buf);
1644         return array;
1645 }
1646
1647 static MonoArray*
1648 get_custom_instances (MonoString *category)
1649 {
1650         SharedCategory *scat;
1651         scat = find_custom_category (category);
1652         if (scat) {
1653                 GSList *list = get_custom_instances_list (scat);
1654                 GSList *tmp;
1655                 int i = 0;
1656                 MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
1657                 for (tmp = list; tmp; tmp = tmp->next) {
1658                         SharedInstance *inst = tmp->data;
1659                         mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1660                         i++;
1661                 }
1662                 g_slist_free (list);
1663                 return array;
1664         }
1665         return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1666 }
1667
1668 MonoArray*
1669 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1670 {
1671         const CategoryDesc* cat;
1672         if (mono_string_compare_ascii (machine, "."))
1673                 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1674         cat = find_category (category);
1675         if (!cat)
1676                 return get_custom_instances (category);
1677         switch (cat->instance_type) {
1678         case MonoInstance:
1679                 return get_mono_instances ();
1680         case CPUInstance:
1681                 return get_cpu_instances ();
1682         case ProcessInstance:
1683                 return get_processes_instances ();
1684         case NetworkInterfaceInstance:
1685                 return get_networkinterface_instances ();
1686         case ThreadInstance:
1687         default:
1688                 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1689         }
1690 }
1691