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