Merge pull request #1156 from felfert/master
[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_mutex_lock (&perfctr_mutex)
157 #define perfctr_unlock() mono_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 = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
363         if (!data) {
364                 area = 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 = 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 = 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         vm_statistics_data_t vmstat;
502         if (KERN_SUCCESS != host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmstat, &count)) {
503                 g_warning ("Mono was unable to retrieve memory usage!");
504                 return 0;
505         }
506
507         return (guint64) vmstat.free_count;
508
509 #elif defined (HAVE_SYSCONF)
510         guint64 page_size = 0, num_pages = 0;
511
512         /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
513          * reports invalid values, please add your OS specific code below. */
514 #ifdef _SC_PAGESIZE
515         page_size = (guint64)sysconf (_SC_PAGESIZE);
516 #endif
517
518 #ifdef _SC_AVPHYS_PAGES
519         num_pages = (guint64)sysconf (_SC_AVPHYS_PAGES);
520 #endif
521
522         if (!page_size || !num_pages) {
523                 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
524                 return 0;
525         }
526
527         return page_size * num_pages;
528 #else
529         return 0;
530 #endif
531 }
532
533 void
534 mono_perfcounters_init (void)
535 {
536         int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
537         d_offset += 7;
538         d_offset &= ~7;
539
540         mono_mutex_init_recursive (&perfctr_mutex);
541
542         shared_area = mono_shared_area ();
543         shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
544         shared_area->counters_size = sizeof (MonoPerfCounters);
545         shared_area->data_start = d_offset;
546         shared_area->size = 4096;
547         mono_perfcounters = &shared_area->counters;
548 }
549
550 static int
551 perfctr_type_compress (int type)
552 {
553         int i;
554         for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
555                 if (simple_type_to_type [i] == type)
556                         return i;
557         }
558         /* NumberOfItems32 */
559         return 2;
560 }
561
562 static SharedHeader*
563 shared_data_reserve_room (int size, int ftype)
564 {
565         SharedHeader* header;
566         unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
567         unsigned char *end = (unsigned char *)shared_area + shared_area->size;
568
569         size += 7;
570         size &= ~7;
571         while (p < end) {
572                 unsigned short *next;
573                 if (*p == FTYPE_END) {
574                         if (size < (end - p))
575                                 goto res;
576                         return NULL;
577                 }
578                 if (p + 4 > end)
579                         return NULL;
580                 next = (unsigned short*)(p + 2);
581                 if (*p == FTYPE_DELETED) {
582                         /* we reuse only if it's the same size */
583                         if (*next == size) {
584                                 goto res;
585                         }
586                 }
587                 p += *next;
588         }
589         return NULL;
590
591 res:
592         header = (SharedHeader*)p;
593         header->ftype = ftype;
594         header->extra = 0; /* data_offset could overflow here, so we leave this field unused */
595         header->size = size;
596
597         return header;
598 }
599
600 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
601
602 static void
603 foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
604 {
605         while (p < end) {
606                 unsigned short *next;
607                 if (p + 4 > end)
608                         return;
609                 next = (unsigned short*)(p + 2);
610                 if (!func ((SharedHeader*)p, data))
611                         return;
612                 if (*p == FTYPE_END)
613                         return;
614                 p += *next;
615         }
616 }
617
618 static void
619 foreach_shared_item (SharedFunc func, void *data)
620 {
621         unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
622         unsigned char *end = (unsigned char *)shared_area + shared_area->size;
623
624         foreach_shared_item_in_area (p, end, func, data);
625 }
626
627 static int
628 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
629 {
630         /* FIXME: make this case insensitive */
631         guint16 *strc = mono_string_chars (str);
632         while (*strc == *ascii_str++) {
633                 if (*strc == 0)
634                         return 0;
635                 strc++;
636         }
637         return *strc - *(const unsigned char *)(ascii_str - 1);
638 }
639
640 typedef struct {
641         MonoString *name;
642         SharedCategory *cat;
643 } CatSearch;
644
645 static gboolean
646 category_search (SharedHeader *header, void *data)
647 {
648         CatSearch *search = data;
649         if (header->ftype == FTYPE_CATEGORY) {
650                 SharedCategory *cat = (SharedCategory*)header;
651                 if (mono_string_compare_ascii (search->name, cat->name) == 0) {
652                         search->cat = cat;
653                         return FALSE;
654                 }
655         }
656         return TRUE;
657 }
658
659 static SharedCategory*
660 find_custom_category (MonoString *name)
661 {
662         CatSearch search;
663         search.name = name;
664         search.cat = NULL;
665         foreach_shared_item (category_search, &search);
666         return search.cat;
667 }
668
669 static gboolean
670 category_collect (SharedHeader *header, void *data)
671 {
672         GSList **list = data;
673         if (header->ftype == FTYPE_CATEGORY) {
674                 *list = g_slist_prepend (*list, header);
675         }
676         return TRUE;
677 }
678
679 static GSList*
680 get_custom_categories (void) {
681         GSList *list = NULL;
682         foreach_shared_item (category_collect, &list);
683         return list;
684 }
685
686 static char*
687 custom_category_counters (SharedCategory* cat)
688 {
689         char *p = cat->name + strlen (cat->name) + 1;
690         p += strlen (p) + 1; /* skip category help */
691         return p;
692 }
693
694 static SharedCounter*
695 find_custom_counter (SharedCategory* cat, MonoString *name)
696 {
697         int i;
698         char *p = custom_category_counters (cat);
699         for (i = 0; i < cat->num_counters; ++i) {
700                 SharedCounter *counter = (SharedCounter*)p;
701                 if (mono_string_compare_ascii (name, counter->name) == 0)
702                         return counter;
703                 p += 2; /* skip counter type */
704                 p += strlen (p) + 1; /* skip counter name */
705                 p += strlen (p) + 1; /* skip counter help */
706         }
707         return NULL;
708 }
709
710 typedef struct {
711         unsigned int cat_offset;
712         SharedCategory* cat;
713         char *name;
714         SharedInstance* result;
715         GSList *list;
716 } InstanceSearch;
717
718 static gboolean
719 instance_search (SharedHeader *header, void *data)
720 {
721         InstanceSearch *search = data;
722         if (header->ftype == FTYPE_INSTANCE) {
723                 SharedInstance *ins = (SharedInstance*)header;
724                 if (search->cat_offset == ins->category_offset) {
725                         if (search->name) {
726                                 if (strcmp (search->name, ins->instance_name) == 0) {
727                                         search->result = ins;
728                                         return FALSE;
729                                 }
730                         } else {
731                                 search->list = g_slist_prepend (search->list, ins);
732                         }
733                 }
734         }
735         return TRUE;
736 }
737
738 static SharedInstance*
739 find_custom_instance (SharedCategory* cat, char *name)
740 {
741         InstanceSearch search;
742         search.cat_offset = (char*)cat - (char*)shared_area;
743         search.cat = cat;
744         search.name = name;
745         search.list = NULL;
746         search.result = NULL;
747         foreach_shared_item (instance_search, &search);
748         return search.result;
749 }
750
751 static GSList*
752 get_custom_instances_list (SharedCategory* cat)
753 {
754         InstanceSearch search;
755         search.cat_offset = (char*)cat - (char*)shared_area;
756         search.cat = cat;
757         search.name = NULL;
758         search.list = NULL;
759         search.result = NULL;
760         foreach_shared_item (instance_search, &search);
761         return search.list;
762 }
763
764 static char*
765 custom_category_help (SharedCategory* cat)
766 {
767         return cat->name + strlen (cat->name) + 1;
768 }
769
770 static const CounterDesc*
771 get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
772 {
773         const CounterDesc *cdesc = &predef_counters [desc->first_counter];
774         const CounterDesc *end = &predef_counters [desc [1].first_counter];
775         for (; cdesc < end; ++cdesc) {
776                 if (mono_string_compare_ascii (counter, cdesc->name) == 0)
777                         return cdesc;
778         }
779         return NULL;
780 }
781
782 /* fill the info in sample (except the raw value) */
783 static void
784 fill_sample (MonoCounterSample *sample)
785 {
786         sample->timeStamp = mono_100ns_ticks ();
787         sample->timeStamp100nSec = sample->timeStamp;
788         sample->counterTimeStamp = sample->timeStamp;
789         sample->counterFrequency = 10000000;
790         sample->systemFrequency = 10000000;
791         // the real basevalue needs to be get from a different counter...
792         sample->baseValue = 0;
793 }
794
795 static int
796 id_from_string (MonoString *instance, gboolean is_process)
797 {
798         int id = -1;
799         if (mono_string_length (instance)) {
800                 char *id_str = mono_string_to_utf8 (instance);
801                 char *end;
802                 id = strtol (id_str, &end, 0);
803                 if (end == id_str && !is_process)
804                         id = -1;
805                 g_free (id_str);
806         }
807         return id;
808 }
809
810 static MonoBoolean
811 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
812 {
813         MonoProcessError error;
814         int id = GPOINTER_TO_INT (vtable->arg);
815         int pid = id >> 5;
816         id &= 0x1f;
817         if (!only_value) {
818                 fill_sample (sample);
819                 sample->baseValue = 1;
820         }
821         sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
822         switch (id) {
823         case COUNTER_CPU_USER_TIME:
824                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
825                 return TRUE;
826         case COUNTER_CPU_PRIV_TIME:
827                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
828                 return TRUE;
829         case COUNTER_CPU_INTR_TIME:
830                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
831                 return TRUE;
832         case COUNTER_CPU_DCP_TIME:
833                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
834                 return TRUE;
835         case COUNTER_CPU_PROC_TIME:
836                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
837                 return TRUE;
838         }
839         return FALSE;
840 }
841
842 static void*
843 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
844 {
845         int id = id_from_string (instance, FALSE) << 5;
846         const CounterDesc *cdesc;
847         *custom = FALSE;
848         /* increase the shift above and the mask also in the implementation functions */
849         //g_assert (32 > desc [1].first_counter - desc->first_counter);
850         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
851                 *type = cdesc->type;
852                 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
853         }
854         return NULL;
855 }
856
857 static MonoBoolean
858 get_network_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
859 {
860         MonoNetworkError error = MONO_NETWORK_ERROR_OTHER;
861         NetworkVtableArg *narg = (NetworkVtableArg*) vtable->arg;
862         if (!only_value) {
863                 fill_sample (sample);
864         }
865
866         sample->counterType = predef_counters [predef_categories [CATEGORY_NETWORK].first_counter + narg->id].type;
867         switch (narg->id) {
868         case COUNTER_NETWORK_BYTESRECSEC:
869                 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESREC, &error);
870                 break;
871         case COUNTER_NETWORK_BYTESSENTSEC:
872                 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESSENT, &error);
873                 break;
874         case COUNTER_NETWORK_BYTESTOTALSEC:
875                 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESTOTAL, &error);
876                 break;
877         }
878
879         if (error == MONO_NETWORK_ERROR_NONE)
880                 return TRUE;
881         else
882                 return FALSE;
883 }
884
885 static void
886 network_cleanup (ImplVtable *vtable)
887 {
888         NetworkVtableArg *narg;
889
890         if (vtable == NULL)
891                 return;
892
893         narg = vtable->arg;
894         if (narg == NULL)
895                 return;
896
897         g_free (narg->name);
898         narg->name = NULL;
899         g_free (narg);
900         vtable->arg = NULL;
901 }
902
903 static void*
904 network_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
905 {
906         const CounterDesc *cdesc;
907         NetworkVtableArg *narg;
908         ImplVtable *vtable;
909         char *instance_name;
910
911         *custom = FALSE;
912         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_NETWORK], counter))) {
913                 instance_name = mono_string_to_utf8 (instance);
914                 narg = g_new0 (NetworkVtableArg, 1);
915                 narg->id = cdesc->id;
916                 narg->name = instance_name;
917                 *type = cdesc->type;
918                 vtable = create_vtable (narg, get_network_counter, NULL);
919                 vtable->cleanup = network_cleanup;
920                 return vtable;
921         }
922         return NULL;
923 }
924
925 static MonoBoolean
926 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
927 {
928         int id = GPOINTER_TO_INT (vtable->arg);
929         int pid = id >> 5;
930         if (pid < 0)
931                 return FALSE;
932         id &= 0x1f;
933         if (!only_value) {
934                 fill_sample (sample);
935                 sample->baseValue = 1;
936         }
937         sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
938         switch (id) {
939         case COUNTER_PROC_USER_TIME:
940                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
941                 return TRUE;
942         case COUNTER_PROC_PRIV_TIME:
943                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
944                 return TRUE;
945         case COUNTER_PROC_PROC_TIME:
946                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
947                 return TRUE;
948         case COUNTER_PROC_THREADS:
949                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
950                 return TRUE;
951         case COUNTER_PROC_VBYTES:
952                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
953                 return TRUE;
954         case COUNTER_PROC_WSET:
955                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
956                 return TRUE;
957         case COUNTER_PROC_PBYTES:
958                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
959                 return TRUE;
960         }
961         return FALSE;
962 }
963
964 static void*
965 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
966 {
967         int id = id_from_string (instance, TRUE) << 5;
968         const CounterDesc *cdesc;
969         *custom = FALSE;
970         /* increase the shift above and the mask also in the implementation functions */
971         //g_assert (32 > desc [1].first_counter - desc->first_counter);
972         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
973                 *type = cdesc->type;
974                 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
975         }
976         return NULL;
977 }
978
979 static MonoBoolean
980 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
981 {
982         int id = GPOINTER_TO_INT (vtable->arg);
983         if (!only_value) {
984                 fill_sample (sample);
985                 sample->baseValue = 1;
986         }
987         sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
988         switch (id) {
989         case COUNTER_MEM_NUM_OBJECTS:
990                 sample->rawValue = 0;
991                 return TRUE;
992         case COUNTER_MEM_PHYS_TOTAL:
993                 sample->rawValue = mono_determine_physical_ram_size ();;
994                 return TRUE;
995         case COUNTER_MEM_PHYS_AVAILABLE:
996                 sample->rawValue = mono_determine_physical_ram_available_size ();;
997                 return TRUE;
998         }
999         return FALSE;
1000 }
1001
1002 static void*
1003 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
1004 {
1005         const CounterDesc *cdesc;
1006         *custom = FALSE;
1007         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
1008                 *type = cdesc->type;
1009                 return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
1010         }
1011         return NULL;
1012 }
1013
1014 static MonoBoolean
1015 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1016 {
1017         PredefVtable *vt = (PredefVtable *)vtable;
1018         const CounterDesc *desc;
1019         int cat_id = GPOINTER_TO_INT (vtable->arg);
1020         int id = cat_id >> 16;
1021         cat_id &= 0xffff;
1022         if (!only_value) {
1023                 fill_sample (sample);
1024                 sample->baseValue = 1;
1025         }
1026         desc = &predef_counters [predef_categories [cat_id].first_counter + id];
1027         sample->counterType = desc->type;
1028         /* FIXME: check that the offset fits inside imported counters */
1029         /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
1030         sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
1031         return TRUE;
1032 }
1033
1034 static ImplVtable*
1035 predef_vtable (void *arg, MonoString *instance)
1036 {
1037         MonoSharedArea *area;
1038         PredefVtable *vtable;
1039         char *pids = mono_string_to_utf8 (instance);
1040         int pid;
1041
1042         pid = atoi (pids);
1043         g_free (pids);
1044         area = load_sarea_for_pid (pid);
1045         if (!area)
1046                 return NULL;
1047
1048         vtable = g_new (PredefVtable, 1);
1049         vtable->vtable.arg = arg;
1050         vtable->vtable.sample = predef_readonly_counter;
1051         vtable->vtable.cleanup = predef_cleanup;
1052         vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
1053         vtable->pid = pid;
1054
1055         return (ImplVtable*)vtable;
1056 }
1057
1058 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
1059  * this needs some way to set sample->counterType as well, though.
1060  */
1061 static MonoBoolean
1062 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1063 {
1064         int cat_id = GPOINTER_TO_INT (vtable->arg);
1065         int id = cat_id >> 16;
1066         cat_id &= 0xffff;
1067         if (!only_value) {
1068                 fill_sample (sample);
1069                 sample->baseValue = 1;
1070         }
1071         sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
1072         switch (cat_id) {
1073         case CATEGORY_EXC:
1074                 switch (id) {
1075                 case COUNTER_EXC_THROWN:
1076                         sample->rawValue = mono_perfcounters->exceptions_thrown;
1077                         return TRUE;
1078                 }
1079                 break;
1080         case CATEGORY_ASPNET:
1081                 switch (id) {
1082                 case COUNTER_ASPNET_REQ_Q:
1083                         sample->rawValue = mono_perfcounters->aspnet_requests_queued;
1084                         return TRUE;
1085                 case COUNTER_ASPNET_REQ_TOTAL:
1086                         sample->rawValue = mono_perfcounters->aspnet_requests;
1087                         return TRUE;
1088                 }
1089                 break;
1090         case CATEGORY_THREADPOOL:
1091                 switch (id) {
1092                 case COUNTER_THREADPOOL_WORKITEMS:
1093                         sample->rawValue = mono_perfcounters->threadpool_workitems;
1094                         return TRUE;
1095                 case COUNTER_THREADPOOL_IOWORKITEMS:
1096                         sample->rawValue = mono_perfcounters->threadpool_ioworkitems;
1097                         return TRUE;
1098                 case COUNTER_THREADPOOL_THREADS:
1099                         sample->rawValue = mono_perfcounters->threadpool_threads;
1100                         return TRUE;
1101                 case COUNTER_THREADPOOL_IOTHREADS:
1102                         sample->rawValue = mono_perfcounters->threadpool_iothreads;
1103                         return TRUE;
1104                 }
1105                 break;
1106         case CATEGORY_JIT:
1107                 switch (id) {
1108                 case COUNTER_JIT_BYTES:
1109                         sample->rawValue = mono_perfcounters->jit_bytes;
1110                         return TRUE;
1111                 case COUNTER_JIT_METHODS:
1112                         sample->rawValue = mono_perfcounters->jit_methods;
1113                         return TRUE;
1114                 case COUNTER_JIT_TIME:
1115                         sample->rawValue = mono_perfcounters->jit_time;
1116                         return TRUE;
1117                 case COUNTER_JIT_BYTES_PSEC:
1118                         sample->rawValue = mono_perfcounters->jit_bytes;
1119                         return TRUE;
1120                 case COUNTER_JIT_FAILURES:
1121                         sample->rawValue = mono_perfcounters->jit_failures;
1122                         return TRUE;
1123                 }
1124                 break;
1125         }
1126         return FALSE;
1127 }
1128
1129 static gint64
1130 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1131 {
1132         guint32 *volatile ptr = NULL;
1133         gint64 *volatile ptr64 = NULL;
1134         int cat_id = GPOINTER_TO_INT (vtable->arg);
1135         int id = cat_id >> 16;
1136         cat_id &= 0xffff;
1137         switch (cat_id) {
1138         case CATEGORY_ASPNET:
1139                 switch (id) {
1140                 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
1141                 case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
1142                 }
1143                 break;
1144         case CATEGORY_THREADPOOL:
1145                 switch (id) {
1146                 case COUNTER_THREADPOOL_WORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_workitems; break;
1147                 case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_ioworkitems; break;
1148                 case COUNTER_THREADPOOL_THREADS: ptr = &mono_perfcounters->threadpool_threads; break;
1149                 case COUNTER_THREADPOOL_IOTHREADS: ptr = &mono_perfcounters->threadpool_iothreads; break;
1150                 }
1151                 break;
1152         }
1153         if (ptr) {
1154                 if (do_incr) {
1155                         if (value == 1)
1156                                 return InterlockedIncrement ((gint32 *) ptr); /* FIXME: sign */
1157                         if (value == -1)
1158                                 return InterlockedDecrement ((gint32 *) ptr); /* FIXME: sign */
1159
1160                         *ptr += value;
1161                         return *ptr;
1162                 }
1163                 /* this can be non-atomic */
1164                 *ptr = value;
1165                 return value;
1166         } else if (ptr64) {
1167                 if (do_incr) {
1168                         /* FIXME: we need to do this atomically */
1169                         /* No InterlockedIncrement64() yet */
1170                         /*
1171                         if (value == 1)
1172                                 return InterlockedIncrement64 (ptr);
1173                         if (value == -1)
1174                                 return InterlockedDecrement64 (ptr);
1175                         */
1176
1177                         *ptr64 += value;
1178                         return *ptr64;
1179                 }
1180                 /* this can be non-atomic */
1181                 *ptr64 = value;
1182                 return value;
1183         }
1184         return 0;
1185 }
1186
1187 static void*
1188 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
1189 {
1190         const CounterDesc *cdesc;
1191         *custom = TRUE;
1192         if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
1193                 *type = cdesc->type;
1194                 if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
1195                         return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
1196                 else
1197                         return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
1198         }
1199         return NULL;
1200 }
1201
1202 static MonoBoolean
1203 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1204 {
1205         CustomVTable *counter_data = (CustomVTable *)vtable;
1206         if (!only_value) {
1207                 fill_sample (sample);
1208                 sample->baseValue = 1;
1209         }
1210         sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
1211         if (!vtable->arg)
1212                 sample->rawValue = 0;
1213         else
1214                 sample->rawValue = *(guint64*)vtable->arg;
1215         return TRUE;
1216 }
1217
1218 static gint64
1219 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1220 {
1221         /* FIXME: check writability */
1222         guint64 *ptr = vtable->arg;
1223         if (ptr) {
1224                 if (do_incr) {
1225                         /* FIXME: we need to do this atomically */
1226                         *ptr += value;
1227                         return *ptr;
1228                 }
1229                 /* this can be non-atomic */
1230                 *ptr = value;
1231                 return value;
1232         }
1233         return 0;
1234 }
1235
1236 static SharedInstance*
1237 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, char* name)
1238 {
1239         SharedInstance* inst;
1240         char *p;
1241         int size, data_offset;
1242         inst = find_custom_instance (cat, name);
1243         if (inst)
1244                 return inst;
1245         size = sizeof (SharedInstance) + strlen (name);
1246         size += 7;
1247         size &= ~7;
1248         data_offset = size;
1249         size += (sizeof (guint64) * cat->num_counters);
1250         perfctr_lock ();
1251         inst = (SharedInstance*) shared_data_reserve_room (size, FTYPE_INSTANCE);
1252         if (!inst) {
1253                 perfctr_unlock ();
1254                 g_free (name);
1255                 return NULL;
1256         }
1257         inst->category_offset = (char*)cat - (char*)shared_area;
1258         cat->num_instances++;
1259         /* now copy the variable data */
1260         p = inst->instance_name;
1261         strcpy (p, name);
1262         p += strlen (name) + 1;
1263         perfctr_unlock ();
1264
1265         return inst;
1266 }
1267
1268 static ImplVtable*
1269 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
1270 {
1271         CustomVTable* vtable;
1272         vtable = g_new0 (CustomVTable, 1);
1273         vtable->vtable.arg = data;
1274         vtable->vtable.sample = custom_writable_counter;
1275         vtable->vtable.update = custom_writable_update;
1276         vtable->instance_desc = inst;
1277         vtable->counter_desc = scounter;
1278
1279         return (ImplVtable*)vtable;
1280 }
1281
1282 static gpointer
1283 custom_get_value_address (SharedCounter *scounter, SharedInstance* sinst)
1284 {
1285         int offset = sizeof (SharedInstance) + strlen (sinst->instance_name);
1286         offset += 7;
1287         offset &= ~7;
1288         offset += scounter->seq_num * sizeof (guint64);
1289         return (char*)sinst + offset;
1290 }
1291
1292 static void*
1293 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
1294 {
1295         SharedCounter *scounter;
1296         SharedInstance* inst;
1297         char *name;
1298
1299         scounter = find_custom_counter (cat, counter);
1300         if (!scounter)
1301                 return NULL;
1302         *type = simple_type_to_type [scounter->type];
1303         name = mono_string_to_utf8 (counter);
1304         inst = custom_get_instance (cat, scounter, name);
1305         g_free (name);
1306         if (!inst)
1307                 return NULL;
1308         return custom_vtable (scounter, inst, custom_get_value_address (scounter, inst));
1309 }
1310
1311 static const CategoryDesc*
1312 find_category (MonoString *category)
1313 {
1314         int i;
1315         for (i = 0; i < NUM_CATEGORIES; ++i) {
1316                 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1317                         return &predef_categories [i];
1318         }
1319         return NULL;
1320 }
1321
1322 void*
1323 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1324                 MonoString* machine, int *type, MonoBoolean *custom)
1325 {
1326         const CategoryDesc *cdesc;
1327         /* no support for counters on other machines */
1328         if (mono_string_compare_ascii (machine, "."))
1329                 return NULL;
1330         cdesc = find_category (category);
1331         if (!cdesc) {
1332                 SharedCategory *scat = find_custom_category (category);
1333                 if (!scat)
1334                         return NULL;
1335                 *custom = TRUE;
1336                 return custom_get_impl (scat, counter, instance, type);
1337         }
1338         switch (cdesc->id) {
1339         case CATEGORY_CPU:
1340                 return cpu_get_impl (counter, instance, type, custom);
1341         case CATEGORY_PROC:
1342                 return process_get_impl (counter, instance, type, custom);
1343         case CATEGORY_MONO_MEM:
1344                 return mono_mem_get_impl (counter, instance, type, custom);
1345         case CATEGORY_NETWORK:
1346                 return network_get_impl (counter, instance, type, custom);
1347         case CATEGORY_JIT:
1348         case CATEGORY_EXC:
1349         case CATEGORY_GC:
1350         case CATEGORY_REMOTING:
1351         case CATEGORY_LOADING:
1352         case CATEGORY_THREAD:
1353         case CATEGORY_INTEROP:
1354         case CATEGORY_SECURITY:
1355         case CATEGORY_ASPNET:
1356         case CATEGORY_THREADPOOL:
1357                 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
1358         }
1359         return NULL;
1360 }
1361
1362 MonoBoolean
1363 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1364 {
1365         ImplVtable *vtable = impl;
1366         if (vtable && vtable->sample)
1367                 return vtable->sample (vtable, only_value, sample);
1368         return FALSE;
1369 }
1370
1371 gint64
1372 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1373 {
1374         ImplVtable *vtable = impl;
1375         if (vtable && vtable->update)
1376                 return vtable->update (vtable, do_incr, value);
1377         return 0;
1378 }
1379
1380 void
1381 mono_perfcounter_free_data (void *impl)
1382 {
1383         ImplVtable *vtable = impl;
1384         if (vtable && vtable->cleanup)
1385                 vtable->cleanup (vtable);
1386         g_free (impl);
1387 }
1388
1389 /* Category icalls */
1390 MonoBoolean
1391 mono_perfcounter_category_del (MonoString *name)
1392 {
1393         const CategoryDesc *cdesc;
1394         SharedCategory *cat;
1395         cdesc = find_category (name);
1396         /* can't delete a predefined category */
1397         if (cdesc)
1398                 return FALSE;
1399         perfctr_lock ();
1400         cat = find_custom_category (name);
1401         /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1402         if (!cat || cat->num_instances) {
1403                 perfctr_unlock ();
1404                 return FALSE;
1405         }
1406         cat->header.ftype = FTYPE_DELETED;
1407         perfctr_unlock ();
1408         return TRUE;
1409 }
1410
1411 MonoString*
1412 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1413 {
1414         const CategoryDesc *cdesc;
1415         /* no support for counters on other machines */
1416         if (mono_string_compare_ascii (machine, "."))
1417                 return NULL;
1418         cdesc = find_category (category);
1419         if (!cdesc) {
1420                 SharedCategory *scat = find_custom_category (category);
1421                 if (!scat)
1422                         return NULL;
1423                 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1424         }
1425         return mono_string_new (mono_domain_get (), cdesc->help);
1426 }
1427
1428 /*
1429  * Check if the category named @category exists on @machine. If @counter is not NULL, return
1430  * TRUE only if a counter with that name exists in the category.
1431  */
1432 MonoBoolean
1433 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1434 {
1435         const CategoryDesc *cdesc;
1436         /* no support for counters on other machines */
1437         if (mono_string_compare_ascii (machine, "."))
1438                 return FALSE;
1439         cdesc = find_category (category);
1440         if (!cdesc) {
1441                 SharedCategory *scat = find_custom_category (category);
1442                 if (!scat)
1443                         return FALSE;
1444                 /* counter is allowed to be null */
1445                 if (!counter)
1446                         return TRUE;
1447                 /* search through the custom category */
1448                 return find_custom_counter (scat, counter) != NULL;
1449         }
1450         /* counter is allowed to be null */
1451         if (!counter)
1452                 return TRUE;
1453         if (get_counter_in_category (cdesc, counter))
1454                 return TRUE;
1455         return FALSE;
1456 }
1457
1458 /* C map of the type with the same name */
1459 typedef struct {
1460         MonoObject object;
1461         MonoString *help;
1462         MonoString *name;
1463         int type;
1464 } CounterCreationData;
1465
1466 /*
1467  * Since we'll keep a copy of the category per-process, we should also make sure
1468  * categories with the same name are compatible.
1469  */
1470 MonoBoolean
1471 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1472 {
1473         MonoError error;
1474         int result = FALSE;
1475         int i, size;
1476         int num_counters = mono_array_length (items);
1477         int counters_data_size;
1478         char *name = NULL;
1479         char *chelp = NULL;
1480         char **counter_info = NULL;
1481         char *p;
1482         SharedCategory *cat;
1483
1484         /* FIXME: ensure there isn't a category created already */
1485         mono_error_init (&error);
1486         name = mono_string_to_utf8_checked (category, &error);
1487         if (!mono_error_ok (&error))
1488                 goto failure;
1489         chelp = mono_string_to_utf8_checked (help, &error);
1490         if (!mono_error_ok (&error))
1491                 goto failure;
1492         counter_info = g_new0 (char*, num_counters * 2);
1493         /* calculate the size we need structure size + name/help + 2 0 string terminators */
1494         size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1495         for (i = 0; i < num_counters; ++i) {
1496                 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1497                 counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error);
1498                 if (!mono_error_ok (&error))
1499                         goto failure;
1500                 counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error);
1501                 if (!mono_error_ok (&error))
1502                         goto failure;
1503                 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1504         }
1505         for (i = 0; i < num_counters * 2; ++i) {
1506                 if (!counter_info [i])
1507                         goto failure;
1508                 size += strlen (counter_info [i]) + 1;
1509         }
1510         size += 7;
1511         size &= ~7;
1512         counters_data_size = num_counters * 8; /* optimize for size later */
1513         if (size > 65535)
1514                 goto failure;
1515         perfctr_lock ();
1516         cat = (SharedCategory*) shared_data_reserve_room (size, FTYPE_CATEGORY);
1517         if (!cat) {
1518                 perfctr_unlock ();
1519                 goto failure;
1520         }
1521         cat->num_counters = num_counters;
1522         cat->counters_data_size = counters_data_size;
1523         /* now copy the vaiable data */
1524         p = cat->name;
1525         strcpy (p, name);
1526         p += strlen (name) + 1;
1527         strcpy (p, chelp);
1528         p += strlen (chelp) + 1;
1529         for (i = 0; i < num_counters; ++i) {
1530                 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1531                 /* emit the SharedCounter structures */
1532                 *p++ = perfctr_type_compress (data->type);
1533                 *p++ = i;
1534                 strcpy (p, counter_info [i * 2]);
1535                 p += strlen (counter_info [i * 2]) + 1;
1536                 strcpy (p, counter_info [i * 2 + 1]);
1537                 p += strlen (counter_info [i * 2 + 1]) + 1;
1538         }
1539
1540         perfctr_unlock ();
1541         result = TRUE;
1542 failure:
1543         if (counter_info) {
1544                 for (i = 0; i < num_counters * 2; ++i) {
1545                         g_free (counter_info [i]);
1546                 }
1547                 g_free (counter_info);
1548         }
1549         g_free (name);
1550         g_free (chelp);
1551         mono_error_cleanup (&error);
1552         return result;
1553 }
1554
1555 int
1556 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1557 {
1558         const CategoryDesc *cdesc;
1559         SharedInstance *sinst;
1560         char *name;
1561         /* no support for counters on other machines */
1562         /*FIXME: machine appears to be wrong
1563         if (mono_string_compare_ascii (machine, "."))
1564                 return FALSE;*/
1565         cdesc = find_category (category);
1566         if (!cdesc) {
1567                 SharedCategory *scat;
1568                 scat = find_custom_category (category);
1569                 if (!scat)
1570                         return FALSE;
1571                 name = mono_string_to_utf8 (instance);
1572                 sinst = find_custom_instance (scat, name);
1573                 g_free (name);
1574                 if (sinst)
1575                         return TRUE;
1576         } else {
1577                 /* FIXME: search instance */
1578         }
1579         return FALSE;
1580 }
1581
1582 MonoArray*
1583 mono_perfcounter_category_names (MonoString *machine)
1584 {
1585         int i;
1586         MonoArray *res;
1587         MonoDomain *domain = mono_domain_get ();
1588         GSList *custom_categories, *tmp;
1589         /* no support for counters on other machines */
1590         if (mono_string_compare_ascii (machine, "."))
1591                 return mono_array_new (domain, mono_get_string_class (), 0);
1592         perfctr_lock ();
1593         custom_categories = get_custom_categories ();
1594         res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1595         for (i = 0; i < NUM_CATEGORIES; ++i) {
1596                 const CategoryDesc *cdesc = &predef_categories [i];
1597                 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1598         }
1599         for (tmp = custom_categories; tmp; tmp = tmp->next) {
1600                 SharedCategory *scat = tmp->data;
1601                 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1602                 i++;
1603         }
1604         perfctr_unlock ();
1605         g_slist_free (custom_categories);
1606         return res;
1607 }
1608
1609 MonoArray*
1610 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1611 {
1612         int i;
1613         SharedCategory *scat;
1614         const CategoryDesc *cdesc;
1615         MonoArray *res;
1616         MonoDomain *domain = mono_domain_get ();
1617         /* no support for counters on other machines */
1618         if (mono_string_compare_ascii (machine, "."))
1619                 return mono_array_new (domain, mono_get_string_class (), 0);
1620         cdesc = find_category (category);
1621         if (cdesc) {
1622                 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1623                 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1624                         const CounterDesc *desc = &predef_counters [i];
1625                         mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1626                 }
1627                 return res;
1628         }
1629         perfctr_lock ();
1630         scat = find_custom_category (category);
1631         if (scat) {
1632                 char *p = custom_category_counters (scat);
1633                 int i;
1634                 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1635                 for (i = 0; i < scat->num_counters; ++i) {
1636                         mono_array_setref (res, i, mono_string_new (domain, p + 1));
1637                         p += 2; /* skip counter type */
1638                         p += strlen (p) + 1; /* skip counter name */
1639                         p += strlen (p) + 1; /* skip counter help */
1640                 }
1641                 perfctr_unlock ();
1642                 return res;
1643         }
1644         perfctr_unlock ();
1645         return mono_array_new (domain, mono_get_string_class (), 0);
1646 }
1647
1648 static MonoArray*
1649 get_string_array (void **array, int count, gboolean is_process)
1650 {
1651         int i;
1652         MonoDomain *domain = mono_domain_get ();
1653         MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1654         for (i = 0; i < count; ++i) {
1655                 char buf [128];
1656                 char *p;
1657                 if (is_process) {
1658                         char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1659                         p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1660                 } else {
1661                         sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1662                         p = buf;
1663                 }
1664                 mono_array_setref (res, i, mono_string_new (domain, p));
1665                 if (p != buf)
1666                         g_free (p);
1667         }
1668         return res;
1669 }
1670
1671 static MonoArray*
1672 get_string_array_of_strings (void **array, int count)
1673 {
1674         int i;
1675         MonoDomain *domain = mono_domain_get ();
1676         MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1677         for (i = 0; i < count; ++i) {
1678                 char* p = array[i];
1679                 mono_array_setref (res, i, mono_string_new (domain, p));
1680         }
1681
1682         return res;
1683 }
1684
1685 static MonoArray*
1686 get_mono_instances (void)
1687 {
1688         int count = 64;
1689         int res;
1690         void **buf = NULL;
1691         MonoArray *array;
1692         do {
1693                 count *= 2;
1694                 g_free (buf);
1695                 buf = g_new (void*, count);
1696                 res = mono_shared_area_instances (buf, count);
1697         } while (res == count);
1698         array = get_string_array (buf, res, TRUE);
1699         g_free (buf);
1700         return array;
1701 }
1702
1703 static MonoArray*
1704 get_cpu_instances (void)
1705 {
1706         void **buf = NULL;
1707         int i, count;
1708         MonoArray *array;
1709
1710         count = mono_cpu_count () + 1; /* +1 for "_Total" */
1711         buf = g_new (void*, count);
1712         for (i = 0; i < count; ++i)
1713                 buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
1714         array = get_string_array (buf, count, FALSE);
1715         g_free (buf);
1716         mono_array_setref (array, 0, mono_string_new (mono_domain_get (), "_Total"));
1717         return array;
1718 }
1719
1720 static MonoArray*
1721 get_processes_instances (void)
1722 {
1723         MonoArray *array;
1724         int count = 0;
1725         void **buf = mono_process_list (&count);
1726         if (!buf)
1727                 return get_string_array (NULL, 0, FALSE);
1728         array = get_string_array (buf, count, TRUE);
1729         g_free (buf);
1730         return array;
1731 }
1732
1733 static MonoArray*
1734 get_networkinterface_instances (void)
1735 {
1736         MonoArray *array;
1737         int count = 0;
1738         void **buf = mono_networkinterface_list (&count);
1739         if (!buf)
1740                 return get_string_array_of_strings (NULL, 0);
1741         array = get_string_array_of_strings (buf, count);
1742         g_strfreev ((char **) buf);
1743         return array;
1744 }
1745
1746 static MonoArray*
1747 get_custom_instances (MonoString *category)
1748 {
1749         SharedCategory *scat;
1750         scat = find_custom_category (category);
1751         if (scat) {
1752                 GSList *list = get_custom_instances_list (scat);
1753                 GSList *tmp;
1754                 int i = 0;
1755                 MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
1756                 for (tmp = list; tmp; tmp = tmp->next) {
1757                         SharedInstance *inst = tmp->data;
1758                         mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1759                         i++;
1760                 }
1761                 g_slist_free (list);
1762                 return array;
1763         }
1764         return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1765 }
1766
1767 MonoArray*
1768 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1769 {
1770         const CategoryDesc* cat;
1771         if (mono_string_compare_ascii (machine, "."))
1772                 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1773         cat = find_category (category);
1774         if (!cat)
1775                 return get_custom_instances (category);
1776         switch (cat->instance_type) {
1777         case MonoInstance:
1778                 return get_mono_instances ();
1779         case CPUInstance:
1780                 return get_cpu_instances ();
1781         case ProcessInstance:
1782                 return get_processes_instances ();
1783         case NetworkInterfaceInstance:
1784                 return get_networkinterface_instances ();
1785         case ThreadInstance:
1786         default:
1787                 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1788         }
1789 }
1790
1791 typedef struct {
1792         PerfCounterEnumCallback cb;
1793         void *data;
1794 } PerfCounterForeachData;
1795
1796 static gboolean
1797 mono_perfcounter_foreach_shared_item (SharedHeader *header, gpointer data)
1798 {
1799         int i;
1800         char *p, *name, *help;
1801         unsigned char type;
1802         int seq_num;
1803         void *addr;
1804         SharedCategory *cat;
1805         SharedCounter *counter;
1806         SharedInstance *inst;
1807         PerfCounterForeachData *foreach_data = data;
1808
1809         if (header->ftype == FTYPE_CATEGORY) {
1810                 cat = (SharedCategory*)header;
1811
1812                 p = cat->name;
1813                 p += strlen (p) + 1; /* skip category name */
1814                 p += strlen (p) + 1; /* skip category help */
1815
1816                 for (i = 0; i < cat->num_counters; ++i) {
1817                         counter = (SharedCounter*) p;
1818                         type = (unsigned char)*p++;
1819                         seq_num = (int)*p++;
1820                         name = p;
1821                         p += strlen (p) + 1;
1822                         help = p;
1823                         p += strlen (p) + 1;
1824
1825                         inst = custom_get_instance (cat, counter, name);
1826                         if (!inst)
1827                                 return FALSE;
1828                         addr = custom_get_value_address (counter, inst);
1829                         if (!foreach_data->cb (cat->name, name, type, addr ? *(gint64*)addr : 0, foreach_data->data))
1830                                 return FALSE;
1831                 }
1832         }
1833
1834         return TRUE;
1835 }
1836
1837 void
1838 mono_perfcounter_foreach (PerfCounterEnumCallback cb, gpointer data)
1839 {
1840         PerfCounterForeachData foreach_data = { cb, data };
1841
1842         perfctr_lock ();
1843
1844         foreach_shared_item (mono_perfcounter_foreach_shared_item, &foreach_data);
1845
1846         perfctr_unlock ();
1847 }
1848
1849 #else
1850 void*
1851 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance, MonoString* machine, int *type, MonoBoolean *custom)
1852 {
1853         g_assert_not_reached ();
1854 }
1855
1856 MonoBoolean
1857 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1858 {
1859         g_assert_not_reached ();
1860 }
1861
1862 gint64
1863 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1864 {
1865         g_assert_not_reached ();
1866 }
1867
1868 void
1869 mono_perfcounter_free_data (void *impl)
1870 {
1871         g_assert_not_reached ();
1872 }
1873
1874 /* Category icalls */
1875 MonoBoolean
1876 mono_perfcounter_category_del (MonoString *name)
1877 {
1878         g_assert_not_reached ();
1879 }
1880
1881 MonoString*
1882 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1883 {
1884         g_assert_not_reached ();
1885 }
1886
1887 MonoBoolean
1888 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1889 {
1890         g_assert_not_reached ();
1891 }
1892
1893 MonoBoolean
1894 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1895 {
1896         g_assert_not_reached ();
1897 }
1898
1899 int
1900 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1901 {
1902         g_assert_not_reached ();
1903 }
1904
1905 MonoArray*
1906 mono_perfcounter_category_names (MonoString *machine)
1907 {
1908         g_assert_not_reached ();
1909 }
1910
1911 MonoArray*
1912 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1913 {
1914         g_assert_not_reached ();
1915 }
1916
1917 MonoArray*
1918 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1919 {
1920         g_assert_not_reached ();
1921 }
1922 #endif