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