Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / metadata / mono-perfcounters.c
1 /**
2  * \file
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 "utils/unlocked.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 = InterlockedRead (&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 = InterlockedRead (&mono_perfcounters->aspnet_requests_queued);
1085                         return TRUE;
1086                 case COUNTER_ASPNET_REQ_TOTAL:
1087                         sample->rawValue = InterlockedRead (&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 = InterlockedRead64 (&mono_perfcounters->threadpool_workitems);
1095                         return TRUE;
1096                 case COUNTER_THREADPOOL_IOWORKITEMS:
1097                         sample->rawValue = InterlockedRead64 (&mono_perfcounters->threadpool_ioworkitems);
1098                         return TRUE;
1099                 case COUNTER_THREADPOOL_THREADS:
1100                         sample->rawValue = InterlockedRead (&mono_perfcounters->threadpool_threads);
1101                         return TRUE;
1102                 case COUNTER_THREADPOOL_IOTHREADS:
1103                         sample->rawValue = InterlockedRead (&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 = InterlockedRead (&mono_perfcounters->jit_bytes);
1111                         return TRUE;
1112                 case COUNTER_JIT_METHODS:
1113                         sample->rawValue = InterlockedRead (&mono_perfcounters->jit_methods);
1114                         return TRUE;
1115                 case COUNTER_JIT_TIME:
1116                         sample->rawValue = InterlockedRead (&mono_perfcounters->jit_time);
1117                         return TRUE;
1118                 case COUNTER_JIT_BYTES_PSEC:
1119                         sample->rawValue = InterlockedRead (&mono_perfcounters->jit_bytes);
1120                         return TRUE;
1121                 case COUNTER_JIT_FAILURES:
1122                         sample->rawValue = InterlockedRead (&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         gint32 *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 = &mono_perfcounters->threadpool_workitems; break;
1148                 case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = &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 (ptr);
1158                         if (value == -1)
1159                                 return InterlockedDecrement (ptr);
1160
1161                         return InterlockedAdd(ptr, value);
1162                 }
1163                 /* this can be non-atomic */
1164                 *ptr = value;
1165                 return value;
1166         } else if (ptr64) {
1167                 if (do_incr) {
1168                         if (value == 1)
1169                                 return UnlockedIncrement64 (ptr64); /* FIXME: use InterlockedIncrement64 () */
1170                         if (value == -1)
1171                                 return UnlockedDecrement64 (ptr64); /* FIXME: use InterlockedDecrement64 () */
1172
1173                         return UnlockedAdd64 (ptr64, value); /* FIXME: use InterlockedAdd64 () */
1174                 }
1175                 /* this can be non-atomic */
1176                 *ptr64 = value;
1177                 return value;
1178         }
1179         return 0;
1180 }
1181
1182 static void*
1183 predef_writable_get_impl (int cat, MonoString* counter, const gchar *instance, int *type, MonoBoolean *custom)
1184 {
1185         const CounterDesc *cdesc;
1186         *custom = TRUE;
1187         if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
1188                 *type = cdesc->type;
1189                 if (instance == NULL || strcmp (instance, "") == 0)
1190                         return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
1191                 else
1192                         return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
1193         }
1194         return NULL;
1195 }
1196
1197 static MonoBoolean
1198 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1199 {
1200         CustomVTable *counter_data = (CustomVTable *)vtable;
1201         if (!only_value) {
1202                 fill_sample (sample);
1203                 sample->baseValue = 1;
1204         }
1205         sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
1206         if (!vtable->arg)
1207                 sample->rawValue = 0;
1208         else
1209                 sample->rawValue = *(guint64*)vtable->arg;
1210         return TRUE;
1211 }
1212
1213 static gint64
1214 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1215 {
1216         /* FIXME: check writability */
1217         guint64 *ptr = (guint64 *)vtable->arg;
1218         if (ptr) {
1219                 if (do_incr) {
1220                         /* FIXME: we need to do this atomically */
1221                         *ptr += value;
1222                         return *ptr;
1223                 }
1224                 /* this can be non-atomic */
1225                 *ptr = value;
1226                 return value;
1227         }
1228         return 0;
1229 }
1230
1231 static SharedInstance*
1232 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, char* name)
1233 {
1234         SharedInstance* inst;
1235         char *p;
1236         int size;
1237         inst = find_custom_instance (cat, name);
1238         if (inst)
1239                 return inst;
1240         size = sizeof (SharedInstance) + strlen (name);
1241         size += 7;
1242         size &= ~7;
1243         size += (sizeof (guint64) * cat->num_counters);
1244         perfctr_lock ();
1245         inst = (SharedInstance*) shared_data_reserve_room (size, FTYPE_INSTANCE);
1246         if (!inst) {
1247                 perfctr_unlock ();
1248                 g_free (name);
1249                 return NULL;
1250         }
1251         inst->category_offset = (char*)cat - (char*)shared_area;
1252         cat->num_instances++;
1253         /* now copy the variable data */
1254         p = inst->instance_name;
1255         strcpy (p, name);
1256         p += strlen (name) + 1;
1257         perfctr_unlock ();
1258
1259         return inst;
1260 }
1261
1262 static ImplVtable*
1263 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
1264 {
1265         CustomVTable* vtable;
1266         vtable = g_new0 (CustomVTable, 1);
1267         vtable->vtable.arg = data;
1268         vtable->vtable.sample = custom_writable_counter;
1269         vtable->vtable.update = custom_writable_update;
1270         vtable->instance_desc = inst;
1271         vtable->counter_desc = scounter;
1272
1273         return (ImplVtable*)vtable;
1274 }
1275
1276 static gpointer
1277 custom_get_value_address (SharedCounter *scounter, SharedInstance* sinst)
1278 {
1279         int offset = sizeof (SharedInstance) + strlen (sinst->instance_name);
1280         offset += 7;
1281         offset &= ~7;
1282         offset += scounter->seq_num * sizeof (guint64);
1283         return (char*)sinst + offset;
1284 }
1285
1286 static void*
1287 custom_get_impl (SharedCategory *cat, MonoString *counter, MonoString* instance, int *type, MonoError *error)
1288 {
1289         SharedCounter *scounter;
1290         SharedInstance* inst;
1291         char *name;
1292
1293         error_init (error);
1294         scounter = find_custom_counter (cat, counter);
1295         if (!scounter)
1296                 return NULL;
1297         name = mono_string_to_utf8_checked (counter, error);
1298         return_val_if_nok (error, NULL);
1299         *type = simple_type_to_type [scounter->type];
1300         inst = custom_get_instance (cat, scounter, name);
1301         g_free (name);
1302         if (!inst)
1303                 return NULL;
1304         return custom_vtable (scounter, inst, (char *)custom_get_value_address (scounter, inst));
1305 }
1306
1307 static const CategoryDesc*
1308 find_category (MonoString *category)
1309 {
1310         int i;
1311         for (i = 0; i < NUM_CATEGORIES; ++i) {
1312                 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1313                         return &predef_categories [i];
1314         }
1315         return NULL;
1316 }
1317
1318 void*
1319 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1320                 MonoString* machine, int *type, MonoBoolean *custom)
1321 {
1322         MonoError error;
1323         const CategoryDesc *cdesc;
1324         void *result = NULL;
1325         /* no support for counters on other machines */
1326         if (mono_string_compare_ascii (machine, "."))
1327                 return NULL;
1328         cdesc = find_category (category);
1329         if (!cdesc) {
1330                 SharedCategory *scat = find_custom_category (category);
1331                 if (!scat)
1332                         return NULL;
1333                 *custom = TRUE;
1334                 result = custom_get_impl (scat, counter, instance, type, &error);
1335                 if (mono_error_set_pending_exception (&error))
1336                         return NULL;
1337                 return result;
1338         }
1339         gchar *c_instance = mono_string_to_utf8_checked (instance, &error);
1340         if (mono_error_set_pending_exception (&error))
1341                 return NULL;
1342         switch (cdesc->id) {
1343         case CATEGORY_CPU:
1344                 result = cpu_get_impl (counter, c_instance, type, custom);
1345                 break;
1346         case CATEGORY_PROC:
1347                 result = process_get_impl (counter, c_instance, type, custom);
1348                 break;
1349         case CATEGORY_MONO_MEM:
1350                 result = mono_mem_get_impl (counter, c_instance, type, custom);
1351                 break;
1352         case CATEGORY_NETWORK:
1353                 result = network_get_impl (counter, c_instance, type, custom);
1354                 break;
1355         case CATEGORY_JIT:
1356         case CATEGORY_EXC:
1357         case CATEGORY_GC:
1358         case CATEGORY_REMOTING:
1359         case CATEGORY_LOADING:
1360         case CATEGORY_THREAD:
1361         case CATEGORY_INTEROP:
1362         case CATEGORY_SECURITY:
1363         case CATEGORY_ASPNET:
1364         case CATEGORY_THREADPOOL:
1365                 result = predef_writable_get_impl (cdesc->id, counter, c_instance, type, custom);
1366                 break;
1367         }
1368         g_free (c_instance);
1369         return result;
1370 }
1371
1372 MonoBoolean
1373 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1374 {
1375         ImplVtable *vtable = (ImplVtable *)impl;
1376         if (vtable && vtable->sample)
1377                 return vtable->sample (vtable, only_value, sample);
1378         return FALSE;
1379 }
1380
1381 gint64
1382 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1383 {
1384         ImplVtable *vtable = (ImplVtable *)impl;
1385         if (vtable && vtable->update)
1386                 return vtable->update (vtable, do_incr, value);
1387         return 0;
1388 }
1389
1390 void
1391 mono_perfcounter_free_data (void *impl)
1392 {
1393         ImplVtable *vtable = (ImplVtable *)impl;
1394         if (vtable && vtable->cleanup)
1395                 vtable->cleanup (vtable);
1396         g_free (impl);
1397 }
1398
1399 /* Category icalls */
1400 MonoBoolean
1401 mono_perfcounter_category_del (MonoString *name)
1402 {
1403         const CategoryDesc *cdesc;
1404         SharedCategory *cat;
1405         cdesc = find_category (name);
1406         /* can't delete a predefined category */
1407         if (cdesc)
1408                 return FALSE;
1409         perfctr_lock ();
1410         cat = find_custom_category (name);
1411         /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1412         if (!cat || cat->num_instances) {
1413                 perfctr_unlock ();
1414                 return FALSE;
1415         }
1416         cat->header.ftype = FTYPE_DELETED;
1417         perfctr_unlock ();
1418         return TRUE;
1419 }
1420
1421 /* this is an icall */
1422 MonoString*
1423 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1424 {
1425         MonoError error;
1426         MonoString *result = NULL;
1427         const CategoryDesc *cdesc;
1428         error_init (&error);
1429         /* no support for counters on other machines */
1430         if (mono_string_compare_ascii (machine, "."))
1431                 return NULL;
1432         cdesc = find_category (category);
1433         if (!cdesc) {
1434                 SharedCategory *scat = find_custom_category (category);
1435                 if (!scat)
1436                         return NULL;
1437                 result = mono_string_new_checked (mono_domain_get (), custom_category_help (scat), &error);
1438                 if (mono_error_set_pending_exception (&error))
1439                         return NULL;
1440                 return result;
1441         }
1442         result = mono_string_new_checked (mono_domain_get (), cdesc->help, &error);
1443         if (mono_error_set_pending_exception (&error))
1444                 return NULL;
1445         return result;
1446 }
1447
1448 /*
1449  * Check if the category named @category exists on @machine. If @counter is not NULL, return
1450  * TRUE only if a counter with that name exists in the category.
1451  */
1452 MonoBoolean
1453 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1454 {
1455         const CategoryDesc *cdesc;
1456         /* no support for counters on other machines */
1457         if (mono_string_compare_ascii (machine, "."))
1458                 return FALSE;
1459         cdesc = find_category (category);
1460         if (!cdesc) {
1461                 SharedCategory *scat = find_custom_category (category);
1462                 if (!scat)
1463                         return FALSE;
1464                 /* counter is allowed to be null */
1465                 if (!counter)
1466                         return TRUE;
1467                 /* search through the custom category */
1468                 return find_custom_counter (scat, counter) != NULL;
1469         }
1470         /* counter is allowed to be null */
1471         if (!counter)
1472                 return TRUE;
1473         if (get_counter_in_category (cdesc, counter))
1474                 return TRUE;
1475         return FALSE;
1476 }
1477
1478 /* C map of the type with the same name */
1479 typedef struct {
1480         MonoObject object;
1481         MonoString *help;
1482         MonoString *name;
1483         int type;
1484 } CounterCreationData;
1485
1486 /*
1487  * Since we'll keep a copy of the category per-process, we should also make sure
1488  * categories with the same name are compatible.
1489  */
1490 MonoBoolean
1491 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1492 {
1493         MonoError error;
1494         int result = FALSE;
1495         int i, size;
1496         int num_counters = mono_array_length (items);
1497         int counters_data_size;
1498         char *name = NULL;
1499         char *chelp = NULL;
1500         char **counter_info = NULL;
1501         char *p;
1502         SharedCategory *cat;
1503
1504         /* FIXME: ensure there isn't a category created already */
1505         name = mono_string_to_utf8_checked (category, &error);
1506         if (!mono_error_ok (&error))
1507                 goto failure;
1508         chelp = mono_string_to_utf8_checked (help, &error);
1509         if (!mono_error_ok (&error))
1510                 goto failure;
1511         counter_info = g_new0 (char*, num_counters * 2);
1512         /* calculate the size we need structure size + name/help + 2 0 string terminators */
1513         size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1514         for (i = 0; i < num_counters; ++i) {
1515                 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1516                 counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error);
1517                 if (!mono_error_ok (&error))
1518                         goto failure;
1519                 counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error);
1520                 if (!mono_error_ok (&error))
1521                         goto failure;
1522                 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1523         }
1524         for (i = 0; i < num_counters * 2; ++i) {
1525                 if (!counter_info [i])
1526                         goto failure;
1527                 size += strlen (counter_info [i]) + 1;
1528         }
1529         size += 7;
1530         size &= ~7;
1531         counters_data_size = num_counters * 8; /* optimize for size later */
1532         if (size > 65535)
1533                 goto failure;
1534         perfctr_lock ();
1535         cat = (SharedCategory*) shared_data_reserve_room (size, FTYPE_CATEGORY);
1536         if (!cat) {
1537                 perfctr_unlock ();
1538                 goto failure;
1539         }
1540         cat->num_counters = num_counters;
1541         cat->counters_data_size = counters_data_size;
1542         /* now copy the vaiable data */
1543         p = cat->name;
1544         strcpy (p, name);
1545         p += strlen (name) + 1;
1546         strcpy (p, chelp);
1547         p += strlen (chelp) + 1;
1548         for (i = 0; i < num_counters; ++i) {
1549                 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1550                 /* emit the SharedCounter structures */
1551                 *p++ = perfctr_type_compress (data->type);
1552                 *p++ = i;
1553                 strcpy (p, counter_info [i * 2]);
1554                 p += strlen (counter_info [i * 2]) + 1;
1555                 strcpy (p, counter_info [i * 2 + 1]);
1556                 p += strlen (counter_info [i * 2 + 1]) + 1;
1557         }
1558
1559         perfctr_unlock ();
1560         result = TRUE;
1561 failure:
1562         if (counter_info) {
1563                 for (i = 0; i < num_counters * 2; ++i) {
1564                         g_free (counter_info [i]);
1565                 }
1566                 g_free (counter_info);
1567         }
1568         g_free (name);
1569         g_free (chelp);
1570         mono_error_cleanup (&error);
1571         return result;
1572 }
1573
1574 int
1575 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1576 {
1577         MonoError error;
1578         const CategoryDesc *cdesc;
1579         SharedInstance *sinst;
1580         char *name;
1581         /* no support for counters on other machines */
1582         /*FIXME: machine appears to be wrong
1583         if (mono_string_compare_ascii (machine, "."))
1584                 return FALSE;*/
1585         cdesc = find_category (category);
1586         if (!cdesc) {
1587                 SharedCategory *scat;
1588                 scat = find_custom_category (category);
1589                 if (!scat)
1590                         return FALSE;
1591                 name = mono_string_to_utf8_checked (instance, &error);
1592                 if (mono_error_set_pending_exception (&error))
1593                         return FALSE;
1594                 sinst = find_custom_instance (scat, name);
1595                 g_free (name);
1596                 if (sinst)
1597                         return TRUE;
1598         } else {
1599                 /* FIXME: search instance */
1600         }
1601         return FALSE;
1602 }
1603
1604 /* this is an icall */
1605 MonoArray*
1606 mono_perfcounter_category_names (MonoString *machine)
1607 {
1608         MonoError error;
1609         int i;
1610         MonoArray *res;
1611         MonoDomain *domain = mono_domain_get ();
1612         GSList *custom_categories, *tmp;
1613         /* no support for counters on other machines */
1614         if (mono_string_compare_ascii (machine, ".")) {
1615                 res = mono_array_new_checked (domain, mono_get_string_class (), 0, &error);
1616                 mono_error_set_pending_exception (&error);
1617                 return res;
1618         }
1619         perfctr_lock ();
1620         custom_categories = get_custom_categories ();
1621         res = mono_array_new_checked (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories), &error);
1622         if (mono_error_set_pending_exception (&error)) {
1623                 perfctr_unlock ();
1624                 return NULL;
1625         }
1626
1627         for (i = 0; i < NUM_CATEGORIES; ++i) {
1628                 const CategoryDesc *cdesc = &predef_categories [i];
1629                 MonoString *name = mono_string_new_checked (domain, cdesc->name, &error);
1630                 if (!is_ok (&error))
1631                         goto leave;
1632                 mono_array_setref (res, i, name);
1633         }
1634         for (tmp = custom_categories; tmp; tmp = tmp->next) {
1635                 SharedCategory *scat = (SharedCategory *)tmp->data;
1636                 MonoString *name = mono_string_new_checked (domain, scat->name, &error);
1637                 if (!is_ok (&error))
1638                         goto leave;
1639                 mono_array_setref (res, i, name);
1640                 i++;
1641         }
1642 leave:
1643         perfctr_unlock ();
1644         g_slist_free (custom_categories);
1645         mono_error_set_pending_exception (&error);
1646         return res;
1647 }
1648
1649 MonoArray*
1650 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1651 {
1652         MonoError error;
1653         int i;
1654         SharedCategory *scat;
1655         const CategoryDesc *cdesc;
1656         MonoArray *res;
1657         MonoDomain *domain = mono_domain_get ();
1658         /* no support for counters on other machines */
1659         if (mono_string_compare_ascii (machine, ".")) {
1660                 res =  mono_array_new_checked (domain, mono_get_string_class (), 0, &error);
1661                 mono_error_set_pending_exception (&error);
1662                 return res;
1663         }
1664         cdesc = find_category (category);
1665         if (cdesc) {
1666                 res = mono_array_new_checked (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter, &error);
1667                 if (mono_error_set_pending_exception (&error))
1668                         return NULL;
1669                 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1670                         const CounterDesc *desc = &predef_counters [i];
1671                         MonoString *name = mono_string_new_checked (domain, desc->name, &error);
1672                         if (mono_error_set_pending_exception (&error))
1673                                 return NULL;
1674                         mono_array_setref (res, i - cdesc->first_counter, name);
1675                 }
1676                 return res;
1677         }
1678         perfctr_lock ();
1679         scat = find_custom_category (category);
1680         if (scat) {
1681                 char *p = custom_category_counters (scat);
1682                 int i;
1683                 res = mono_array_new_checked (domain, mono_get_string_class (), scat->num_counters, &error);
1684                 if (mono_error_set_pending_exception (&error)) {
1685                         perfctr_unlock ();
1686                         return NULL;
1687                 }
1688
1689                 for (i = 0; i < scat->num_counters; ++i) {
1690                         MonoString *str = mono_string_new_checked (domain, p + 1, &error);
1691                         if (!is_ok (&error))
1692                                 goto leave;
1693                         mono_array_setref (res, i, str);
1694                         p += 2; /* skip counter type */
1695                         p += strlen (p) + 1; /* skip counter name */
1696                         p += strlen (p) + 1; /* skip counter help */
1697                 }
1698         } else
1699                 res = mono_array_new_checked (domain, mono_get_string_class (), 0, &error);
1700 leave:
1701         perfctr_unlock ();
1702         mono_error_set_pending_exception (&error);
1703         return res;
1704 }
1705
1706 static MonoArray*
1707 get_string_array (void **array, int count, gboolean is_process, MonoError *error)
1708 {
1709         int i;
1710         MonoDomain *domain = mono_domain_get ();
1711         error_init (error);
1712         MonoArray * res = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), count, error);
1713         return_val_if_nok (error, NULL);
1714         for (i = 0; i < count; ++i) {
1715                 char buf [128];
1716                 char *p;
1717                 if (is_process) {
1718                         char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1719                         p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1720                 } else {
1721                         sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1722                         p = buf;
1723                 }
1724                 MonoString *str = mono_string_new_checked (domain, p, error);
1725                 if (p != buf)
1726                         g_free (p);
1727                 return_val_if_nok (error, NULL);
1728                 mono_array_setref (res, i, str);
1729         }
1730         return res;
1731 }
1732
1733 static MonoArray*
1734 get_string_array_of_strings (void **array, int count, MonoError *error)
1735 {
1736         int i;
1737         MonoDomain *domain = mono_domain_get ();
1738         error_init (error);
1739         MonoArray * res = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), count, error);
1740         return_val_if_nok (error, NULL);
1741         for (i = 0; i < count; ++i) {
1742                 char* p = (char *)array[i];
1743                 MonoString *str = mono_string_new_checked (domain, p, error);
1744                 return_val_if_nok (error, NULL);
1745                 mono_array_setref (res, i, str);
1746         }
1747
1748         return res;
1749 }
1750
1751 static MonoArray*
1752 get_mono_instances (MonoError *error)
1753 {
1754         int count = 64;
1755         int res;
1756         void **buf = NULL;
1757         MonoArray *array;
1758         error_init (error);
1759         do {
1760                 count *= 2;
1761                 g_free (buf);
1762                 buf = g_new (void*, count);
1763                 res = mono_shared_area_instances (buf, count);
1764         } while (res == count);
1765         array = get_string_array (buf, res, TRUE, error);
1766         g_free (buf);
1767         return array;
1768 }
1769
1770 static MonoArray*
1771 get_cpu_instances (MonoError *error)
1772 {
1773         void **buf = NULL;
1774         int i, count;
1775         MonoArray *array;
1776         error_init (error);
1777         count = mono_cpu_count () + 1; /* +1 for "_Total" */
1778         buf = g_new (void*, count);
1779         for (i = 0; i < count; ++i)
1780                 buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
1781         array = get_string_array (buf, count, FALSE, error);
1782         g_free (buf);
1783         MonoString *total = mono_string_new_checked (mono_domain_get (), "_Total", error);
1784         return_val_if_nok (error, NULL);
1785         mono_array_setref (array, 0, total);
1786         return array;
1787 }
1788
1789 static MonoArray*
1790 get_processes_instances (MonoError *error)
1791 {
1792         MonoArray *array;
1793         int count = 0;
1794         void **buf = mono_process_list (&count);
1795         error_init (error);
1796         if (!buf)
1797                 return get_string_array (NULL, 0, FALSE, error);
1798         array = get_string_array (buf, count, TRUE, error);
1799         g_free (buf);
1800         return array;
1801 }
1802
1803 static MonoArray*
1804 get_networkinterface_instances (MonoError *error)
1805 {
1806         MonoArray *array;
1807         int count = 0;
1808         error_init (error);
1809         void **buf = mono_networkinterface_list (&count);
1810         if (!buf)
1811                 return get_string_array_of_strings (NULL, 0, error);
1812         array = get_string_array_of_strings (buf, count, error);
1813         g_strfreev ((char **) buf);
1814         return array;
1815 }
1816
1817 static MonoArray*
1818 get_custom_instances (MonoString *category, MonoError *error)
1819 {
1820         SharedCategory *scat;
1821         error_init (error);
1822         scat = find_custom_category (category);
1823         if (scat) {
1824                 GSList *list = get_custom_instances_list (scat);
1825                 GSList *tmp;
1826                 int i = 0;
1827                 MonoArray *array = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), g_slist_length (list), error);
1828                 if (!is_ok (error)) {
1829                         g_slist_free (list);
1830                         return NULL;
1831                 }
1832                 for (tmp = list; tmp; tmp = tmp->next) {
1833                         SharedInstance *inst = (SharedInstance *)tmp->data;
1834                         MonoString *str = mono_string_new_checked (mono_domain_get (), inst->instance_name, error);
1835                         if (!is_ok (error)) {
1836                                 g_slist_free (list);
1837                                 return NULL;
1838                         }
1839                         mono_array_setref (array, i, str);
1840                         i++;
1841                 }
1842                 g_slist_free (list);
1843                 return array;
1844         }
1845         return mono_array_new_checked (mono_domain_get (), mono_get_string_class (), 0, error);
1846 }
1847
1848 MonoArray*
1849 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1850 {
1851         MonoError error;
1852         const CategoryDesc* cat;
1853         MonoArray *result = NULL;
1854         if (mono_string_compare_ascii (machine, ".")) {
1855                 result = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), 0, &error);
1856                 mono_error_set_pending_exception (&error);
1857                 return result;
1858         }
1859         
1860         cat = find_category (category);
1861         if (!cat) {
1862                 MonoArray *result = get_custom_instances (category, &error);
1863                 mono_error_set_pending_exception (&error);
1864                 return result;
1865         }
1866         switch (cat->instance_type) {
1867         case MonoInstance:
1868                 result = get_mono_instances (&error);
1869                 break;
1870         case CPUInstance:
1871                 result = get_cpu_instances (&error);
1872                 break;
1873         case ProcessInstance:
1874                 result = get_processes_instances (&error);
1875                 break;
1876         case NetworkInterfaceInstance:
1877                 result = get_networkinterface_instances (&error);
1878                 break;
1879         case ThreadInstance:
1880         default:
1881                 result = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), 0, &error);
1882         }
1883         mono_error_set_pending_exception (&error);
1884         return result;
1885 }
1886
1887 typedef struct {
1888         PerfCounterEnumCallback cb;
1889         void *data;
1890 } PerfCounterForeachData;
1891
1892 static gboolean
1893 mono_perfcounter_foreach_shared_item (SharedHeader *header, gpointer data)
1894 {
1895         int i;
1896         char *p, *name;
1897         unsigned char type;
1898         void *addr;
1899         SharedCategory *cat;
1900         SharedCounter *counter;
1901         SharedInstance *inst;
1902         PerfCounterForeachData *foreach_data = (PerfCounterForeachData *)data;
1903
1904         if (header->ftype == FTYPE_CATEGORY) {
1905                 cat = (SharedCategory*)header;
1906
1907                 p = cat->name;
1908                 p += strlen (p) + 1; /* skip category name */
1909                 p += strlen (p) + 1; /* skip category help */
1910
1911                 for (i = 0; i < cat->num_counters; ++i) {
1912                         counter = (SharedCounter*) p;
1913                         type = (unsigned char)*p++;
1914                         /* seq_num = (int)* */ p++;
1915                         name = p;
1916                         p += strlen (p) + 1;
1917                         /* help = p; */
1918                         p += strlen (p) + 1;
1919
1920                         inst = custom_get_instance (cat, counter, name);
1921                         if (!inst)
1922                                 return FALSE;
1923                         addr = custom_get_value_address (counter, inst);
1924                         if (!foreach_data->cb (cat->name, name, type, addr ? *(gint64*)addr : 0, foreach_data->data))
1925                                 return FALSE;
1926                 }
1927         }
1928
1929         return TRUE;
1930 }
1931
1932 void
1933 mono_perfcounter_foreach (PerfCounterEnumCallback cb, gpointer data)
1934 {
1935         PerfCounterForeachData foreach_data = { cb, data };
1936
1937         perfctr_lock ();
1938
1939         foreach_shared_item (mono_perfcounter_foreach_shared_item, &foreach_data);
1940
1941         perfctr_unlock ();
1942 }
1943
1944 #else
1945 void*
1946 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance, MonoString* machine, int *type, MonoBoolean *custom)
1947 {
1948         g_assert_not_reached ();
1949 }
1950
1951 MonoBoolean
1952 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1953 {
1954         g_assert_not_reached ();
1955 }
1956
1957 gint64
1958 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1959 {
1960         g_assert_not_reached ();
1961 }
1962
1963 void
1964 mono_perfcounter_free_data (void *impl)
1965 {
1966         g_assert_not_reached ();
1967 }
1968
1969 /* Category icalls */
1970 MonoBoolean
1971 mono_perfcounter_category_del (MonoString *name)
1972 {
1973         g_assert_not_reached ();
1974 }
1975
1976 MonoString*
1977 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1978 {
1979         g_assert_not_reached ();
1980 }
1981
1982 MonoBoolean
1983 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1984 {
1985         g_assert_not_reached ();
1986 }
1987
1988 MonoBoolean
1989 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1990 {
1991         g_assert_not_reached ();
1992 }
1993
1994 int
1995 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1996 {
1997         g_assert_not_reached ();
1998 }
1999
2000 MonoArray*
2001 mono_perfcounter_category_names (MonoString *machine)
2002 {
2003         g_assert_not_reached ();
2004 }
2005
2006 MonoArray*
2007 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
2008 {
2009         g_assert_not_reached ();
2010 }
2011
2012 MonoArray*
2013 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
2014 {
2015         g_assert_not_reached ();
2016 }
2017 #endif