[runtime] Replace pedump boehm dependency with sgen dependency
[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         MonoError error;
1594         int i;
1595         MonoArray *res;
1596         MonoDomain *domain = mono_domain_get ();
1597         GSList *custom_categories, *tmp;
1598         /* no support for counters on other machines */
1599         if (mono_string_compare_ascii (machine, ".")) {
1600                 res = mono_array_new_checked (domain, mono_get_string_class (), 0, &error);
1601                 mono_error_set_pending_exception (&error);
1602                 return res;
1603         }
1604         perfctr_lock ();
1605         custom_categories = get_custom_categories ();
1606         res = mono_array_new_checked (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories), &error);
1607         if (mono_error_set_pending_exception (&error)) {
1608                 perfctr_unlock ();
1609                 return NULL;
1610         }
1611
1612         for (i = 0; i < NUM_CATEGORIES; ++i) {
1613                 const CategoryDesc *cdesc = &predef_categories [i];
1614                 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1615         }
1616         for (tmp = custom_categories; tmp; tmp = tmp->next) {
1617                 SharedCategory *scat = (SharedCategory *)tmp->data;
1618                 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1619                 i++;
1620         }
1621         perfctr_unlock ();
1622         g_slist_free (custom_categories);
1623         return res;
1624 }
1625
1626 MonoArray*
1627 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1628 {
1629         MonoError error;
1630         int i;
1631         SharedCategory *scat;
1632         const CategoryDesc *cdesc;
1633         MonoArray *res;
1634         MonoDomain *domain = mono_domain_get ();
1635         /* no support for counters on other machines */
1636         if (mono_string_compare_ascii (machine, ".")) {
1637                 res =  mono_array_new_checked (domain, mono_get_string_class (), 0, &error);
1638                 mono_error_set_pending_exception (&error);
1639                 return res;
1640         }
1641         cdesc = find_category (category);
1642         if (cdesc) {
1643                 res = mono_array_new_checked (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter, &error);
1644                 if (mono_error_set_pending_exception (&error))
1645                         return NULL;
1646                 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1647                         const CounterDesc *desc = &predef_counters [i];
1648                         mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1649                 }
1650                 return res;
1651         }
1652         perfctr_lock ();
1653         scat = find_custom_category (category);
1654         if (scat) {
1655                 char *p = custom_category_counters (scat);
1656                 int i;
1657                 res = mono_array_new_checked (domain, mono_get_string_class (), scat->num_counters, &error);
1658                 if (mono_error_set_pending_exception (&error)) {
1659                         perfctr_unlock ();
1660                         return NULL;
1661                 }
1662
1663                 for (i = 0; i < scat->num_counters; ++i) {
1664                         mono_array_setref (res, i, mono_string_new (domain, p + 1));
1665                         p += 2; /* skip counter type */
1666                         p += strlen (p) + 1; /* skip counter name */
1667                         p += strlen (p) + 1; /* skip counter help */
1668                 }
1669                 perfctr_unlock ();
1670                 return res;
1671         }
1672         perfctr_unlock ();
1673         res = mono_array_new_checked (domain, mono_get_string_class (), 0, &error);
1674         mono_error_set_pending_exception (&error);
1675         return res;
1676 }
1677
1678 static MonoArray*
1679 get_string_array (void **array, int count, gboolean is_process, MonoError *error)
1680 {
1681         int i;
1682         MonoDomain *domain = mono_domain_get ();
1683         mono_error_init (error);
1684         MonoArray * res = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), count, error);
1685         return_val_if_nok (error, NULL);
1686         for (i = 0; i < count; ++i) {
1687                 char buf [128];
1688                 char *p;
1689                 if (is_process) {
1690                         char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1691                         p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1692                 } else {
1693                         sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1694                         p = buf;
1695                 }
1696                 mono_array_setref (res, i, mono_string_new (domain, p));
1697                 if (p != buf)
1698                         g_free (p);
1699         }
1700         return res;
1701 }
1702
1703 static MonoArray*
1704 get_string_array_of_strings (void **array, int count, MonoError *error)
1705 {
1706         int i;
1707         MonoDomain *domain = mono_domain_get ();
1708         mono_error_init (error);
1709         MonoArray * res = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), count, error);
1710         return_val_if_nok (error, NULL);
1711         for (i = 0; i < count; ++i) {
1712                 char* p = (char *)array[i];
1713                 mono_array_setref (res, i, mono_string_new (domain, p));
1714         }
1715
1716         return res;
1717 }
1718
1719 static MonoArray*
1720 get_mono_instances (MonoError *error)
1721 {
1722         int count = 64;
1723         int res;
1724         void **buf = NULL;
1725         MonoArray *array;
1726         mono_error_init (error);
1727         do {
1728                 count *= 2;
1729                 g_free (buf);
1730                 buf = g_new (void*, count);
1731                 res = mono_shared_area_instances (buf, count);
1732         } while (res == count);
1733         array = get_string_array (buf, res, TRUE, error);
1734         g_free (buf);
1735         return array;
1736 }
1737
1738 static MonoArray*
1739 get_cpu_instances (MonoError *error)
1740 {
1741         void **buf = NULL;
1742         int i, count;
1743         MonoArray *array;
1744         mono_error_init (error);
1745         count = mono_cpu_count () + 1; /* +1 for "_Total" */
1746         buf = g_new (void*, count);
1747         for (i = 0; i < count; ++i)
1748                 buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
1749         array = get_string_array (buf, count, FALSE, error);
1750         g_free (buf);
1751         mono_array_setref (array, 0, mono_string_new (mono_domain_get (), "_Total"));
1752         return array;
1753 }
1754
1755 static MonoArray*
1756 get_processes_instances (MonoError *error)
1757 {
1758         MonoArray *array;
1759         int count = 0;
1760         void **buf = mono_process_list (&count);
1761         mono_error_init (error);
1762         if (!buf)
1763                 return get_string_array (NULL, 0, FALSE, error);
1764         array = get_string_array (buf, count, TRUE, error);
1765         g_free (buf);
1766         return array;
1767 }
1768
1769 static MonoArray*
1770 get_networkinterface_instances (MonoError *error)
1771 {
1772         MonoArray *array;
1773         int count = 0;
1774         mono_error_init (error);
1775         void **buf = mono_networkinterface_list (&count);
1776         if (!buf)
1777                 return get_string_array_of_strings (NULL, 0, error);
1778         array = get_string_array_of_strings (buf, count, error);
1779         g_strfreev ((char **) buf);
1780         return array;
1781 }
1782
1783 static MonoArray*
1784 get_custom_instances (MonoString *category, MonoError *error)
1785 {
1786         SharedCategory *scat;
1787         mono_error_init (error);
1788         scat = find_custom_category (category);
1789         if (scat) {
1790                 GSList *list = get_custom_instances_list (scat);
1791                 GSList *tmp;
1792                 int i = 0;
1793                 MonoArray *array = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), g_slist_length (list), error);
1794                 if (!is_ok (error)) {
1795                         g_slist_free (list);
1796                         return NULL;
1797                 }
1798                 for (tmp = list; tmp; tmp = tmp->next) {
1799                         SharedInstance *inst = (SharedInstance *)tmp->data;
1800                         mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1801                         i++;
1802                 }
1803                 g_slist_free (list);
1804                 return array;
1805         }
1806         return mono_array_new_checked (mono_domain_get (), mono_get_string_class (), 0, error);
1807 }
1808
1809 MonoArray*
1810 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1811 {
1812         MonoError error;
1813         const CategoryDesc* cat;
1814         MonoArray *result = NULL;
1815         if (mono_string_compare_ascii (machine, ".")) {
1816                 result = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), 0, &error);
1817                 mono_error_set_pending_exception (&error);
1818                 return result;
1819         }
1820         
1821         cat = find_category (category);
1822         if (!cat) {
1823                 MonoArray *result = get_custom_instances (category, &error);
1824                 mono_error_set_pending_exception (&error);
1825                 return result;
1826         }
1827         switch (cat->instance_type) {
1828         case MonoInstance:
1829                 result = get_mono_instances (&error);
1830                 break;
1831         case CPUInstance:
1832                 result = get_cpu_instances (&error);
1833                 break;
1834         case ProcessInstance:
1835                 result = get_processes_instances (&error);
1836                 break;
1837         case NetworkInterfaceInstance:
1838                 result = get_networkinterface_instances (&error);
1839                 break;
1840         case ThreadInstance:
1841         default:
1842                 result = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), 0, &error);
1843         }
1844         mono_error_set_pending_exception (&error);
1845         return result;
1846 }
1847
1848 typedef struct {
1849         PerfCounterEnumCallback cb;
1850         void *data;
1851 } PerfCounterForeachData;
1852
1853 static gboolean
1854 mono_perfcounter_foreach_shared_item (SharedHeader *header, gpointer data)
1855 {
1856         int i;
1857         char *p, *name;
1858         unsigned char type;
1859         void *addr;
1860         SharedCategory *cat;
1861         SharedCounter *counter;
1862         SharedInstance *inst;
1863         PerfCounterForeachData *foreach_data = (PerfCounterForeachData *)data;
1864
1865         if (header->ftype == FTYPE_CATEGORY) {
1866                 cat = (SharedCategory*)header;
1867
1868                 p = cat->name;
1869                 p += strlen (p) + 1; /* skip category name */
1870                 p += strlen (p) + 1; /* skip category help */
1871
1872                 for (i = 0; i < cat->num_counters; ++i) {
1873                         counter = (SharedCounter*) p;
1874                         type = (unsigned char)*p++;
1875                         /* seq_num = (int)* */ p++;
1876                         name = p;
1877                         p += strlen (p) + 1;
1878                         /* help = p; */
1879                         p += strlen (p) + 1;
1880
1881                         inst = custom_get_instance (cat, counter, name);
1882                         if (!inst)
1883                                 return FALSE;
1884                         addr = custom_get_value_address (counter, inst);
1885                         if (!foreach_data->cb (cat->name, name, type, addr ? *(gint64*)addr : 0, foreach_data->data))
1886                                 return FALSE;
1887                 }
1888         }
1889
1890         return TRUE;
1891 }
1892
1893 void
1894 mono_perfcounter_foreach (PerfCounterEnumCallback cb, gpointer data)
1895 {
1896         PerfCounterForeachData foreach_data = { cb, data };
1897
1898         perfctr_lock ();
1899
1900         foreach_shared_item (mono_perfcounter_foreach_shared_item, &foreach_data);
1901
1902         perfctr_unlock ();
1903 }
1904
1905 #else
1906 void*
1907 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance, MonoString* machine, int *type, MonoBoolean *custom)
1908 {
1909         g_assert_not_reached ();
1910 }
1911
1912 MonoBoolean
1913 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1914 {
1915         g_assert_not_reached ();
1916 }
1917
1918 gint64
1919 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1920 {
1921         g_assert_not_reached ();
1922 }
1923
1924 void
1925 mono_perfcounter_free_data (void *impl)
1926 {
1927         g_assert_not_reached ();
1928 }
1929
1930 /* Category icalls */
1931 MonoBoolean
1932 mono_perfcounter_category_del (MonoString *name)
1933 {
1934         g_assert_not_reached ();
1935 }
1936
1937 MonoString*
1938 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1939 {
1940         g_assert_not_reached ();
1941 }
1942
1943 MonoBoolean
1944 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1945 {
1946         g_assert_not_reached ();
1947 }
1948
1949 MonoBoolean
1950 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1951 {
1952         g_assert_not_reached ();
1953 }
1954
1955 int
1956 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1957 {
1958         g_assert_not_reached ();
1959 }
1960
1961 MonoArray*
1962 mono_perfcounter_category_names (MonoString *machine)
1963 {
1964         g_assert_not_reached ();
1965 }
1966
1967 MonoArray*
1968 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1969 {
1970         g_assert_not_reached ();
1971 }
1972
1973 MonoArray*
1974 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1975 {
1976         g_assert_not_reached ();
1977 }
1978 #endif