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