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