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