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