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