2009-09-25 Gonzalo Paniagua Javier <gonzalo@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 /* 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         
365         perfctr_lock ();
366         if (!pid_to_shared_area) {
367                 perfctr_unlock ();
368                 return;
369         }
370         unref_pid_unlocked (vt->pid);
371         perfctr_unlock ();
372 }
373
374 void
375 mono_perfcounters_init (void)
376 {
377         int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
378         d_offset += 7;
379         d_offset &= ~7;
380
381         InitializeCriticalSection (&perfctr_mutex);
382
383         shared_area = mono_shared_area ();
384         shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
385         shared_area->counters_size = sizeof (MonoPerfCounters);
386         shared_area->data_start = d_offset;
387         shared_area->size = 4096;
388         mono_perfcounters = &shared_area->counters;
389 }
390
391 static int
392 perfctr_type_compress (int type)
393 {
394         int i;
395         for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
396                 if (simple_type_to_type [i] == type)
397                         return i;
398         }
399         /* NumberOfItems32 */
400         return 2;
401 }
402
403 static unsigned char*
404 shared_data_find_room (int size)
405 {
406         unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
407         unsigned char *end = (unsigned char *)shared_area + shared_area->size;
408
409         size += 7;
410         size &= ~7;
411         while (p < end) {
412                 unsigned short *next;
413                 if (*p == FTYPE_END) {
414                         if (size < (end - p))
415                                 return p;
416                         return NULL;
417                 }
418                 if (p + 4 > end)
419                         return NULL;
420                 next = (unsigned short*)(p + 2);
421                 if (*p == FTYPE_DELETED) {
422                         /* we reuse only if it's the same size */
423                         if (*next == size) {
424                                 return p;
425                         }
426                 }
427                 p += *next;
428         }
429         return NULL;
430 }
431
432 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
433
434 static void
435 foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
436 {
437         while (p < end) {
438                 unsigned short *next;
439                 if (p + 4 > end)
440                         return;
441                 next = (unsigned short*)(p + 2);
442                 if (!func ((SharedHeader*)p, data))
443                         return;
444                 if (*p == FTYPE_END)
445                         return;
446                 p += *next;
447         }
448 }
449
450 static void
451 foreach_shared_item (SharedFunc func, void *data)
452 {
453         unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
454         unsigned char *end = (unsigned char *)shared_area + shared_area->size;
455
456         foreach_shared_item_in_area (p, end, func, data);
457 }
458
459 static int
460 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
461 {
462         /* FIXME: make this case insensitive */
463         guint16 *strc = mono_string_chars (str);
464         while (*strc == *ascii_str++) {
465                 if (*strc == 0)
466                         return 0;
467                 strc++;
468         }
469         return *strc - *(const unsigned char *)(ascii_str - 1);
470 }
471
472 typedef struct {
473         MonoString *name;
474         SharedCategory *cat;
475 } CatSearch;
476
477 static gboolean
478 category_search (SharedHeader *header, void *data)
479 {
480         CatSearch *search = data;
481         if (header->ftype == FTYPE_CATEGORY) {
482                 SharedCategory *cat = (SharedCategory*)header;
483                 if (mono_string_compare_ascii (search->name, cat->name) == 0) {
484                         search->cat = cat;
485                         return FALSE;
486                 }
487         }
488         return TRUE;
489 }
490
491 static SharedCategory*
492 find_custom_category (MonoString *name)
493 {
494         CatSearch search;
495         search.name = name;
496         search.cat = NULL;
497         foreach_shared_item (category_search, &search);
498         return search.cat;
499 }
500
501 static gboolean
502 category_collect (SharedHeader *header, void *data)
503 {
504         GSList **list = data;
505         if (header->ftype == FTYPE_CATEGORY) {
506                 *list = g_slist_prepend (*list, header);
507         }
508         return TRUE;
509 }
510
511 static GSList*
512 get_custom_categories (void) {
513         GSList *list = NULL;
514         foreach_shared_item (category_collect, &list);
515         return list;
516 }
517
518 static char*
519 custom_category_counters (SharedCategory* cat)
520 {
521         char *p = cat->name + strlen (cat->name) + 1;
522         p += strlen (p) + 1; /* skip category help */
523         return p;
524 }
525
526 static SharedCounter*
527 find_custom_counter (SharedCategory* cat, MonoString *name)
528 {
529         int i;
530         char *p = custom_category_counters (cat);
531         for (i = 0; i < cat->num_counters; ++i) {
532                 SharedCounter *counter = (SharedCounter*)p;
533                 if (mono_string_compare_ascii (name, counter->name) == 0)
534                         return counter;
535                 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
536                 p += strlen (p) + 1; /* skip counter help */
537         }
538         return NULL;
539 }
540
541 typedef struct {
542         unsigned int cat_offset;
543         SharedCategory* cat;
544         MonoString *instance;
545         SharedInstance* result;
546         GSList *list;
547 } InstanceSearch;
548
549 static gboolean
550 instance_search (SharedHeader *header, void *data)
551 {
552         InstanceSearch *search = data;
553         if (header->ftype == FTYPE_INSTANCE) {
554                 SharedInstance *ins = (SharedInstance*)header;
555                 if (search->cat_offset == ins->category_offset) {
556                         if (search->instance) {
557                                 if (mono_string_compare_ascii (search->instance, ins->instance_name) == 0) {
558                                         search->result = ins;
559                                         return FALSE;
560                                 }
561                         } else {
562                                 search->list = g_slist_prepend (search->list, ins);
563                         }
564                 }
565         }
566         return TRUE;
567 }
568
569 static SharedInstance*
570 find_custom_instance (SharedCategory* cat, MonoString *instance)
571 {
572         InstanceSearch search;
573         search.cat_offset = (char*)cat - (char*)shared_area;
574         search.cat = cat;
575         search.instance = instance;
576         search.list = NULL;
577         search.result = NULL;
578         foreach_shared_item (instance_search, &search);
579         return search.result;
580 }
581
582 static GSList*
583 get_custom_instances_list (SharedCategory* cat)
584 {
585         InstanceSearch search;
586         search.cat_offset = (char*)cat - (char*)shared_area;
587         search.cat = cat;
588         search.instance = NULL;
589         search.list = NULL;
590         search.result = NULL;
591         foreach_shared_item (instance_search, &search);
592         return search.list;
593 }
594
595 static char*
596 custom_category_help (SharedCategory* cat)
597 {
598         return cat->name + strlen (cat->name) + 1;
599 }
600
601 static const CounterDesc*
602 get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
603 {
604         const CounterDesc *cdesc = &predef_counters [desc->first_counter];
605         const CounterDesc *end = &predef_counters [desc [1].first_counter];
606         for (; cdesc < end; ++cdesc) {
607                 if (mono_string_compare_ascii (counter, cdesc->name) == 0)
608                         return cdesc;
609         }
610         return NULL;
611 }
612
613 /* fill the info in sample (except the raw value) */
614 static void
615 fill_sample (MonoCounterSample *sample)
616 {
617         sample->timeStamp = mono_100ns_ticks ();
618         sample->timeStamp100nSec = sample->timeStamp;
619         sample->counterTimeStamp = sample->timeStamp;
620         sample->counterFrequency = 10000000;
621         sample->systemFrequency = 10000000;
622         // the real basevalue needs to be get from a different counter...
623         sample->baseValue = 0;
624 }
625
626 static int
627 id_from_string (MonoString *instance, gboolean is_process)
628 {
629         int id = -1;
630         if (mono_string_length (instance)) {
631                 char *id_str = mono_string_to_utf8 (instance);
632                 char *end;
633                 id = strtol (id_str, &end, 0);
634                 if (end == id_str && !is_process)
635                         id = -1;
636                 g_free (id_str);
637         }
638         return id;
639 }
640
641 static MonoBoolean
642 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
643 {
644         MonoProcessError error;
645         int id = GPOINTER_TO_INT (vtable->arg);
646         int pid = id >> 5;
647         id &= 0x1f;
648         if (!only_value) {
649                 fill_sample (sample);
650                 sample->baseValue = 1;
651         }
652         sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
653         switch (id) {
654         case COUNTER_CPU_USER_TIME:
655                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
656                 return TRUE;
657         case COUNTER_CPU_PRIV_TIME:
658                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
659                 return TRUE;
660         case COUNTER_CPU_INTR_TIME:
661                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
662                 return TRUE;
663         case COUNTER_CPU_DCP_TIME:
664                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
665                 return TRUE;
666         case COUNTER_CPU_PROC_TIME:
667                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
668                 return TRUE;
669         }
670         return FALSE;
671 }
672
673 static void*
674 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
675 {
676         int id = id_from_string (instance, FALSE) << 5;
677         const CounterDesc *cdesc;
678         *custom = FALSE;
679         /* increase the shift above and the mask also in the implementation functions */
680         //g_assert (32 > desc [1].first_counter - desc->first_counter);
681         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
682                 *type = cdesc->type;
683                 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
684         }
685         return NULL;
686 }
687
688 static MonoBoolean
689 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
690 {
691         int id = GPOINTER_TO_INT (vtable->arg);
692         int pid = id >> 5;
693         if (pid < 0)
694                 return FALSE;
695         id &= 0x1f;
696         if (!only_value) {
697                 fill_sample (sample);
698                 sample->baseValue = 1;
699         }
700         sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
701         switch (id) {
702         case COUNTER_PROC_USER_TIME:
703                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
704                 return TRUE;
705         case COUNTER_PROC_PRIV_TIME:
706                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
707                 return TRUE;
708         case COUNTER_PROC_PROC_TIME:
709                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
710                 return TRUE;
711         case COUNTER_PROC_THREADS:
712                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
713                 return TRUE;
714         case COUNTER_PROC_VBYTES:
715                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
716                 return TRUE;
717         case COUNTER_PROC_WSET:
718                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
719                 return TRUE;
720         case COUNTER_PROC_PBYTES:
721                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
722                 return TRUE;
723         }
724         return FALSE;
725 }
726
727 static void*
728 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
729 {
730         int id = id_from_string (instance, TRUE) << 5;
731         const CounterDesc *cdesc;
732         *custom = FALSE;
733         /* increase the shift above and the mask also in the implementation functions */
734         //g_assert (32 > desc [1].first_counter - desc->first_counter);
735         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
736                 *type = cdesc->type;
737                 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
738         }
739         return NULL;
740 }
741
742 static MonoBoolean
743 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
744 {
745         int id = GPOINTER_TO_INT (vtable->arg);
746         if (!only_value) {
747                 fill_sample (sample);
748                 sample->baseValue = 1;
749         }
750         sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
751         switch (id) {
752         case COUNTER_MEM_NUM_OBJECTS:
753                 sample->rawValue = mono_stats.new_object_count;
754                 return TRUE;
755         }
756         return FALSE;
757 }
758
759 static void*
760 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
761 {
762         const CounterDesc *cdesc;
763         *custom = FALSE;
764         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
765                 *type = cdesc->type;
766                 return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
767         }
768         return NULL;
769 }
770
771 static MonoBoolean
772 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
773 {
774         PredefVtable *vt = (PredefVtable *)vtable;
775         const CounterDesc *desc;
776         int cat_id = GPOINTER_TO_INT (vtable->arg);
777         int id = cat_id >> 16;
778         cat_id &= 0xffff;
779         if (!only_value) {
780                 fill_sample (sample);
781                 sample->baseValue = 1;
782         }
783         desc = &predef_counters [predef_categories [cat_id].first_counter + id];
784         sample->counterType = desc->type;
785         /* FIXME: check that the offset fits inside imported counters */
786         /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
787         sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
788         return TRUE;
789 }
790
791 static ImplVtable*
792 predef_vtable (void *arg, MonoString *instance)
793 {
794         MonoSharedArea *area;
795         PredefVtable *vtable;
796         char *pids = mono_string_to_utf8 (instance);
797         int pid;
798
799         pid = atoi (pids);
800         g_free (pids);
801         area = load_sarea_for_pid (pid);
802         if (!area)
803                 return NULL;
804
805         vtable = g_new (PredefVtable, 1);
806         vtable->vtable.arg = arg;
807         vtable->vtable.sample = predef_readonly_counter;
808         vtable->vtable.cleanup = predef_cleanup;
809         vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
810         vtable->pid = pid;
811
812         return (ImplVtable*)vtable;
813 }
814
815 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
816  * this needs some way to set sample->counterType as well, though.
817  */
818 static MonoBoolean
819 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
820 {
821         int cat_id = GPOINTER_TO_INT (vtable->arg);
822         int id = cat_id >> 16;
823         cat_id &= 0xffff;
824         if (!only_value) {
825                 fill_sample (sample);
826                 sample->baseValue = 1;
827         }
828         sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
829         switch (cat_id) {
830         case CATEGORY_EXC:
831                 switch (id) {
832                 case COUNTER_EXC_THROWN:
833                         sample->rawValue = mono_perfcounters->exceptions_thrown;
834                         return TRUE;
835                 }
836                 break;
837         case CATEGORY_ASPNET:
838                 switch (id) {
839                 case COUNTER_ASPNET_REQ_Q:
840                         sample->rawValue = mono_perfcounters->aspnet_requests_queued;
841                         return TRUE;
842                 case COUNTER_ASPNET_REQ_TOTAL:
843                         sample->rawValue = mono_perfcounters->aspnet_requests;
844                         return TRUE;
845                 }
846                 break;
847         case CATEGORY_THREADPOOL:
848                 switch (id) {
849                 case COUNTER_THREADPOOL_WORKITEMS:
850                         sample->rawValue = mono_perfcounters->threadpool_workitems;
851                         return TRUE;
852                 case COUNTER_THREADPOOL_IOWORKITEMS:
853                         sample->rawValue = mono_perfcounters->threadpool_ioworkitems;
854                         return TRUE;
855                 }
856                 break;
857         }
858         return FALSE;
859 }
860
861 static gint64
862 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
863 {
864         guint32 *volatile ptr = NULL;
865         gint64 *volatile ptr64 = NULL;
866         int cat_id = GPOINTER_TO_INT (vtable->arg);
867         int id = cat_id >> 16;
868         cat_id &= 0xffff;
869         switch (cat_id) {
870         case CATEGORY_ASPNET:
871                 switch (id) {
872                 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
873                 case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
874                 }
875                 break;
876         case CATEGORY_THREADPOOL:
877                 switch (id) {
878                 case COUNTER_THREADPOOL_WORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_workitems; break;
879                 case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_ioworkitems; break;
880                 }
881                 break;
882         }
883         if (ptr) {
884                 if (do_incr) {
885                         if (value == 1)
886                                 return InterlockedIncrement ((gint32 *) ptr); /* FIXME: sign */
887                         if (value == -1)
888                                 return InterlockedDecrement ((gint32 *) ptr); /* FIXME: sign */
889
890                         *ptr += value;
891                         return *ptr;
892                 }
893                 /* this can be non-atomic */
894                 *ptr = value;
895                 return value;
896         } else if (ptr64) {
897                 if (do_incr) {
898                         /* FIXME: we need to do this atomically */
899                         /* No InterlockedIncrement64() yet */
900                         /*
901                         if (value == 1)
902                                 return InterlockedIncrement64 (ptr);
903                         if (value == -1)
904                                 return InterlockedDecrement64 (ptr);
905                         */
906
907                         *ptr64 += value;
908                         return *ptr64;
909                 }
910                 /* this can be non-atomic */
911                 *ptr64 = value;
912                 return value;
913         }
914         return 0;
915 }
916
917 static void*
918 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
919 {
920         const CounterDesc *cdesc;
921         *custom = TRUE;
922         if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
923                 *type = cdesc->type;
924                 if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
925                         return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
926                 else
927                         return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
928         }
929         return NULL;
930 }
931
932 static MonoBoolean
933 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
934 {
935         CustomVTable *counter_data = (CustomVTable *)vtable;
936         if (!only_value) {
937                 fill_sample (sample);
938                 sample->baseValue = 1;
939         }
940         sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
941         if (!vtable->arg)
942                 sample->rawValue = 0;
943         else
944                 sample->rawValue = *(guint64*)vtable->arg;
945         return TRUE;
946 }
947
948 static gint64
949 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
950 {
951         /* FIXME: check writability */
952         guint64 *ptr = vtable->arg;
953         if (ptr) {
954                 if (do_incr) {
955                         /* FIXME: we need to do this atomically */
956                         *ptr += value;
957                         return *ptr;
958                 }
959                 /* this can be non-atomic */
960                 *ptr = value;
961                 return value;
962         }
963         return 0;
964 }
965
966 static SharedInstance*
967 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, MonoString* instance)
968 {
969         SharedInstance* inst;
970         unsigned char *ptr;
971         char *p;
972         int size, data_offset;
973         char *name;
974         inst = find_custom_instance (cat, instance);
975         if (inst)
976                 return inst;
977         name = mono_string_to_utf8 (instance);
978         size = sizeof (SharedInstance) + strlen (name);
979         size += 7;
980         size &= ~7;
981         data_offset = size;
982         size += (sizeof (guint64) * cat->num_counters);
983         perfctr_lock ();
984         ptr = shared_data_find_room (size);
985         if (!ptr) {
986                 perfctr_unlock ();
987                 g_free (name);
988                 return NULL;
989         }
990         inst = (SharedInstance*)ptr;
991         inst->header.extra = 0; /* data_offset could overflow here, so we leave this field unused */
992         inst->header.size = size;
993         inst->category_offset = (char*)cat - (char*)shared_area;
994         cat->num_instances++;
995         /* now copy the variable data */
996         p = inst->instance_name;
997         strcpy (p, name);
998         p += strlen (name) + 1;
999         inst->header.ftype = FTYPE_INSTANCE;
1000         perfctr_unlock ();
1001         g_free (name);
1002
1003         return inst;
1004 }
1005
1006 static ImplVtable*
1007 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
1008 {
1009         CustomVTable* vtable;
1010         vtable = g_new0 (CustomVTable, 1);
1011         vtable->vtable.arg = data;
1012         vtable->vtable.sample = custom_writable_counter;
1013         vtable->vtable.update = custom_writable_update;
1014         vtable->instance_desc = inst;
1015         vtable->counter_desc = scounter;
1016
1017         return (ImplVtable*)vtable;
1018 }
1019
1020 static void*
1021 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
1022 {
1023         SharedCounter *scounter;
1024         SharedInstance* inst;
1025         int size;
1026
1027         scounter = find_custom_counter (cat, counter);
1028         if (!scounter)
1029                 return NULL;
1030         *type = simple_type_to_type [scounter->type];
1031         inst = custom_get_instance (cat, scounter, instance);
1032         if (!inst)
1033                 return NULL;
1034         size = sizeof (SharedInstance) + strlen (inst->instance_name);
1035         size += 7;
1036         size &= ~7;
1037         return custom_vtable (scounter, inst, (char*)inst + size + scounter->seq_num * sizeof (guint64));
1038 }
1039
1040 static const CategoryDesc*
1041 find_category (MonoString *category)
1042 {
1043         int i;
1044         for (i = 0; i < NUM_CATEGORIES; ++i) {
1045                 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1046                         return &predef_categories [i];
1047         }
1048         return NULL;
1049 }
1050
1051 void*
1052 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1053                 MonoString* machine, int *type, MonoBoolean *custom)
1054 {
1055         const CategoryDesc *cdesc;
1056         /* no support for counters on other machines */
1057         if (mono_string_compare_ascii (machine, "."))
1058                 return NULL;
1059         cdesc = find_category (category);
1060         if (!cdesc) {
1061                 SharedCategory *scat = find_custom_category (category);
1062                 if (!scat)
1063                         return NULL;
1064                 *custom = TRUE;
1065                 return custom_get_impl (scat, counter, instance, type);
1066         }
1067         switch (cdesc->id) {
1068         case CATEGORY_CPU:
1069                 return cpu_get_impl (counter, instance, type, custom);
1070         case CATEGORY_PROC:
1071                 return process_get_impl (counter, instance, type, custom);
1072         case CATEGORY_MONO_MEM:
1073                 return mono_mem_get_impl (counter, instance, type, custom);
1074         case CATEGORY_JIT:
1075         case CATEGORY_EXC:
1076         case CATEGORY_GC:
1077         case CATEGORY_REMOTING:
1078         case CATEGORY_LOADING:
1079         case CATEGORY_THREAD:
1080         case CATEGORY_INTEROP:
1081         case CATEGORY_SECURITY:
1082         case CATEGORY_ASPNET:
1083         case CATEGORY_THREADPOOL:
1084                 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
1085         }
1086         return NULL;
1087 }
1088
1089 MonoBoolean
1090 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1091 {
1092         ImplVtable *vtable = impl;
1093         if (vtable && vtable->sample)
1094                 return vtable->sample (vtable, only_value, sample);
1095         return FALSE;
1096 }
1097
1098 gint64
1099 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1100 {
1101         ImplVtable *vtable = impl;
1102         if (vtable && vtable->update)
1103                 return vtable->update (vtable, do_incr, value);
1104         return 0;
1105 }
1106
1107 void
1108 mono_perfcounter_free_data (void *impl)
1109 {
1110         ImplVtable *vtable = impl;
1111         if (vtable && vtable->cleanup)
1112                 vtable->cleanup (vtable);
1113         g_free (impl);
1114 }
1115
1116 /* Category icalls */
1117 MonoBoolean
1118 mono_perfcounter_category_del (MonoString *name)
1119 {
1120         const CategoryDesc *cdesc;
1121         SharedCategory *cat;
1122         cdesc = find_category (name);
1123         /* can't delete a predefined category */
1124         if (cdesc)
1125                 return FALSE;
1126         perfctr_lock ();
1127         cat = find_custom_category (name);
1128         /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1129         if (!cat || cat->num_instances) {
1130                 perfctr_unlock ();
1131                 return FALSE;
1132         }
1133         cat->header.ftype = FTYPE_DELETED;
1134         perfctr_unlock ();
1135         return TRUE;
1136 }
1137
1138 MonoString*
1139 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1140 {
1141         const CategoryDesc *cdesc;
1142         /* no support for counters on other machines */
1143         if (mono_string_compare_ascii (machine, "."))
1144                 return NULL;
1145         cdesc = find_category (category);
1146         if (!cdesc) {
1147                 SharedCategory *scat = find_custom_category (category);
1148                 if (!scat)
1149                         return NULL;
1150                 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1151         }
1152         return mono_string_new (mono_domain_get (), cdesc->help);
1153 }
1154
1155 /*
1156  * Check if the category named @category exists on @machine. If @counter is not NULL, return
1157  * TRUE only if a counter with that name exists in the category.
1158  */
1159 MonoBoolean
1160 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1161 {
1162         const CategoryDesc *cdesc;
1163         /* no support for counters on other machines */
1164         if (mono_string_compare_ascii (machine, "."))
1165                 return FALSE;
1166         cdesc = find_category (category);
1167         if (!cdesc) {
1168                 SharedCategory *scat = find_custom_category (category);
1169                 if (!scat)
1170                         return FALSE;
1171                 /* counter is allowed to be null */
1172                 if (!counter)
1173                         return TRUE;
1174                 /* search through the custom category */
1175                 return find_custom_counter (scat, counter) != NULL;
1176         }
1177         /* counter is allowed to be null */
1178         if (!counter)
1179                 return TRUE;
1180         if (get_counter_in_category (cdesc, counter))
1181                 return TRUE;
1182         return FALSE;
1183 }
1184
1185 /* C map of the type with the same name */
1186 typedef struct {
1187         MonoObject object;
1188         MonoString *help;
1189         MonoString *name;
1190         int type;
1191 } CounterCreationData;
1192
1193 /*
1194  * Since we'll keep a copy of the category per-process, we should also make sure
1195  * categories with the same name are compatible.
1196  */
1197 MonoBoolean
1198 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1199 {
1200         int result = FALSE;
1201         int i, size;
1202         int num_counters = mono_array_length (items);
1203         int counters_data_size;
1204         char *name;
1205         char *chelp;
1206         char **counter_info;
1207         unsigned char *ptr;
1208         char *p;
1209         SharedCategory *cat;
1210
1211         /* FIXME: ensure there isn't a category created already */
1212         name = mono_string_to_utf8 (category);
1213         chelp = mono_string_to_utf8 (help);
1214         counter_info = g_new0 (char*, num_counters * 2);
1215         /* calculate the size we need structure size + name/help + 2 0 string terminators */
1216         size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1217         for (i = 0; i < num_counters; ++i) {
1218                 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1219                 counter_info [i * 2] = mono_string_to_utf8 (data->name);
1220                 counter_info [i * 2 + 1] = mono_string_to_utf8 (data->help);
1221                 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1222         }
1223         for (i = 0; i < num_counters * 2; ++i) {
1224                 if (!counter_info [i])
1225                         goto failure;
1226                 size += strlen (counter_info [i]) + 1;
1227         }
1228         size += 7;
1229         size &= ~7;
1230         counters_data_size = num_counters * 8; /* optimize for size later */
1231         if (size > 65535)
1232                 goto failure;
1233         perfctr_lock ();
1234         ptr = shared_data_find_room (size);
1235         if (!ptr) {
1236                 perfctr_unlock ();
1237                 goto failure;
1238         }
1239         cat = (SharedCategory*)ptr;
1240         cat->header.extra = type;
1241         cat->header.size = size;
1242         cat->num_counters = num_counters;
1243         cat->counters_data_size = counters_data_size;
1244         /* now copy the vaiable data */
1245         p = cat->name;
1246         strcpy (p, name);
1247         p += strlen (name) + 1;
1248         strcpy (p, chelp);
1249         p += strlen (chelp) + 1;
1250         for (i = 0; i < num_counters; ++i) {
1251                 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1252                 /* emit the SharedCounter structures */
1253                 *p++ = perfctr_type_compress (data->type);
1254                 *p++ = i;
1255                 strcpy (p, counter_info [i * 2]);
1256                 p += strlen (counter_info [i * 2]) + 1;
1257                 strcpy (p, counter_info [i * 2 + 1]);
1258                 p += strlen (counter_info [i * 2 + 1]) + 1;
1259         }
1260         cat->header.ftype = FTYPE_CATEGORY;
1261
1262         perfctr_unlock ();
1263         result = TRUE;
1264 failure:
1265         for (i = 0; i < num_counters * 2; ++i) {
1266                 g_free (counter_info [i]);
1267         }
1268         g_free (counter_info);
1269         g_free (name);
1270         g_free (chelp);
1271         return result;
1272 }
1273
1274 int
1275 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1276 {
1277         const CategoryDesc *cdesc;
1278         /* no support for counters on other machines */
1279         /*FIXME: machine appears to be wrong
1280         if (mono_string_compare_ascii (machine, "."))
1281                 return FALSE;*/
1282         cdesc = find_category (category);
1283         if (!cdesc) {
1284                 SharedCategory *scat;
1285                 scat = find_custom_category (category);
1286                 if (!scat)
1287                         return FALSE;
1288                 if (find_custom_instance (scat, instance))
1289                         return TRUE;
1290         } else {
1291                 /* FIXME: search instance */
1292         }
1293         return FALSE;
1294 }
1295
1296 MonoArray*
1297 mono_perfcounter_category_names (MonoString *machine)
1298 {
1299         int i;
1300         MonoArray *res;
1301         MonoDomain *domain = mono_domain_get ();
1302         GSList *custom_categories, *tmp;
1303         /* no support for counters on other machines */
1304         if (mono_string_compare_ascii (machine, "."))
1305                 return mono_array_new (domain, mono_get_string_class (), 0);
1306         perfctr_lock ();
1307         custom_categories = get_custom_categories ();
1308         res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1309         for (i = 0; i < NUM_CATEGORIES; ++i) {
1310                 const CategoryDesc *cdesc = &predef_categories [i];
1311                 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1312         }
1313         for (tmp = custom_categories; tmp; tmp = tmp->next) {
1314                 SharedCategory *scat = tmp->data;
1315                 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1316                 i++;
1317         }
1318         perfctr_unlock ();
1319         g_slist_free (custom_categories);
1320         return res;
1321 }
1322
1323 MonoArray*
1324 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1325 {
1326         int i;
1327         SharedCategory *scat;
1328         const CategoryDesc *cdesc;
1329         MonoArray *res;
1330         MonoDomain *domain = mono_domain_get ();
1331         /* no support for counters on other machines */
1332         if (mono_string_compare_ascii (machine, "."))
1333                 return mono_array_new (domain, mono_get_string_class (), 0);
1334         cdesc = find_category (category);
1335         if (cdesc) {
1336                 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1337                 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1338                         const CounterDesc *desc = &predef_counters [i];
1339                         mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1340                 }
1341                 return res;
1342         }
1343         perfctr_lock ();
1344         scat = find_custom_category (category);
1345         if (scat) {
1346                 char *p = custom_category_counters (scat);
1347                 int i;
1348                 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1349                 for (i = 0; i < scat->num_counters; ++i) {
1350                         mono_array_setref (res, i, mono_string_new (domain, p + 1));
1351                         p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
1352                         p += strlen (p) + 1; /* skip counter help */
1353                 }
1354                 perfctr_unlock ();
1355                 return res;
1356         }
1357         perfctr_unlock ();
1358         return mono_array_new (domain, mono_get_string_class (), 0);
1359 }
1360
1361 static MonoArray*
1362 get_string_array (void **array, int count, gboolean is_process)
1363 {
1364         int i;
1365         MonoDomain *domain = mono_domain_get ();
1366         MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1367         for (i = 0; i < count; ++i) {
1368                 char buf [128];
1369                 char *p;
1370                 if (is_process) {
1371                         char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1372                         p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1373                 } else {
1374                         sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1375                         p = buf;
1376                 }
1377                 mono_array_setref (res, i, mono_string_new (domain, p));
1378                 if (p != buf)
1379                         g_free (p);
1380         }
1381         return res;
1382 }
1383
1384 static MonoArray*
1385 get_mono_instances (void)
1386 {
1387         int count = 64;
1388         int res;
1389         void **buf = NULL;
1390         MonoArray *array;
1391         do {
1392                 count *= 2;
1393                 g_free (buf);
1394                 buf = g_new (void*, count);
1395                 res = mono_shared_area_instances (buf, count);
1396         } while (res == count);
1397         array = get_string_array (buf, res, TRUE);
1398         g_free (buf);
1399         return array;
1400 }
1401
1402 static MonoArray*
1403 get_cpu_instances (void)
1404 {
1405         void **buf = NULL;
1406         int i, count;
1407         MonoArray *array;
1408         count = mono_cpu_count ();
1409         buf = g_new (void*, count);
1410         for (i = 0; i < count; ++i)
1411                 buf [i] = GINT_TO_POINTER (i);
1412         array = get_string_array (buf, count, FALSE);
1413         g_free (buf);
1414         return array;
1415 }
1416
1417 static MonoArray*
1418 get_processes_instances (void)
1419 {
1420         MonoArray *array;
1421         int count = 0;
1422         void **buf = mono_process_list (&count);
1423         if (!buf)
1424                 return get_string_array (NULL, 0, FALSE);
1425         array = get_string_array (buf, count, TRUE);
1426         g_free (buf);
1427         return array;
1428 }
1429
1430 static MonoArray*
1431 get_custom_instances (MonoString *category)
1432 {
1433         SharedCategory *scat;
1434         scat = find_custom_category (category);
1435         if (scat) {
1436                 GSList *list = get_custom_instances_list (scat);
1437                 GSList *tmp;
1438                 int i = 0;
1439                 MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
1440                 for (tmp = list; tmp; tmp = tmp->next) {
1441                         SharedInstance *inst = tmp->data;
1442                         mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1443                         i++;
1444                 }
1445                 g_slist_free (list);
1446                 return array;
1447         }
1448         return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1449 }
1450
1451 MonoArray*
1452 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1453 {
1454         const CategoryDesc* cat;
1455         if (mono_string_compare_ascii (machine, "."))
1456                 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1457         cat = find_category (category);
1458         if (!cat)
1459                 return get_custom_instances (category);
1460         switch (cat->instance_type) {
1461         case MonoInstance:
1462                 return get_mono_instances ();
1463         case CPUInstance:
1464                 return get_cpu_instances ();
1465         case ProcessInstance:
1466                 return get_processes_instances ();
1467         case ThreadInstance:
1468         default:
1469                 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1470         }
1471 }
1472