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