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