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