Merge pull request #2274 from esdrubal/udpclientreceive
[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         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_os_mutex_init_recursive (&perfctr_mutex);
541
542         shared_area = (MonoSharedArea *)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 = (CatSearch *)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 = (GSList **)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 = (InstanceSearch *)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 = (NetworkVtableArg *)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 = (guint64 *)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;
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         size += (sizeof (guint64) * cat->num_counters);
1249         perfctr_lock ();
1250         inst = (SharedInstance*) shared_data_reserve_room (size, FTYPE_INSTANCE);
1251         if (!inst) {
1252                 perfctr_unlock ();
1253                 g_free (name);
1254                 return NULL;
1255         }
1256         inst->category_offset = (char*)cat - (char*)shared_area;
1257         cat->num_instances++;
1258         /* now copy the variable data */
1259         p = inst->instance_name;
1260         strcpy (p, name);
1261         p += strlen (name) + 1;
1262         perfctr_unlock ();
1263
1264         return inst;
1265 }
1266
1267 static ImplVtable*
1268 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
1269 {
1270         CustomVTable* vtable;
1271         vtable = g_new0 (CustomVTable, 1);
1272         vtable->vtable.arg = data;
1273         vtable->vtable.sample = custom_writable_counter;
1274         vtable->vtable.update = custom_writable_update;
1275         vtable->instance_desc = inst;
1276         vtable->counter_desc = scounter;
1277
1278         return (ImplVtable*)vtable;
1279 }
1280
1281 static gpointer
1282 custom_get_value_address (SharedCounter *scounter, SharedInstance* sinst)
1283 {
1284         int offset = sizeof (SharedInstance) + strlen (sinst->instance_name);
1285         offset += 7;
1286         offset &= ~7;
1287         offset += scounter->seq_num * sizeof (guint64);
1288         return (char*)sinst + offset;
1289 }
1290
1291 static void*
1292 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
1293 {
1294         SharedCounter *scounter;
1295         SharedInstance* inst;
1296         char *name;
1297
1298         scounter = find_custom_counter (cat, counter);
1299         if (!scounter)
1300                 return NULL;
1301         *type = simple_type_to_type [scounter->type];
1302         name = mono_string_to_utf8 (counter);
1303         inst = custom_get_instance (cat, scounter, name);
1304         g_free (name);
1305         if (!inst)
1306                 return NULL;
1307         return custom_vtable (scounter, inst, (char *)custom_get_value_address (scounter, inst));
1308 }
1309
1310 static const CategoryDesc*
1311 find_category (MonoString *category)
1312 {
1313         int i;
1314         for (i = 0; i < NUM_CATEGORIES; ++i) {
1315                 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1316                         return &predef_categories [i];
1317         }
1318         return NULL;
1319 }
1320
1321 void*
1322 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1323                 MonoString* machine, int *type, MonoBoolean *custom)
1324 {
1325         const CategoryDesc *cdesc;
1326         /* no support for counters on other machines */
1327         if (mono_string_compare_ascii (machine, "."))
1328                 return NULL;
1329         cdesc = find_category (category);
1330         if (!cdesc) {
1331                 SharedCategory *scat = find_custom_category (category);
1332                 if (!scat)
1333                         return NULL;
1334                 *custom = TRUE;
1335                 return custom_get_impl (scat, counter, instance, type);
1336         }
1337         switch (cdesc->id) {
1338         case CATEGORY_CPU:
1339                 return cpu_get_impl (counter, instance, type, custom);
1340         case CATEGORY_PROC:
1341                 return process_get_impl (counter, instance, type, custom);
1342         case CATEGORY_MONO_MEM:
1343                 return mono_mem_get_impl (counter, instance, type, custom);
1344         case CATEGORY_NETWORK:
1345                 return network_get_impl (counter, instance, type, custom);
1346         case CATEGORY_JIT:
1347         case CATEGORY_EXC:
1348         case CATEGORY_GC:
1349         case CATEGORY_REMOTING:
1350         case CATEGORY_LOADING:
1351         case CATEGORY_THREAD:
1352         case CATEGORY_INTEROP:
1353         case CATEGORY_SECURITY:
1354         case CATEGORY_ASPNET:
1355         case CATEGORY_THREADPOOL:
1356                 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
1357         }
1358         return NULL;
1359 }
1360
1361 MonoBoolean
1362 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1363 {
1364         ImplVtable *vtable = (ImplVtable *)impl;
1365         if (vtable && vtable->sample)
1366                 return vtable->sample (vtable, only_value, sample);
1367         return FALSE;
1368 }
1369
1370 gint64
1371 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1372 {
1373         ImplVtable *vtable = (ImplVtable *)impl;
1374         if (vtable && vtable->update)
1375                 return vtable->update (vtable, do_incr, value);
1376         return 0;
1377 }
1378
1379 void
1380 mono_perfcounter_free_data (void *impl)
1381 {
1382         ImplVtable *vtable = (ImplVtable *)impl;
1383         if (vtable && vtable->cleanup)
1384                 vtable->cleanup (vtable);
1385         g_free (impl);
1386 }
1387
1388 /* Category icalls */
1389 MonoBoolean
1390 mono_perfcounter_category_del (MonoString *name)
1391 {
1392         const CategoryDesc *cdesc;
1393         SharedCategory *cat;
1394         cdesc = find_category (name);
1395         /* can't delete a predefined category */
1396         if (cdesc)
1397                 return FALSE;
1398         perfctr_lock ();
1399         cat = find_custom_category (name);
1400         /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1401         if (!cat || cat->num_instances) {
1402                 perfctr_unlock ();
1403                 return FALSE;
1404         }
1405         cat->header.ftype = FTYPE_DELETED;
1406         perfctr_unlock ();
1407         return TRUE;
1408 }
1409
1410 MonoString*
1411 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1412 {
1413         const CategoryDesc *cdesc;
1414         /* no support for counters on other machines */
1415         if (mono_string_compare_ascii (machine, "."))
1416                 return NULL;
1417         cdesc = find_category (category);
1418         if (!cdesc) {
1419                 SharedCategory *scat = find_custom_category (category);
1420                 if (!scat)
1421                         return NULL;
1422                 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1423         }
1424         return mono_string_new (mono_domain_get (), cdesc->help);
1425 }
1426
1427 /*
1428  * Check if the category named @category exists on @machine. If @counter is not NULL, return
1429  * TRUE only if a counter with that name exists in the category.
1430  */
1431 MonoBoolean
1432 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1433 {
1434         const CategoryDesc *cdesc;
1435         /* no support for counters on other machines */
1436         if (mono_string_compare_ascii (machine, "."))
1437                 return FALSE;
1438         cdesc = find_category (category);
1439         if (!cdesc) {
1440                 SharedCategory *scat = find_custom_category (category);
1441                 if (!scat)
1442                         return FALSE;
1443                 /* counter is allowed to be null */
1444                 if (!counter)
1445                         return TRUE;
1446                 /* search through the custom category */
1447                 return find_custom_counter (scat, counter) != NULL;
1448         }
1449         /* counter is allowed to be null */
1450         if (!counter)
1451                 return TRUE;
1452         if (get_counter_in_category (cdesc, counter))
1453                 return TRUE;
1454         return FALSE;
1455 }
1456
1457 /* C map of the type with the same name */
1458 typedef struct {
1459         MonoObject object;
1460         MonoString *help;
1461         MonoString *name;
1462         int type;
1463 } CounterCreationData;
1464
1465 /*
1466  * Since we'll keep a copy of the category per-process, we should also make sure
1467  * categories with the same name are compatible.
1468  */
1469 MonoBoolean
1470 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1471 {
1472         MonoError error;
1473         int result = FALSE;
1474         int i, size;
1475         int num_counters = mono_array_length (items);
1476         int counters_data_size;
1477         char *name = NULL;
1478         char *chelp = NULL;
1479         char **counter_info = NULL;
1480         char *p;
1481         SharedCategory *cat;
1482
1483         /* FIXME: ensure there isn't a category created already */
1484         mono_error_init (&error);
1485         name = mono_string_to_utf8_checked (category, &error);
1486         if (!mono_error_ok (&error))
1487                 goto failure;
1488         chelp = mono_string_to_utf8_checked (help, &error);
1489         if (!mono_error_ok (&error))
1490                 goto failure;
1491         counter_info = g_new0 (char*, num_counters * 2);
1492         /* calculate the size we need structure size + name/help + 2 0 string terminators */
1493         size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1494         for (i = 0; i < num_counters; ++i) {
1495                 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1496                 counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error);
1497                 if (!mono_error_ok (&error))
1498                         goto failure;
1499                 counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error);
1500                 if (!mono_error_ok (&error))
1501                         goto failure;
1502                 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1503         }
1504         for (i = 0; i < num_counters * 2; ++i) {
1505                 if (!counter_info [i])
1506                         goto failure;
1507                 size += strlen (counter_info [i]) + 1;
1508         }
1509         size += 7;
1510         size &= ~7;
1511         counters_data_size = num_counters * 8; /* optimize for size later */
1512         if (size > 65535)
1513                 goto failure;
1514         perfctr_lock ();
1515         cat = (SharedCategory*) shared_data_reserve_room (size, FTYPE_CATEGORY);
1516         if (!cat) {
1517                 perfctr_unlock ();
1518                 goto failure;
1519         }
1520         cat->num_counters = num_counters;
1521         cat->counters_data_size = counters_data_size;
1522         /* now copy the vaiable data */
1523         p = cat->name;
1524         strcpy (p, name);
1525         p += strlen (name) + 1;
1526         strcpy (p, chelp);
1527         p += strlen (chelp) + 1;
1528         for (i = 0; i < num_counters; ++i) {
1529                 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1530                 /* emit the SharedCounter structures */
1531                 *p++ = perfctr_type_compress (data->type);
1532                 *p++ = i;
1533                 strcpy (p, counter_info [i * 2]);
1534                 p += strlen (counter_info [i * 2]) + 1;
1535                 strcpy (p, counter_info [i * 2 + 1]);
1536                 p += strlen (counter_info [i * 2 + 1]) + 1;
1537         }
1538
1539         perfctr_unlock ();
1540         result = TRUE;
1541 failure:
1542         if (counter_info) {
1543                 for (i = 0; i < num_counters * 2; ++i) {
1544                         g_free (counter_info [i]);
1545                 }
1546                 g_free (counter_info);
1547         }
1548         g_free (name);
1549         g_free (chelp);
1550         mono_error_cleanup (&error);
1551         return result;
1552 }
1553
1554 int
1555 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1556 {
1557         const CategoryDesc *cdesc;
1558         SharedInstance *sinst;
1559         char *name;
1560         /* no support for counters on other machines */
1561         /*FIXME: machine appears to be wrong
1562         if (mono_string_compare_ascii (machine, "."))
1563                 return FALSE;*/
1564         cdesc = find_category (category);
1565         if (!cdesc) {
1566                 SharedCategory *scat;
1567                 scat = find_custom_category (category);
1568                 if (!scat)
1569                         return FALSE;
1570                 name = mono_string_to_utf8 (instance);
1571                 sinst = find_custom_instance (scat, name);
1572                 g_free (name);
1573                 if (sinst)
1574                         return TRUE;
1575         } else {
1576                 /* FIXME: search instance */
1577         }
1578         return FALSE;
1579 }
1580
1581 MonoArray*
1582 mono_perfcounter_category_names (MonoString *machine)
1583 {
1584         int i;
1585         MonoArray *res;
1586         MonoDomain *domain = mono_domain_get ();
1587         GSList *custom_categories, *tmp;
1588         /* no support for counters on other machines */
1589         if (mono_string_compare_ascii (machine, "."))
1590                 return mono_array_new (domain, mono_get_string_class (), 0);
1591         perfctr_lock ();
1592         custom_categories = get_custom_categories ();
1593         res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1594         for (i = 0; i < NUM_CATEGORIES; ++i) {
1595                 const CategoryDesc *cdesc = &predef_categories [i];
1596                 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1597         }
1598         for (tmp = custom_categories; tmp; tmp = tmp->next) {
1599                 SharedCategory *scat = (SharedCategory *)tmp->data;
1600                 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1601                 i++;
1602         }
1603         perfctr_unlock ();
1604         g_slist_free (custom_categories);
1605         return res;
1606 }
1607
1608 MonoArray*
1609 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1610 {
1611         int i;
1612         SharedCategory *scat;
1613         const CategoryDesc *cdesc;
1614         MonoArray *res;
1615         MonoDomain *domain = mono_domain_get ();
1616         /* no support for counters on other machines */
1617         if (mono_string_compare_ascii (machine, "."))
1618                 return mono_array_new (domain, mono_get_string_class (), 0);
1619         cdesc = find_category (category);
1620         if (cdesc) {
1621                 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1622                 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1623                         const CounterDesc *desc = &predef_counters [i];
1624                         mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1625                 }
1626                 return res;
1627         }
1628         perfctr_lock ();
1629         scat = find_custom_category (category);
1630         if (scat) {
1631                 char *p = custom_category_counters (scat);
1632                 int i;
1633                 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1634                 for (i = 0; i < scat->num_counters; ++i) {
1635                         mono_array_setref (res, i, mono_string_new (domain, p + 1));
1636                         p += 2; /* skip counter type */
1637                         p += strlen (p) + 1; /* skip counter name */
1638                         p += strlen (p) + 1; /* skip counter help */
1639                 }
1640                 perfctr_unlock ();
1641                 return res;
1642         }
1643         perfctr_unlock ();
1644         return mono_array_new (domain, mono_get_string_class (), 0);
1645 }
1646
1647 static MonoArray*
1648 get_string_array (void **array, int count, gboolean is_process)
1649 {
1650         int i;
1651         MonoDomain *domain = mono_domain_get ();
1652         MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1653         for (i = 0; i < count; ++i) {
1654                 char buf [128];
1655                 char *p;
1656                 if (is_process) {
1657                         char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1658                         p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1659                 } else {
1660                         sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1661                         p = buf;
1662                 }
1663                 mono_array_setref (res, i, mono_string_new (domain, p));
1664                 if (p != buf)
1665                         g_free (p);
1666         }
1667         return res;
1668 }
1669
1670 static MonoArray*
1671 get_string_array_of_strings (void **array, int count)
1672 {
1673         int i;
1674         MonoDomain *domain = mono_domain_get ();
1675         MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1676         for (i = 0; i < count; ++i) {
1677                 char* p = (char *)array[i];
1678                 mono_array_setref (res, i, mono_string_new (domain, p));
1679         }
1680
1681         return res;
1682 }
1683
1684 static MonoArray*
1685 get_mono_instances (void)
1686 {
1687         int count = 64;
1688         int res;
1689         void **buf = NULL;
1690         MonoArray *array;
1691         do {
1692                 count *= 2;
1693                 g_free (buf);
1694                 buf = g_new (void*, count);
1695                 res = mono_shared_area_instances (buf, count);
1696         } while (res == count);
1697         array = get_string_array (buf, res, TRUE);
1698         g_free (buf);
1699         return array;
1700 }
1701
1702 static MonoArray*
1703 get_cpu_instances (void)
1704 {
1705         void **buf = NULL;
1706         int i, count;
1707         MonoArray *array;
1708
1709         count = mono_cpu_count () + 1; /* +1 for "_Total" */
1710         buf = g_new (void*, count);
1711         for (i = 0; i < count; ++i)
1712                 buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
1713         array = get_string_array (buf, count, FALSE);
1714         g_free (buf);
1715         mono_array_setref (array, 0, mono_string_new (mono_domain_get (), "_Total"));
1716         return array;
1717 }
1718
1719 static MonoArray*
1720 get_processes_instances (void)
1721 {
1722         MonoArray *array;
1723         int count = 0;
1724         void **buf = mono_process_list (&count);
1725         if (!buf)
1726                 return get_string_array (NULL, 0, FALSE);
1727         array = get_string_array (buf, count, TRUE);
1728         g_free (buf);
1729         return array;
1730 }
1731
1732 static MonoArray*
1733 get_networkinterface_instances (void)
1734 {
1735         MonoArray *array;
1736         int count = 0;
1737         void **buf = mono_networkinterface_list (&count);
1738         if (!buf)
1739                 return get_string_array_of_strings (NULL, 0);
1740         array = get_string_array_of_strings (buf, count);
1741         g_strfreev ((char **) buf);
1742         return array;
1743 }
1744
1745 static MonoArray*
1746 get_custom_instances (MonoString *category)
1747 {
1748         SharedCategory *scat;
1749         scat = find_custom_category (category);
1750         if (scat) {
1751                 GSList *list = get_custom_instances_list (scat);
1752                 GSList *tmp;
1753                 int i = 0;
1754                 MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
1755                 for (tmp = list; tmp; tmp = tmp->next) {
1756                         SharedInstance *inst = (SharedInstance *)tmp->data;
1757                         mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1758                         i++;
1759                 }
1760                 g_slist_free (list);
1761                 return array;
1762         }
1763         return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1764 }
1765
1766 MonoArray*
1767 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1768 {
1769         const CategoryDesc* cat;
1770         if (mono_string_compare_ascii (machine, "."))
1771                 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1772         cat = find_category (category);
1773         if (!cat)
1774                 return get_custom_instances (category);
1775         switch (cat->instance_type) {
1776         case MonoInstance:
1777                 return get_mono_instances ();
1778         case CPUInstance:
1779                 return get_cpu_instances ();
1780         case ProcessInstance:
1781                 return get_processes_instances ();
1782         case NetworkInterfaceInstance:
1783                 return get_networkinterface_instances ();
1784         case ThreadInstance:
1785         default:
1786                 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1787         }
1788 }
1789
1790 typedef struct {
1791         PerfCounterEnumCallback cb;
1792         void *data;
1793 } PerfCounterForeachData;
1794
1795 static gboolean
1796 mono_perfcounter_foreach_shared_item (SharedHeader *header, gpointer data)
1797 {
1798         int i;
1799         char *p, *name;
1800         unsigned char type;
1801         void *addr;
1802         SharedCategory *cat;
1803         SharedCounter *counter;
1804         SharedInstance *inst;
1805         PerfCounterForeachData *foreach_data = (PerfCounterForeachData *)data;
1806
1807         if (header->ftype == FTYPE_CATEGORY) {
1808                 cat = (SharedCategory*)header;
1809
1810                 p = cat->name;
1811                 p += strlen (p) + 1; /* skip category name */
1812                 p += strlen (p) + 1; /* skip category help */
1813
1814                 for (i = 0; i < cat->num_counters; ++i) {
1815                         counter = (SharedCounter*) p;
1816                         type = (unsigned char)*p++;
1817                         /* seq_num = (int)* */ p++;
1818                         name = p;
1819                         p += strlen (p) + 1;
1820                         /* help = p; */
1821                         p += strlen (p) + 1;
1822
1823                         inst = custom_get_instance (cat, counter, name);
1824                         if (!inst)
1825                                 return FALSE;
1826                         addr = custom_get_value_address (counter, inst);
1827                         if (!foreach_data->cb (cat->name, name, type, addr ? *(gint64*)addr : 0, foreach_data->data))
1828                                 return FALSE;
1829                 }
1830         }
1831
1832         return TRUE;
1833 }
1834
1835 void
1836 mono_perfcounter_foreach (PerfCounterEnumCallback cb, gpointer data)
1837 {
1838         PerfCounterForeachData foreach_data = { cb, data };
1839
1840         perfctr_lock ();
1841
1842         foreach_shared_item (mono_perfcounter_foreach_shared_item, &foreach_data);
1843
1844         perfctr_unlock ();
1845 }
1846
1847 #else
1848 void*
1849 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance, MonoString* machine, int *type, MonoBoolean *custom)
1850 {
1851         g_assert_not_reached ();
1852 }
1853
1854 MonoBoolean
1855 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1856 {
1857         g_assert_not_reached ();
1858 }
1859
1860 gint64
1861 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1862 {
1863         g_assert_not_reached ();
1864 }
1865
1866 void
1867 mono_perfcounter_free_data (void *impl)
1868 {
1869         g_assert_not_reached ();
1870 }
1871
1872 /* Category icalls */
1873 MonoBoolean
1874 mono_perfcounter_category_del (MonoString *name)
1875 {
1876         g_assert_not_reached ();
1877 }
1878
1879 MonoString*
1880 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1881 {
1882         g_assert_not_reached ();
1883 }
1884
1885 MonoBoolean
1886 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1887 {
1888         g_assert_not_reached ();
1889 }
1890
1891 MonoBoolean
1892 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1893 {
1894         g_assert_not_reached ();
1895 }
1896
1897 int
1898 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1899 {
1900         g_assert_not_reached ();
1901 }
1902
1903 MonoArray*
1904 mono_perfcounter_category_names (MonoString *machine)
1905 {
1906         g_assert_not_reached ();
1907 }
1908
1909 MonoArray*
1910 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1911 {
1912         g_assert_not_reached ();
1913 }
1914
1915 MonoArray*
1916 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1917 {
1918         g_assert_not_reached ();
1919 }
1920 #endif