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