Merge pull request #2890 from mono/alexischr/remove-managed-alloc-string-zeroing
[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         int id = -1;
808         if (mono_string_length (instance)) {
809                 char *id_str = mono_string_to_utf8 (instance);
810                 char *end;
811                 id = strtol (id_str, &end, 0);
812                 if (end == id_str && !is_process)
813                         id = -1;
814                 g_free (id_str);
815         }
816         return id;
817 }
818
819 static MonoBoolean
820 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
821 {
822         MonoProcessError error;
823         int id = GPOINTER_TO_INT (vtable->arg);
824         int pid = id >> 5;
825         id &= 0x1f;
826         if (!only_value) {
827                 fill_sample (sample);
828                 sample->baseValue = 1;
829         }
830         sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
831         switch (id) {
832         case COUNTER_CPU_USER_TIME:
833                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
834                 return TRUE;
835         case COUNTER_CPU_PRIV_TIME:
836                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
837                 return TRUE;
838         case COUNTER_CPU_INTR_TIME:
839                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
840                 return TRUE;
841         case COUNTER_CPU_DCP_TIME:
842                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
843                 return TRUE;
844         case COUNTER_CPU_PROC_TIME:
845                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
846                 return TRUE;
847         }
848         return FALSE;
849 }
850
851 static void*
852 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
853 {
854         int id = id_from_string (instance, FALSE) << 5;
855         const CounterDesc *cdesc;
856         *custom = FALSE;
857         /* increase the shift above and the mask also in the implementation functions */
858         //g_assert (32 > desc [1].first_counter - desc->first_counter);
859         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
860                 *type = cdesc->type;
861                 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
862         }
863         return NULL;
864 }
865
866 static MonoBoolean
867 get_network_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
868 {
869         MonoNetworkError error = MONO_NETWORK_ERROR_OTHER;
870         NetworkVtableArg *narg = (NetworkVtableArg*) vtable->arg;
871         if (!only_value) {
872                 fill_sample (sample);
873         }
874
875         sample->counterType = predef_counters [predef_categories [CATEGORY_NETWORK].first_counter + narg->id].type;
876         switch (narg->id) {
877         case COUNTER_NETWORK_BYTESRECSEC:
878                 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESREC, &error);
879                 break;
880         case COUNTER_NETWORK_BYTESSENTSEC:
881                 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESSENT, &error);
882                 break;
883         case COUNTER_NETWORK_BYTESTOTALSEC:
884                 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESTOTAL, &error);
885                 break;
886         }
887
888         if (error == MONO_NETWORK_ERROR_NONE)
889                 return TRUE;
890         else
891                 return FALSE;
892 }
893
894 static void
895 network_cleanup (ImplVtable *vtable)
896 {
897         NetworkVtableArg *narg;
898
899         if (vtable == NULL)
900                 return;
901
902         narg = (NetworkVtableArg *)vtable->arg;
903         if (narg == NULL)
904                 return;
905
906         g_free (narg->name);
907         narg->name = NULL;
908         g_free (narg);
909         vtable->arg = NULL;
910 }
911
912 static void*
913 network_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
914 {
915         const CounterDesc *cdesc;
916         NetworkVtableArg *narg;
917         ImplVtable *vtable;
918         char *instance_name;
919
920         *custom = FALSE;
921         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_NETWORK], counter))) {
922                 instance_name = mono_string_to_utf8 (instance);
923                 narg = g_new0 (NetworkVtableArg, 1);
924                 narg->id = cdesc->id;
925                 narg->name = instance_name;
926                 *type = cdesc->type;
927                 vtable = create_vtable (narg, get_network_counter, NULL);
928                 vtable->cleanup = network_cleanup;
929                 return vtable;
930         }
931         return NULL;
932 }
933
934 static MonoBoolean
935 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
936 {
937         int id = GPOINTER_TO_INT (vtable->arg);
938         int pid = id >> 5;
939         if (pid < 0)
940                 return FALSE;
941         id &= 0x1f;
942         if (!only_value) {
943                 fill_sample (sample);
944                 sample->baseValue = 1;
945         }
946         sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
947         switch (id) {
948         case COUNTER_PROC_USER_TIME:
949                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
950                 return TRUE;
951         case COUNTER_PROC_PRIV_TIME:
952                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
953                 return TRUE;
954         case COUNTER_PROC_PROC_TIME:
955                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
956                 return TRUE;
957         case COUNTER_PROC_THREADS:
958                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
959                 return TRUE;
960         case COUNTER_PROC_VBYTES:
961                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
962                 return TRUE;
963         case COUNTER_PROC_WSET:
964                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
965                 return TRUE;
966         case COUNTER_PROC_PBYTES:
967                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
968                 return TRUE;
969         }
970         return FALSE;
971 }
972
973 static void*
974 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
975 {
976         int id = id_from_string (instance, TRUE) << 5;
977         const CounterDesc *cdesc;
978         *custom = FALSE;
979         /* increase the shift above and the mask also in the implementation functions */
980         //g_assert (32 > desc [1].first_counter - desc->first_counter);
981         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
982                 *type = cdesc->type;
983                 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
984         }
985         return NULL;
986 }
987
988 static MonoBoolean
989 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
990 {
991         int id = GPOINTER_TO_INT (vtable->arg);
992         if (!only_value) {
993                 fill_sample (sample);
994                 sample->baseValue = 1;
995         }
996         sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
997         switch (id) {
998         case COUNTER_MEM_NUM_OBJECTS:
999                 sample->rawValue = 0;
1000                 return TRUE;
1001         case COUNTER_MEM_PHYS_TOTAL:
1002                 sample->rawValue = mono_determine_physical_ram_size ();;
1003                 return TRUE;
1004         case COUNTER_MEM_PHYS_AVAILABLE:
1005                 sample->rawValue = mono_determine_physical_ram_available_size ();;
1006                 return TRUE;
1007         }
1008         return FALSE;
1009 }
1010
1011 static void*
1012 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
1013 {
1014         const CounterDesc *cdesc;
1015         *custom = FALSE;
1016         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
1017                 *type = cdesc->type;
1018                 return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
1019         }
1020         return NULL;
1021 }
1022
1023 static MonoBoolean
1024 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1025 {
1026         PredefVtable *vt = (PredefVtable *)vtable;
1027         const CounterDesc *desc;
1028         int cat_id = GPOINTER_TO_INT (vtable->arg);
1029         int id = cat_id >> 16;
1030         cat_id &= 0xffff;
1031         if (!only_value) {
1032                 fill_sample (sample);
1033                 sample->baseValue = 1;
1034         }
1035         desc = &predef_counters [predef_categories [cat_id].first_counter + id];
1036         sample->counterType = desc->type;
1037         /* FIXME: check that the offset fits inside imported counters */
1038         /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
1039         sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
1040         return TRUE;
1041 }
1042
1043 static ImplVtable*
1044 predef_vtable (void *arg, MonoString *instance)
1045 {
1046         MonoSharedArea *area;
1047         PredefVtable *vtable;
1048         char *pids = mono_string_to_utf8 (instance);
1049         int pid;
1050
1051         pid = atoi (pids);
1052         g_free (pids);
1053         area = load_sarea_for_pid (pid);
1054         if (!area)
1055                 return NULL;
1056
1057         vtable = g_new (PredefVtable, 1);
1058         vtable->vtable.arg = arg;
1059         vtable->vtable.sample = predef_readonly_counter;
1060         vtable->vtable.cleanup = predef_cleanup;
1061         vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
1062         vtable->pid = pid;
1063
1064         return (ImplVtable*)vtable;
1065 }
1066
1067 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
1068  * this needs some way to set sample->counterType as well, though.
1069  */
1070 static MonoBoolean
1071 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1072 {
1073         int cat_id = GPOINTER_TO_INT (vtable->arg);
1074         int id = cat_id >> 16;
1075         cat_id &= 0xffff;
1076         if (!only_value) {
1077                 fill_sample (sample);
1078                 sample->baseValue = 1;
1079         }
1080         sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
1081         switch (cat_id) {
1082         case CATEGORY_EXC:
1083                 switch (id) {
1084                 case COUNTER_EXC_THROWN:
1085                         sample->rawValue = mono_perfcounters->exceptions_thrown;
1086                         return TRUE;
1087                 }
1088                 break;
1089         case CATEGORY_ASPNET:
1090                 switch (id) {
1091                 case COUNTER_ASPNET_REQ_Q:
1092                         sample->rawValue = mono_perfcounters->aspnet_requests_queued;
1093                         return TRUE;
1094                 case COUNTER_ASPNET_REQ_TOTAL:
1095                         sample->rawValue = mono_perfcounters->aspnet_requests;
1096                         return TRUE;
1097                 }
1098                 break;
1099         case CATEGORY_THREADPOOL:
1100                 switch (id) {
1101                 case COUNTER_THREADPOOL_WORKITEMS:
1102                         sample->rawValue = mono_perfcounters->threadpool_workitems;
1103                         return TRUE;
1104                 case COUNTER_THREADPOOL_IOWORKITEMS:
1105                         sample->rawValue = mono_perfcounters->threadpool_ioworkitems;
1106                         return TRUE;
1107                 case COUNTER_THREADPOOL_THREADS:
1108                         sample->rawValue = mono_perfcounters->threadpool_threads;
1109                         return TRUE;
1110                 case COUNTER_THREADPOOL_IOTHREADS:
1111                         sample->rawValue = mono_perfcounters->threadpool_iothreads;
1112                         return TRUE;
1113                 }
1114                 break;
1115         case CATEGORY_JIT:
1116                 switch (id) {
1117                 case COUNTER_JIT_BYTES:
1118                         sample->rawValue = mono_perfcounters->jit_bytes;
1119                         return TRUE;
1120                 case COUNTER_JIT_METHODS:
1121                         sample->rawValue = mono_perfcounters->jit_methods;
1122                         return TRUE;
1123                 case COUNTER_JIT_TIME:
1124                         sample->rawValue = mono_perfcounters->jit_time;
1125                         return TRUE;
1126                 case COUNTER_JIT_BYTES_PSEC:
1127                         sample->rawValue = mono_perfcounters->jit_bytes;
1128                         return TRUE;
1129                 case COUNTER_JIT_FAILURES:
1130                         sample->rawValue = mono_perfcounters->jit_failures;
1131                         return TRUE;
1132                 }
1133                 break;
1134         }
1135         return FALSE;
1136 }
1137
1138 static gint64
1139 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1140 {
1141         guint32 *volatile ptr = NULL;
1142         gint64 *volatile ptr64 = NULL;
1143         int cat_id = GPOINTER_TO_INT (vtable->arg);
1144         int id = cat_id >> 16;
1145         cat_id &= 0xffff;
1146         switch (cat_id) {
1147         case CATEGORY_ASPNET:
1148                 switch (id) {
1149                 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
1150                 case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
1151                 }
1152                 break;
1153         case CATEGORY_THREADPOOL:
1154                 switch (id) {
1155                 case COUNTER_THREADPOOL_WORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_workitems; break;
1156                 case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_ioworkitems; break;
1157                 case COUNTER_THREADPOOL_THREADS: ptr = &mono_perfcounters->threadpool_threads; break;
1158                 case COUNTER_THREADPOOL_IOTHREADS: ptr = &mono_perfcounters->threadpool_iothreads; break;
1159                 }
1160                 break;
1161         }
1162         if (ptr) {
1163                 if (do_incr) {
1164                         if (value == 1)
1165                                 return InterlockedIncrement ((gint32 *) ptr); /* FIXME: sign */
1166                         if (value == -1)
1167                                 return InterlockedDecrement ((gint32 *) ptr); /* FIXME: sign */
1168
1169                         *ptr += value;
1170                         return *ptr;
1171                 }
1172                 /* this can be non-atomic */
1173                 *ptr = value;
1174                 return value;
1175         } else if (ptr64) {
1176                 if (do_incr) {
1177                         /* FIXME: we need to do this atomically */
1178                         /* No InterlockedIncrement64() yet */
1179                         /*
1180                         if (value == 1)
1181                                 return InterlockedIncrement64 (ptr);
1182                         if (value == -1)
1183                                 return InterlockedDecrement64 (ptr);
1184                         */
1185
1186                         *ptr64 += value;
1187                         return *ptr64;
1188                 }
1189                 /* this can be non-atomic */
1190                 *ptr64 = value;
1191                 return value;
1192         }
1193         return 0;
1194 }
1195
1196 static void*
1197 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
1198 {
1199         const CounterDesc *cdesc;
1200         *custom = TRUE;
1201         if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
1202                 *type = cdesc->type;
1203                 if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
1204                         return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
1205                 else
1206                         return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
1207         }
1208         return NULL;
1209 }
1210
1211 static MonoBoolean
1212 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1213 {
1214         CustomVTable *counter_data = (CustomVTable *)vtable;
1215         if (!only_value) {
1216                 fill_sample (sample);
1217                 sample->baseValue = 1;
1218         }
1219         sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
1220         if (!vtable->arg)
1221                 sample->rawValue = 0;
1222         else
1223                 sample->rawValue = *(guint64*)vtable->arg;
1224         return TRUE;
1225 }
1226
1227 static gint64
1228 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1229 {
1230         /* FIXME: check writability */
1231         guint64 *ptr = (guint64 *)vtable->arg;
1232         if (ptr) {
1233                 if (do_incr) {
1234                         /* FIXME: we need to do this atomically */
1235                         *ptr += value;
1236                         return *ptr;
1237                 }
1238                 /* this can be non-atomic */
1239                 *ptr = value;
1240                 return value;
1241         }
1242         return 0;
1243 }
1244
1245 static SharedInstance*
1246 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, char* name)
1247 {
1248         SharedInstance* inst;
1249         char *p;
1250         int size;
1251         inst = find_custom_instance (cat, name);
1252         if (inst)
1253                 return inst;
1254         size = sizeof (SharedInstance) + strlen (name);
1255         size += 7;
1256         size &= ~7;
1257         size += (sizeof (guint64) * cat->num_counters);
1258         perfctr_lock ();
1259         inst = (SharedInstance*) shared_data_reserve_room (size, FTYPE_INSTANCE);
1260         if (!inst) {
1261                 perfctr_unlock ();
1262                 g_free (name);
1263                 return NULL;
1264         }
1265         inst->category_offset = (char*)cat - (char*)shared_area;
1266         cat->num_instances++;
1267         /* now copy the variable data */
1268         p = inst->instance_name;
1269         strcpy (p, name);
1270         p += strlen (name) + 1;
1271         perfctr_unlock ();
1272
1273         return inst;
1274 }
1275
1276 static ImplVtable*
1277 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
1278 {
1279         CustomVTable* vtable;
1280         vtable = g_new0 (CustomVTable, 1);
1281         vtable->vtable.arg = data;
1282         vtable->vtable.sample = custom_writable_counter;
1283         vtable->vtable.update = custom_writable_update;
1284         vtable->instance_desc = inst;
1285         vtable->counter_desc = scounter;
1286
1287         return (ImplVtable*)vtable;
1288 }
1289
1290 static gpointer
1291 custom_get_value_address (SharedCounter *scounter, SharedInstance* sinst)
1292 {
1293         int offset = sizeof (SharedInstance) + strlen (sinst->instance_name);
1294         offset += 7;
1295         offset &= ~7;
1296         offset += scounter->seq_num * sizeof (guint64);
1297         return (char*)sinst + offset;
1298 }
1299
1300 static void*
1301 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
1302 {
1303         SharedCounter *scounter;
1304         SharedInstance* inst;
1305         char *name;
1306
1307         scounter = find_custom_counter (cat, counter);
1308         if (!scounter)
1309                 return NULL;
1310         *type = simple_type_to_type [scounter->type];
1311         name = mono_string_to_utf8 (counter);
1312         inst = custom_get_instance (cat, scounter, name);
1313         g_free (name);
1314         if (!inst)
1315                 return NULL;
1316         return custom_vtable (scounter, inst, (char *)custom_get_value_address (scounter, inst));
1317 }
1318
1319 static const CategoryDesc*
1320 find_category (MonoString *category)
1321 {
1322         int i;
1323         for (i = 0; i < NUM_CATEGORIES; ++i) {
1324                 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1325                         return &predef_categories [i];
1326         }
1327         return NULL;
1328 }
1329
1330 void*
1331 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1332                 MonoString* machine, int *type, MonoBoolean *custom)
1333 {
1334         const CategoryDesc *cdesc;
1335         /* no support for counters on other machines */
1336         if (mono_string_compare_ascii (machine, "."))
1337                 return NULL;
1338         cdesc = find_category (category);
1339         if (!cdesc) {
1340                 SharedCategory *scat = find_custom_category (category);
1341                 if (!scat)
1342                         return NULL;
1343                 *custom = TRUE;
1344                 return custom_get_impl (scat, counter, instance, type);
1345         }
1346         switch (cdesc->id) {
1347         case CATEGORY_CPU:
1348                 return cpu_get_impl (counter, instance, type, custom);
1349         case CATEGORY_PROC:
1350                 return process_get_impl (counter, instance, type, custom);
1351         case CATEGORY_MONO_MEM:
1352                 return mono_mem_get_impl (counter, instance, type, custom);
1353         case CATEGORY_NETWORK:
1354                 return network_get_impl (counter, instance, type, custom);
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                 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
1366         }
1367         return NULL;
1368 }
1369
1370 MonoBoolean
1371 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1372 {
1373         ImplVtable *vtable = (ImplVtable *)impl;
1374         if (vtable && vtable->sample)
1375                 return vtable->sample (vtable, only_value, sample);
1376         return FALSE;
1377 }
1378
1379 gint64
1380 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1381 {
1382         ImplVtable *vtable = (ImplVtable *)impl;
1383         if (vtable && vtable->update)
1384                 return vtable->update (vtable, do_incr, value);
1385         return 0;
1386 }
1387
1388 void
1389 mono_perfcounter_free_data (void *impl)
1390 {
1391         ImplVtable *vtable = (ImplVtable *)impl;
1392         if (vtable && vtable->cleanup)
1393                 vtable->cleanup (vtable);
1394         g_free (impl);
1395 }
1396
1397 /* Category icalls */
1398 MonoBoolean
1399 mono_perfcounter_category_del (MonoString *name)
1400 {
1401         const CategoryDesc *cdesc;
1402         SharedCategory *cat;
1403         cdesc = find_category (name);
1404         /* can't delete a predefined category */
1405         if (cdesc)
1406                 return FALSE;
1407         perfctr_lock ();
1408         cat = find_custom_category (name);
1409         /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1410         if (!cat || cat->num_instances) {
1411                 perfctr_unlock ();
1412                 return FALSE;
1413         }
1414         cat->header.ftype = FTYPE_DELETED;
1415         perfctr_unlock ();
1416         return TRUE;
1417 }
1418
1419 MonoString*
1420 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1421 {
1422         const CategoryDesc *cdesc;
1423         /* no support for counters on other machines */
1424         if (mono_string_compare_ascii (machine, "."))
1425                 return NULL;
1426         cdesc = find_category (category);
1427         if (!cdesc) {
1428                 SharedCategory *scat = find_custom_category (category);
1429                 if (!scat)
1430                         return NULL;
1431                 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1432         }
1433         return mono_string_new (mono_domain_get (), cdesc->help);
1434 }
1435
1436 /*
1437  * Check if the category named @category exists on @machine. If @counter is not NULL, return
1438  * TRUE only if a counter with that name exists in the category.
1439  */
1440 MonoBoolean
1441 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1442 {
1443         const CategoryDesc *cdesc;
1444         /* no support for counters on other machines */
1445         if (mono_string_compare_ascii (machine, "."))
1446                 return FALSE;
1447         cdesc = find_category (category);
1448         if (!cdesc) {
1449                 SharedCategory *scat = find_custom_category (category);
1450                 if (!scat)
1451                         return FALSE;
1452                 /* counter is allowed to be null */
1453                 if (!counter)
1454                         return TRUE;
1455                 /* search through the custom category */
1456                 return find_custom_counter (scat, counter) != NULL;
1457         }
1458         /* counter is allowed to be null */
1459         if (!counter)
1460                 return TRUE;
1461         if (get_counter_in_category (cdesc, counter))
1462                 return TRUE;
1463         return FALSE;
1464 }
1465
1466 /* C map of the type with the same name */
1467 typedef struct {
1468         MonoObject object;
1469         MonoString *help;
1470         MonoString *name;
1471         int type;
1472 } CounterCreationData;
1473
1474 /*
1475  * Since we'll keep a copy of the category per-process, we should also make sure
1476  * categories with the same name are compatible.
1477  */
1478 MonoBoolean
1479 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1480 {
1481         MonoError error;
1482         int result = FALSE;
1483         int i, size;
1484         int num_counters = mono_array_length (items);
1485         int counters_data_size;
1486         char *name = NULL;
1487         char *chelp = NULL;
1488         char **counter_info = NULL;
1489         char *p;
1490         SharedCategory *cat;
1491
1492         /* FIXME: ensure there isn't a category created already */
1493         mono_error_init (&error);
1494         name = mono_string_to_utf8_checked (category, &error);
1495         if (!mono_error_ok (&error))
1496                 goto failure;
1497         chelp = mono_string_to_utf8_checked (help, &error);
1498         if (!mono_error_ok (&error))
1499                 goto failure;
1500         counter_info = g_new0 (char*, num_counters * 2);
1501         /* calculate the size we need structure size + name/help + 2 0 string terminators */
1502         size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1503         for (i = 0; i < num_counters; ++i) {
1504                 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1505                 counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error);
1506                 if (!mono_error_ok (&error))
1507                         goto failure;
1508                 counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error);
1509                 if (!mono_error_ok (&error))
1510                         goto failure;
1511                 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1512         }
1513         for (i = 0; i < num_counters * 2; ++i) {
1514                 if (!counter_info [i])
1515                         goto failure;
1516                 size += strlen (counter_info [i]) + 1;
1517         }
1518         size += 7;
1519         size &= ~7;
1520         counters_data_size = num_counters * 8; /* optimize for size later */
1521         if (size > 65535)
1522                 goto failure;
1523         perfctr_lock ();
1524         cat = (SharedCategory*) shared_data_reserve_room (size, FTYPE_CATEGORY);
1525         if (!cat) {
1526                 perfctr_unlock ();
1527                 goto failure;
1528         }
1529         cat->num_counters = num_counters;
1530         cat->counters_data_size = counters_data_size;
1531         /* now copy the vaiable data */
1532         p = cat->name;
1533         strcpy (p, name);
1534         p += strlen (name) + 1;
1535         strcpy (p, chelp);
1536         p += strlen (chelp) + 1;
1537         for (i = 0; i < num_counters; ++i) {
1538                 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1539                 /* emit the SharedCounter structures */
1540                 *p++ = perfctr_type_compress (data->type);
1541                 *p++ = i;
1542                 strcpy (p, counter_info [i * 2]);
1543                 p += strlen (counter_info [i * 2]) + 1;
1544                 strcpy (p, counter_info [i * 2 + 1]);
1545                 p += strlen (counter_info [i * 2 + 1]) + 1;
1546         }
1547
1548         perfctr_unlock ();
1549         result = TRUE;
1550 failure:
1551         if (counter_info) {
1552                 for (i = 0; i < num_counters * 2; ++i) {
1553                         g_free (counter_info [i]);
1554                 }
1555                 g_free (counter_info);
1556         }
1557         g_free (name);
1558         g_free (chelp);
1559         mono_error_cleanup (&error);
1560         return result;
1561 }
1562
1563 int
1564 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1565 {
1566         const CategoryDesc *cdesc;
1567         SharedInstance *sinst;
1568         char *name;
1569         /* no support for counters on other machines */
1570         /*FIXME: machine appears to be wrong
1571         if (mono_string_compare_ascii (machine, "."))
1572                 return FALSE;*/
1573         cdesc = find_category (category);
1574         if (!cdesc) {
1575                 SharedCategory *scat;
1576                 scat = find_custom_category (category);
1577                 if (!scat)
1578                         return FALSE;
1579                 name = mono_string_to_utf8 (instance);
1580                 sinst = find_custom_instance (scat, name);
1581                 g_free (name);
1582                 if (sinst)
1583                         return TRUE;
1584         } else {
1585                 /* FIXME: search instance */
1586         }
1587         return FALSE;
1588 }
1589
1590 MonoArray*
1591 mono_perfcounter_category_names (MonoString *machine)
1592 {
1593         int i;
1594         MonoArray *res;
1595         MonoDomain *domain = mono_domain_get ();
1596         GSList *custom_categories, *tmp;
1597         /* no support for counters on other machines */
1598         if (mono_string_compare_ascii (machine, "."))
1599                 return mono_array_new (domain, mono_get_string_class (), 0);
1600         perfctr_lock ();
1601         custom_categories = get_custom_categories ();
1602         res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1603         for (i = 0; i < NUM_CATEGORIES; ++i) {
1604                 const CategoryDesc *cdesc = &predef_categories [i];
1605                 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1606         }
1607         for (tmp = custom_categories; tmp; tmp = tmp->next) {
1608                 SharedCategory *scat = (SharedCategory *)tmp->data;
1609                 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1610                 i++;
1611         }
1612         perfctr_unlock ();
1613         g_slist_free (custom_categories);
1614         return res;
1615 }
1616
1617 MonoArray*
1618 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1619 {
1620         int i;
1621         SharedCategory *scat;
1622         const CategoryDesc *cdesc;
1623         MonoArray *res;
1624         MonoDomain *domain = mono_domain_get ();
1625         /* no support for counters on other machines */
1626         if (mono_string_compare_ascii (machine, "."))
1627                 return mono_array_new (domain, mono_get_string_class (), 0);
1628         cdesc = find_category (category);
1629         if (cdesc) {
1630                 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1631                 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1632                         const CounterDesc *desc = &predef_counters [i];
1633                         mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1634                 }
1635                 return res;
1636         }
1637         perfctr_lock ();
1638         scat = find_custom_category (category);
1639         if (scat) {
1640                 char *p = custom_category_counters (scat);
1641                 int i;
1642                 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1643                 for (i = 0; i < scat->num_counters; ++i) {
1644                         mono_array_setref (res, i, mono_string_new (domain, p + 1));
1645                         p += 2; /* skip counter type */
1646                         p += strlen (p) + 1; /* skip counter name */
1647                         p += strlen (p) + 1; /* skip counter help */
1648                 }
1649                 perfctr_unlock ();
1650                 return res;
1651         }
1652         perfctr_unlock ();
1653         return mono_array_new (domain, mono_get_string_class (), 0);
1654 }
1655
1656 static MonoArray*
1657 get_string_array (void **array, int count, gboolean is_process)
1658 {
1659         int i;
1660         MonoDomain *domain = mono_domain_get ();
1661         MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1662         for (i = 0; i < count; ++i) {
1663                 char buf [128];
1664                 char *p;
1665                 if (is_process) {
1666                         char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1667                         p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1668                 } else {
1669                         sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1670                         p = buf;
1671                 }
1672                 mono_array_setref (res, i, mono_string_new (domain, p));
1673                 if (p != buf)
1674                         g_free (p);
1675         }
1676         return res;
1677 }
1678
1679 static MonoArray*
1680 get_string_array_of_strings (void **array, int count)
1681 {
1682         int i;
1683         MonoDomain *domain = mono_domain_get ();
1684         MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1685         for (i = 0; i < count; ++i) {
1686                 char* p = (char *)array[i];
1687                 mono_array_setref (res, i, mono_string_new (domain, p));
1688         }
1689
1690         return res;
1691 }
1692
1693 static MonoArray*
1694 get_mono_instances (void)
1695 {
1696         int count = 64;
1697         int res;
1698         void **buf = NULL;
1699         MonoArray *array;
1700         do {
1701                 count *= 2;
1702                 g_free (buf);
1703                 buf = g_new (void*, count);
1704                 res = mono_shared_area_instances (buf, count);
1705         } while (res == count);
1706         array = get_string_array (buf, res, TRUE);
1707         g_free (buf);
1708         return array;
1709 }
1710
1711 static MonoArray*
1712 get_cpu_instances (void)
1713 {
1714         void **buf = NULL;
1715         int i, count;
1716         MonoArray *array;
1717
1718         count = mono_cpu_count () + 1; /* +1 for "_Total" */
1719         buf = g_new (void*, count);
1720         for (i = 0; i < count; ++i)
1721                 buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
1722         array = get_string_array (buf, count, FALSE);
1723         g_free (buf);
1724         mono_array_setref (array, 0, mono_string_new (mono_domain_get (), "_Total"));
1725         return array;
1726 }
1727
1728 static MonoArray*
1729 get_processes_instances (void)
1730 {
1731         MonoArray *array;
1732         int count = 0;
1733         void **buf = mono_process_list (&count);
1734         if (!buf)
1735                 return get_string_array (NULL, 0, FALSE);
1736         array = get_string_array (buf, count, TRUE);
1737         g_free (buf);
1738         return array;
1739 }
1740
1741 static MonoArray*
1742 get_networkinterface_instances (void)
1743 {
1744         MonoArray *array;
1745         int count = 0;
1746         void **buf = mono_networkinterface_list (&count);
1747         if (!buf)
1748                 return get_string_array_of_strings (NULL, 0);
1749         array = get_string_array_of_strings (buf, count);
1750         g_strfreev ((char **) buf);
1751         return array;
1752 }
1753
1754 static MonoArray*
1755 get_custom_instances (MonoString *category)
1756 {
1757         SharedCategory *scat;
1758         scat = find_custom_category (category);
1759         if (scat) {
1760                 GSList *list = get_custom_instances_list (scat);
1761                 GSList *tmp;
1762                 int i = 0;
1763                 MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
1764                 for (tmp = list; tmp; tmp = tmp->next) {
1765                         SharedInstance *inst = (SharedInstance *)tmp->data;
1766                         mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1767                         i++;
1768                 }
1769                 g_slist_free (list);
1770                 return array;
1771         }
1772         return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1773 }
1774
1775 MonoArray*
1776 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1777 {
1778         const CategoryDesc* cat;
1779         if (mono_string_compare_ascii (machine, "."))
1780                 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1781         cat = find_category (category);
1782         if (!cat)
1783                 return get_custom_instances (category);
1784         switch (cat->instance_type) {
1785         case MonoInstance:
1786                 return get_mono_instances ();
1787         case CPUInstance:
1788                 return get_cpu_instances ();
1789         case ProcessInstance:
1790                 return get_processes_instances ();
1791         case NetworkInterfaceInstance:
1792                 return get_networkinterface_instances ();
1793         case ThreadInstance:
1794         default:
1795                 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1796         }
1797 }
1798
1799 typedef struct {
1800         PerfCounterEnumCallback cb;
1801         void *data;
1802 } PerfCounterForeachData;
1803
1804 static gboolean
1805 mono_perfcounter_foreach_shared_item (SharedHeader *header, gpointer data)
1806 {
1807         int i;
1808         char *p, *name;
1809         unsigned char type;
1810         void *addr;
1811         SharedCategory *cat;
1812         SharedCounter *counter;
1813         SharedInstance *inst;
1814         PerfCounterForeachData *foreach_data = (PerfCounterForeachData *)data;
1815
1816         if (header->ftype == FTYPE_CATEGORY) {
1817                 cat = (SharedCategory*)header;
1818
1819                 p = cat->name;
1820                 p += strlen (p) + 1; /* skip category name */
1821                 p += strlen (p) + 1; /* skip category help */
1822
1823                 for (i = 0; i < cat->num_counters; ++i) {
1824                         counter = (SharedCounter*) p;
1825                         type = (unsigned char)*p++;
1826                         /* seq_num = (int)* */ p++;
1827                         name = p;
1828                         p += strlen (p) + 1;
1829                         /* help = p; */
1830                         p += strlen (p) + 1;
1831
1832                         inst = custom_get_instance (cat, counter, name);
1833                         if (!inst)
1834                                 return FALSE;
1835                         addr = custom_get_value_address (counter, inst);
1836                         if (!foreach_data->cb (cat->name, name, type, addr ? *(gint64*)addr : 0, foreach_data->data))
1837                                 return FALSE;
1838                 }
1839         }
1840
1841         return TRUE;
1842 }
1843
1844 void
1845 mono_perfcounter_foreach (PerfCounterEnumCallback cb, gpointer data)
1846 {
1847         PerfCounterForeachData foreach_data = { cb, data };
1848
1849         perfctr_lock ();
1850
1851         foreach_shared_item (mono_perfcounter_foreach_shared_item, &foreach_data);
1852
1853         perfctr_unlock ();
1854 }
1855
1856 #else
1857 void*
1858 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance, MonoString* machine, int *type, MonoBoolean *custom)
1859 {
1860         g_assert_not_reached ();
1861 }
1862
1863 MonoBoolean
1864 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1865 {
1866         g_assert_not_reached ();
1867 }
1868
1869 gint64
1870 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1871 {
1872         g_assert_not_reached ();
1873 }
1874
1875 void
1876 mono_perfcounter_free_data (void *impl)
1877 {
1878         g_assert_not_reached ();
1879 }
1880
1881 /* Category icalls */
1882 MonoBoolean
1883 mono_perfcounter_category_del (MonoString *name)
1884 {
1885         g_assert_not_reached ();
1886 }
1887
1888 MonoString*
1889 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1890 {
1891         g_assert_not_reached ();
1892 }
1893
1894 MonoBoolean
1895 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1896 {
1897         g_assert_not_reached ();
1898 }
1899
1900 MonoBoolean
1901 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1902 {
1903         g_assert_not_reached ();
1904 }
1905
1906 int
1907 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1908 {
1909         g_assert_not_reached ();
1910 }
1911
1912 MonoArray*
1913 mono_perfcounter_category_names (MonoString *machine)
1914 {
1915         g_assert_not_reached ();
1916 }
1917
1918 MonoArray*
1919 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1920 {
1921         g_assert_not_reached ();
1922 }
1923
1924 MonoArray*
1925 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1926 {
1927         g_assert_not_reached ();
1928 }
1929 #endif